通过之前两篇文章
- 了解了ES的核心概念和基础使用
- 学习进阶的DSL语法处理复杂的查询
这段时间通过在本企业代码中对ES框架的使用,总结了不少经验。主要分为三点
- 企业封装了ES原生的api,需要使用企业项目提供的接口实现 -------简单使用(本章节目的)
- 项目会遇到更复杂的查询需求,需要进一步深入对ES的学习 -------复杂使用
- 了解项目如何封装原生的api,学习设计思想 --------深入学习
目录
- 1. Term查询
- 1.1 原生api实现term查询
- 1.2 企业api实现term查询
- 2. 复合查询__must
- 2.1 原生api实现must查询
- 2.2 企业api实现must查询
- 3. 复合查询__should
- 4. 复合查询__mustnot
- 5. 分页和排序
- 5.1 原生api实现分页和排序
- 5.2 企业api实现分页和排序
- 6 聚合查询
- 6.1 原生api实现桶聚合
- 6.2 企业api实现桶聚合
------------------------------本章节核心目的是梳理出 本企业项目提供的api 和 原生ES提供的api 的使用区别--------------------------------
本企业将ES的api大致封装成了两个核心类
EsOperater类
方法 | 说明 |
---|---|
String[] indexes() | |
Integer from() | 分页 |
Integer size() | 分页 |
List sort() | 排序 |
QueryBuilder queryBuilder() | 普通查询/复合查询 |
EsOperaterBuiler esOperaterBuiler() | 继承类 |
SearchResponse execute() | 执行查询 |
CountResponse queryTotal() | |
SearchResponse executeScroll() | |
QueryBuilder buildQueryBuilder() | |
QueryBuilder buildQueryBuilderByQueryType(EsQueryInfoBean queryInfo) | 根据查询信息bean构造相应的查询器 |
List buildAggBuilder() | 根据aggMap创建聚合器,包括单层聚合和多层聚合 |
AggregationBuilder makeChildAgg(EsAggInfoBean esAggInfo, EsAggInfoBean parentAggInfo) | 递归创建聚合器 |
EsOperater build() |
EsOperaterBuiler类(重点关注)
方法 | 说明 |
---|---|
EsOperaterBuiler indexes(String… indexes) | 设置索引集合 |
EsOperaterBuiler from(Integer from) | 设置分页参数的查询数量 |
EsOperaterBuiler size(Integer size) | 设置分页参数的查询数量 |
EsOperaterBuiler sort(String sort) | 设置排序字段 |
EsOperaterBuiler sortOrder(SortOrder sortOrder) | 设置排序排序方式(升序、降序) |
EsOperaterBuiler queryBuilder(QueryBuilder queryBuilder) | 设置查询构建器(QueryBuilder),如果操作构建器(EsOperater)中buildQueryBuilder()方法构造不出需要的查询构建起, |
Boolean isAliasExists(String indexName) | 查询别名是否存在 |
1. Term查询
1.1 原生api实现term查询
@Test
void TermQuery(){
// 获取client
这里默认已经获取
// 1. 准备request (参数为索引名称)
SearchRequest request = new SearchRequest("indexName");
// 2. 构建DSL
// 2.1 获取建造者
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 2.2 建造者调用DSL
searchSourceBuilder.termQuery("name","zjh");
// 2.3 组装
request.source(searchSourceBuilder);
// 3. 发送请求
SearchResponse reponse = client.search(request, RequestOptions.ESFAULT);
// 4. 解析数据,得到_source数据
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
此时就可以获取到source的数据了。上述写法也可以简化,如下
// 此方式常用
@Test
void TermQuery(){
// 获取client
这里默认已经获取
// 1. 准备request (参数为索引名称)
SearchRequest request = new SearchRequest("indexName");
// 2. 构建DSL语句
request.source().query(QueryBuilders.termQuery("name","zjh"));
// 3. 发送请求
SearchResponse reponse = client.search(request, RequestOptions.ESFAULT);
// 4. 解析数据,得到_source数据
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
1.2 企业api实现term查询
@Test
void TermQuery(){
// 构建索引名称
String indexName = ElasticSearchConst.UNSTRUCTURE_FILE_SCAN_RESULT + taskId;
// 1. 设置索引集合
EsOperater.EsOperaterBuiler builder = EsOperater.esOperaterBuiler().indexes(indexName);
// 2. 设置查询构建器 + 准备DSL语句
builder.queryBuilder(QueryBuilders.termQuery("name","zjh"));
// 3. 发送请求
SearchResponse response = builder.build().execute();
// 4. 解析数据,得到_source数据
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
解释:
步骤一:需要将 索引名 存到 esOperaterBuiler类 的全局变量中,以便其他方法调用
步骤二:需要将 DSL语句 存到 esOperaterBuiler类 的全局变量中,以便其他方法调用
步骤三:需要从esOperaterBuiler类 切换到 esOperater类,再执行最核心的 execute() 方法,这个方法会进行一些列操作,将最终的结果返回给 response
2. 复合查询__must
2.1 原生api实现must查询
@Test
void MustQuery(){
// 获取client
这里默认已经获取
// 1. 准备request (参数为索引名称)
SearchRequest request = new SearchRequest("indexName");
// 2. 构建DSL语句
// 2.1 创建bool查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 2.2 添加must条件
boolQuery.must(QueryBuilders.termQuery("name", "zjh"));
// 2.3 构建请求内容
request.source().query(boolQuery);
// 3. 发送请求
SearchResponse reponse = client.search(request, RequestOptions.ESFAULT);
// 4. 解析数据,得到_source数据
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
2.2 企业api实现must查询
@Test
void TermQuery(){
// 构建索引名称
String indexName = ElasticSearchConst.UNSTRUCTURE_FILE_SCAN_RESULT + taskId;
// 1. 设置索引集合
EsOperater.EsOperaterBuiler builder = EsOperater.esOperaterBuiler().indexes(indexName);
// 2. 设置查询构建器 + 准备DSL语句
// 2.1 创建bool查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 2.2 添加must条件
boolQuery.must(QueryBuilders.termQuery("name", "zjh"));
// 此行代码的作用就是将构造的must条件,存放到EsOperater类的全局变量
builder.queryBuilder(boolQuery);
// 3. 发送请求
SearchResponse response = builder.build().execute();
// 4. 解析数据,得到_source数据
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
解释一下步骤二:
可能会疑惑为什么不这样写
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
BoolQueryBuilder mustQuery = boolQuery.must(QueryBuilders.termQuery("name", "zjh"));
builder.queryBuilder(mustQuery);
因为must(参数)底层会将参数传给boolQuery.must()的boolQuery对象,是递增的逻辑
解释:
步骤一:需要将 索引名 存到 esOperaterBuiler类 的全局变量中,以便其他方法调用
步骤二:需要将 DSL语句(布尔查询) 存到 esOperaterBuiler类 的全局变量中,以便其他方法调用
步骤三:需要从esOperaterBuiler类 切换到 esOperater类,再执行最核心的 execute() 方法,这个方法会进行一些列操作,将最终的结果返回给 response
可以进一步简化
@Test
void TermQuery(){
// 构建索引名称
String indexName = ElasticSearchConst.UNSTRUCTURE_FILE_SCAN_RESULT + taskId;
// DSL语句
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
boolQuery.must(QueryBuilders.termQuery("name", "zjh"));
// 使用企业api实现查询
EsOperater.EsOperaterBuiler builder = EsOperater.esOperaterBuiler();
SearchResponse response = builder.index(indexName).queryBuilder(boolQuery).build().execute();
// 4. 解析数据,得到_source数据
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
3. 复合查询__should
同理
4. 复合查询__mustnot
同理
5. 分页和排序
5.1 原生api实现分页和排序
// 此方式常用
@Test
void TermQuery(){
// 获取client
这里默认已经获取
// 1. 准备request (参数为索引名称)
SearchRequest request = new SearchRequest("indexName");
//2.查询__构建DSL语句
request.source().query(QueryBuilders.termQuery("name","zjh"));
// 分页
request.source().from.size(5);
// 时间排序
request.source().sort(“logTime”,SortOrder.ASC);
// 3. 发送请求
SearchResponse reponse = client.search(request, RequestOptions.ESFAULT);
// 4. 解析数据,得到_source数据
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
5.2 企业api实现分页和排序
@Test
void TermQuery(){
// 构建索引名称
String indexName = ElasticSearchConst.UNSTRUCTURE_FILE_SCAN_RESULT + taskId;
// 1. 设置索引集合
EsOperater.EsOperaterBuiler builder = EsOperater.esOperaterBuiler().indexes(indexName);
// 2. 查询
builder.queryBuilder(QueryBuilders.termQuery("name","zjh"));
// 分页
builder.queryBuilder(QueryBuilders.termQuery("name","zjh")).size(5);
// 排序
builder.queryBuilder(QueryBuilders.termQuery("name","zjh")).sort("logTime").sortOrder(SortOrder.DESC);
// 3. 发送请求
SearchResponse response = builder.build().execute();
// 4. 解析数据,得到_source数据
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
6 聚合查询
6.1 原生api实现桶聚合
// 需求:实现对城市、品牌的聚合。即用户输入城市、品牌,得到搜索结果
@Test
void TermQuery(){
// 获取client
这里默认已经获取
// 1. 准备request (参数为索引名称)
SearchRequest request = new SearchRequest("indexName");
//2.查询
// CityName:自定义桶名; city:根据城市聚合
AggregationBuilder aggregationBuilder1 = AggregationBuilders.terms("CityName").field("city");
AggregationBuilder aggregationBuilder2 = AggregationBuilders.terms("BrandName").field("brand");
request.source().aggregation(aggregationBuilder1);
request.source().aggregation(aggregationBuilder2);
// 3. 发送请求
SearchResponse reponse = client.search(request, RequestOptions.ESFAULT);
// 4. 解析数据
Aggreagtions aggreagtions = response.getAggreagtions();
List<? extends Terms.Bucket> buckets1 = aggreagtions.get("CityName").getBuckets();
for (Terms.Bucket bucket : buckets) {
//打印结果是:西安 或者 上海
System.out.println(bucket.getKeyAsString());
}
List<? extends Terms.Bucket> buckets2 = aggreagtions.get("BrandName").getBuckets();
for (Terms.Bucket bucket : buckets) {
//打印结果是:星巴克 或者 瑞幸
System.out.println(bucket.getKeyAsString());
}
}
6.2 企业api实现桶聚合
// 需求:实现对城市、品牌的聚合。即用户输入城市、品牌,得到搜索结果
@Test
void TermQuery(){
// 获取client
这里默认已经获取
// 1. 准备request (参数为索引名称)
SearchRequest request = new SearchRequest("indexName");
//2.查询
List<AggregationBuilder> aggregationBuilderList = new ArrayList<>();
aggregationBuilderList.add(AggregationBuilders.terms("CityName").field("city"));;
aggregationBuilderList.add(AggregationBuilders.terms("BrandName").field("brand"));
// aggBuilderList()企业封装的工具,将聚合参数赋值到全局变量上
builder.aggBuilderList(aggregationBuilderList);
// 3. 发送请求
SearchResponse response = builder.size(1).build().execute();
// 4. 解析数据
Aggreagtions aggreagtions = response.getAggreagtions();
// 注意ParsedStringTerms,还有ParsedLongTerms、ParsedDoubleTerms...
ParsedStringTerms CityName = aggreagtions.get("CityName");
for (Terms.Bucket bucket : CityName.getBuckets()) {
//打印结果是:西安 或者 上海
System.out.println(bucket.getKeyAsString());
}
ParsedStringTerms BrandName = aggreagtions.get("BrandName");
for (Terms.Bucket bucket : BrandName.getBuckets()) {
//打印结果是:星巴克 或者 瑞幸
System.out.println(bucket.getKeyAsString());
}
}
这里需要解释一下步骤四中的 ParsedStringTerms
ES会将聚合结果封装到特定的类中,方便你来处理不同类型的聚合结果。
ParsedLongTerms
:
- 这个类用于处理长整型(long)类型的聚合结果。
ParsedStringTerms
:
- 这个类用于处理字符串(String)类型的聚合结果。
什么意思呢?在ES中对"CityName"进行聚合。
返回结果中可以看到如下信息,表示星巴克有三家(西安)
key:“星巴克” (字符串类型)
doc_count : 3 (long类型)
因此根据key的类型,正确选择使用ParsedStringTerms || ParsedLongTerms ||…接收聚合结果,否则报错。
示例图: