布尔查询是最常用的组合查询,不仅将多个查询条件组合在一起,并且将查询的结果和结果的评分组合在一起。当查询条件是多个表达式的组合时,布尔查询非常有用,实际上,布尔查询把多个子查询组合(combine)成一个布尔表达式,所有子查询之间的逻辑关系是与(and);只有当一个文档满足布尔查询中的所有子查询条件时,ElasticSearch引擎才认为该文档满足查询条件。布尔查询支持的子查询类型共有四种,分别是:must ,should,must_not和filter:
Query与Filter
查询在Query查询上下文和Filter过滤器上下文中,执行的操作是不一样的:
总而言之:
通常情况下,should子句是数组字段,包含多个should子查询,默认情况下,匹配的文档必须满足其中一个子查询条件。如果查询需要改变默认匹配行为,查询DSL必须显式设置布尔查询的参数minimum_should_match的值,该参数控制一个文档必须匹配的should子查询的数量,我遇到一个布尔查询语句,其should子句中包含两个查询,如果不设置参数minimum_should_match,其默认值是0。建议在布尔查询中,显示设置参数minimum_should_match的值。
注:布尔查询的四个子句,都可以是数组字段,因此,支持嵌套逻辑操作的查询。
例如,对于以下should查询,一个文档必须满足should子句中两个以上的词条查询
SearchSourceBuilder sb=new SearchSourceBuilder();
sb.query(QueryBuilders.boolQuery().should(QueryBuilders.termQuery("scale_bdm_id", "1470"))
.should(QueryBuilders.rangeQuery("stat_date").gte("20180601").lte("20180603"))
.should(QueryBuilders.termsQuery("scale_org_id", new Integer[] {
174,128}))
.should(QueryBuilders.termsQuery("scale_place_status", new Integer[] {
1})).minimumShouldMatch(2)
);
布尔查询的各个子句之间的逻辑关系是与(and),这意味着,一个文档只有同时满足所有的查询子句时,该文档才匹配查询条件,作为结果返回。
在布尔查询中,对查询结果的过滤,建议使用过滤(filter)子句和must_not子句,这两个子句属于过滤上下文(Filter Context),经常使用filter子句,使得ElasticSearch引擎自动缓存数据,当再次搜索已经被缓存的数据时,能够提高查询性能;由于过滤上下文不影响查询的评分,而评分计算让搜索变得复杂,消耗更多CPU资源,因此,filter和must_not查询减轻搜索的工作负载。
一、查询和过滤上下文
在布尔查询中,查询被分为Query Context 和 Filter Context,查询上下文由query参数指定,过滤上下文由filter和must_not参数指定。这两个查询上下文的唯一区别是:Filter Context不影响查询的评分(score)。在布尔查询中,Filter参数和must_not参数使用Filter Context,而must和should使用Query Context,经常使用Filter Context,引擎会自动缓存数据,提高查询性能。
SearchSourceBuilder sb=new SearchSourceBuilder();
sb.query(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("scale_bdm_id", "1470"))
.filter(QueryBuilders.rangeQuery("stat_date").gte("20180601").lte("20180603"))
);
二、布尔查询子句的逻辑关系
在布尔查询中,各个子句之间的逻辑关系是与(and)。对于单个子句,只要一个文档满足该子句的查询条件,返回的逻辑结果就是true,而对于should子句,它一般包含多个子查询条件,参数 minimum_should_match 控制文档必须满足should子句中的子查询条件的数量,只有当文档满足指定数量的should查询条件时,should子句返回的逻辑结果才是true。
SearchSourceBuilder sb=new SearchSourceBuilder();
sb.query(QueryBuilders.boolQuery().filter(QueryBuilders.termQuery("scale_bdm_id", "1470"))
.mustNot(QueryBuilders.rangeQuery("stat_date").gte("20180601").lte("20180603"))
.should(QueryBuilders.termQuery("scale_org_id", 174))
.should(QueryBuilders.termQuery("scale_org_id", 128)).minimumShouldMatch(1));
);
在上述布尔查询中,should子句中包含两个词条查询,由于参数 minimum_should_match的值是1,因此,只要一个稳定满足任意一个词条查询的条件,should子句就匹配成功,返回逻辑结果true,然后和其他子查询进行逻辑运算,只有当该文档满足所有的子查询条件时,才作为查询结果返回到客户端。
三、布尔查询示例分析
1、使用布尔查询实现复杂的分组查询
复杂的分组查询,例如:(A and B) or (C and D) or (E and F) ,把布尔查询作为should子句的一个子查询:
SearchSourceBuilder sb=new SearchSourceBuilder();
sb.query(QueryBuilders.boolQuery().should(QueryBuilders.boolQuery().filter(QueryBuilders.termQuery("scale_org_id", 1))
.filter(QueryBuilders.termQuery("scale_org_id", 2)))
.should(QueryBuilders.boolQuery().filter(QueryBuilders.termQuery("scale_org_id", 3))
.filter(QueryBuilders.termQuery("scale_org_id", 4)))
.should(QueryBuilders.boolQuery().filter(QueryBuilders.termQuery("scale_org_id", 5))
.filter(QueryBuilders.termQuery("scale_org_id", 6)))
);
1.term查询
term是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇
2.match查询
match和term的区别是,match查询的时候,elasticsearch会根据你给定的字段提供合适的分析器,而term查询不会有分析器分析的过程
3.match_all查询
查询指定索引下的所有文档
4.match_phrase查询
短语查询, 搜索的文档必须包含所有短语的分词结果而且顺序一致
5.multi_match查询
可以指定多个字段
sb.query(QueryBuilders.multiMatchQuery("林和西", "shop_address","scale_devops_name"));
6. range范围查询
sb.query(QueryBuilders.rangeQuery("stat_date").gte("20180701").lte("20180606"));
7. wildcard 模糊查询
sb.query(QueryBuilders.wildcardQuery("scale_devops_name", "*刘刚*"));
1. 聚合API
ES中的Aggregations API是从Facets功能基础上发展而来,官网正在进行替换计划,建议用户使用Aggregations API,而不是Facets API。ES中的聚合上可以分为下面两类:
metric(度量)聚合:度量类型聚合主要针对的number类型的数据,需要ES做比较多的计算工作
bucketing(桶)聚合:划分不同的“桶”,将数据分配到不同的“桶”里。非常类似sql中的group语句的含义。
metric既可以作用在整个数据集上,也可以作为bucketing的子聚合作用在每一个“桶”中的数据集上。当然,我们可以把整个数据集合看做一个大“桶”,所有的数据都分配到这个大“桶”中。
1.1 度量类型(metric)聚合
(1)Min Aggregation
最小值查询,作用于number类型字段上。
String[] indes = {
"scale_add_fans_day2"};
SearchRequest searchRequest = new SearchRequest(indes,sb);
SearchResponse search = getClient().search(searchRequest);
Min min= search.getAggregations().get("add_fans_count_min");
(2)Max Aggregation
最大值查询
sb.aggregation(AggregationBuilders.max("add_fans_count_max").field("add_fans_count"));
String[] indes = {
"scale_add_fans_day2"};
SearchRequest searchRequest = new SearchRequest(indes,sb);
SearchResponse search = getClient().search(searchRequest);
Max max= search.getAggregations().get("add_fans_count_max");
(3)Sum Aggregation
数值求和
sb.aggregation(AggregationBuilders.sum("add_fans_count_sum").field("add_fans_count"));
String[] indes = {
"scale_add_fans_day2"};
SearchRequest searchRequest = new SearchRequest(indes,sb);
SearchResponse search = getClient().search(searchRequest);
Sum sum= search.getAggregations().get("add_fans_count_sum");
(4)Avg Aggregation
计算平均值
sb.aggregation(AggregationBuilders.avg("add_fans_count_avg").field("add_fans_count"));
String[] indes = {
"scale_add_fans_day2"};
SearchRequest searchRequest = new SearchRequest(indes,sb);
SearchResponse search = getClient().search(searchRequest);
Avg avg= search.getAggregations().get("add_fans_count_avg");
(5)Stats Aggregation
统计查询,一次性统计出某个字段上的常用统计值
sb.aggregation(AggregationBuilders.stats("add_fans_count_stats").field("add_fans_count"));
String[] indes = {
"scale_add_fans_day2"};
SearchRequest searchRequest = new SearchRequest(indes,sb);
SearchResponse search = getClient().search(searchRequest);
Stats stats= search.getAggregations().get("add_fans_count_stats");
{
"avg":5.689242113293784,"avgAsString":"5.689242113293784","count":1158722,"fragment":true,"max":466.0,"maxAsString":"466.0","min":0.0,"minAsString":"0.0","name":"add_fans_count_stats","sum":6592250.0,"sumAsString":"6592250.0","type":"stats"}
(6)Top hits Aggregation
取符合条件的前n条数据记录
SearchSourceBuilder sb=new SearchSourceBuilder();
sb.query(QueryBuilders.termQuery("scale_org_id", 1));
sb.aggregation(AggregationBuilders.topHits("top").size(2).sort("stat_date", SortOrder.DESC));
String[] indes = {
"scale_add_fans_day2"};
SearchRequest searchRequest = new SearchRequest(indes,sb);
SearchResponse search = getClient().search(searchRequest);
TopHits tophits= search.getAggregations().get("top");
1.2 桶类型(bucketing)聚合
(1)Terms Aggregation
SearchSourceBuilder sb=new SearchSourceBuilder();
sb.query(QueryBuilders.termQuery("scale_org_id", 1));
sb.aggregation(AggregationBuilders.terms("scale_id_group").field("scale_id").size(5).order(BucketOrder.count(false)));
String[] indes = {
"scale_add_fans_day2"};
SearchRequest searchRequest = new SearchRequest(indes,sb);
SearchResponse search = getClient().search(searchRequest);
Terms terms= search.getAggregations().get("scale_id_group");
(2)Range Aggregation
SearchSourceBuilder sb=new SearchSourceBuilder();
sb.aggregation(AggregationBuilders.range("scale_org_id_range").field("scale_org_id").addRange(1, 100).addRange(101, 300));
String[] indes = {
"scale_add_fans_day2"};
SearchRequest searchRequest = new SearchRequest(indes,sb);
SearchResponse search = getClient().search(searchRequest);
System.out.println(JSON.toJSONString( search.getAggregations().get("scale_org_id_range")));
Range range= search.getAggregations().get("scale_org_id_range");
for(Range.Bucket bucket:range.getBuckets()) {
bucket.getDocCount();
}
(3)Date Range Aggregation
SearchSourceBuilder sb=new SearchSourceBuilder();
sb.query(QueryBuilders.matchAllQuery());
sb.aggregation(AggregationBuilders.dateRange("create_time_range").field("create_time").addRange("a", "20180605000000", "20180605235959")
.addRange("b", "20180606000000", "20180606235959"));
String[] indes = {
"baidu_data2"};
SearchRequest searchRequest = new SearchRequest(indes,sb);
SearchResponse search = getClient().search(searchRequest);
Range range = search.getAggregations().get("create_time_range");
for(Range.Bucket bucket:range.getBuckets()) {
bucket.getKeyAsString();
bucket.getDocCount();
}
(4)Histogram Aggregation
直方图聚合,它将某个number类型字段等分成n份,统计落在每一个区间内的记录数。它与前面介绍的Range聚合非常像,只不过Range可以任意划分区间,而Histogram做等间距划分。既然是等间距划分,那么参数里面必然有距离参数,就是interval参数。
SearchSourceBuilder sb=new SearchSourceBuilder();
sb.aggregation(AggregationBuilders.histogram("add_fans_count_his").field("add_fans_count").interval(10));
String[] indes = {
"scale_add_fans_day2"};
SearchRequest searchRequest = new SearchRequest(indes,sb);
SearchResponse search = getClient().search(searchRequest);
System.out.println(JSON.toJSONString( search.getAggregations().get("add_fans_count_his")));
Histogram histogram= search.getAggregations().get("add_fans_count_his");
for(Histogram.Bucket bucket:histogram.getBuckets()) {
bucket.getKeyAsString();
bucket.getDocCount();
}
1.3 嵌套使用
前面已经说过,聚合操作是可以嵌套使用的。通过嵌套,可以使得metric类型的聚合操作作用在每一“桶”上。我们可以使用ES的嵌套聚合操作来完成稍微复杂一点的统计功能
SearchSourceBuilder sb=new SearchSourceBuilder();
sb.query(QueryBuilders.matchAllQuery());
sb.aggregation(AggregationBuilders.terms("scale_id_group").field("scale_id")
.subAggregation(AggregationBuilders.terms("stat_date_group").field("stat_date")));
String[] indes = {
"scale_add_fans_day2"};
SearchRequest searchRequest = new SearchRequest(indes,sb);
SearchResponse search = getClient().search(searchRequest);
Terms terms= search.getAggregations().get("add_fans_count_stats");
for(Terms.Bucket bucket:terms.getBuckets()) {
bucket.getKeyAsString();
bucket.getDocCount();
Terms terms2= bucket.getAggregations().get("stat_date_group");
for(Terms.Bucket bucket2:terms2.getBuckets()) {
bucket2.getKeyAsString();
bucket2.getDocCount();
}
}