学习之路 ── elasticsearch查询语法

努力学习,老大给的文档!


一.组合查询

布尔查询是最常用的组合查询,不仅将多个查询条件组合在一起,并且将查询的结果和结果的评分组合在一起。当查询条件是多个表达式的组合时,布尔查询非常有用,实际上,布尔查询把多个子查询组合(combine)成一个布尔表达式,所有子查询之间的逻辑关系是与(and);只有当一个文档满足布尔查询中的所有子查询条件时,ElasticSearch引擎才认为该文档满足查询条件。布尔查询支持的子查询类型共有四种,分别是:mustshouldmust_notfilter

  • must子句:文档必须匹配must查询条件;
  • should子句:文档应该匹配should子句查询的一个或多个;
  • must_not子句:文档不能匹配该查询条件;
  • filter子句:过滤器,文档必须匹配该过滤条件,跟must子句的唯一区别是,filter不影响查询的score;

Query与Filter

查询在Query查询上下文和Filter过滤器上下文中,执行的操作是不一样的:

  1. 查询上下文:是在使用query进行查询时的执行环境,比如使用search的时候。
    在查询上下文中,查询会回答这个问题——“这个文档是否匹配这个查询,它的相关度高么?”
    ES中索引的数据都会存储一个_score分值,分值越高就代表越匹配。即使lucene使用倒排索引,对于某个搜索的分值计算还是需要一定的时间消耗。
  2. 过滤器上下文:在使用filter参数时候的执行环境,比如在bool查询中使用Must_not或者filter
    在过滤器上下文中,查询会回答这个问题——“这个文档是否匹配?” 它不会去计算任何分值,也不会关心返回的排序问题,因此效率会高一点。
    另外,经常使用过滤器,ES会自动的缓存过滤器的内容,这对于查询来说,会提高很多性能。

总而言之:

  1. 查询上下文:查询操作不仅仅会进行查询,还会计算分值,用于确定相关度;
  2. 过滤器上下文:查询操作仅判断是否满足查询条件,不会计算得分,查询的结果可以被缓存。
    所以,根据实际的需求是否需要获取得分,考虑性能因素,选择不同的查询子句。

通常情况下,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();
			}
		}

你可能感兴趣的:(Java,java,elasticsearch)