ES中的聚合被分为两大类:Metric度量和bucket桶。metric很像SQL中的avg、max、min等方法,而bucket就有点类似group by。
1. 单值聚合
求和:
1 2 3
| "aggs" : { "intraday_return" : { "sum" : { "field" : "change" } } }
|
求最大值、最小值和平均值
1 2 3 4 5
| { "aggs" : { "min_price" : { "min" : { "field" : "price" } } } }
|
1 2 3 4 5
| { "aggs" : { "max_price" : { "max" : { "field" : "price" } } } }
|
1 2 3 4 5
| { "aggs" : { "avg_grade" : { "avg" : { "field" : "grade" } } } }
|
1 2 3 4 5 6 7 8 9
| { "aggs" : { "author_count" : { "cardinality" : { "field" : "author" } } } }
|
2. 多值聚合
1 2 3 4 5 6 7 8 9 10
| { "aggs" : { "load_time_outlier" : { "percentile_ranks" : { "field" : "load_time", "values" : [15, 30] } } } }
|
1 2 3 4 5
| { "aggs" : { "grades_stats" : { "stats" : { "field" : "grade" } } } }
|
1 2 3 4 5
| { "aggs" : { "grades_stats" : { "extended_stats" : { "field" : "grade" } } } }
|
3. terms聚合
terms聚合,它是按照某个字段中的值来分类:比如性别有男、女,就会创建两个桶,分别存放男女的信息。默认会搜集doc_count的信息,即记录有多少男生,有多少女生,然后返回给客户端,这样就完成了一个terms得统计
Bucket可以理解为一个桶,他会遍历文档中的内容,凡是符合要求的就放入按照要求创建的桶中。
1 2 3 4 5 6 7
| { "aggs" : { "genders" : { "terms" : { "field" : "gender" } } } }
|
1 2 3 4 5 6 7 8 9 10
| { "aggs" : { "genders" : { "terms" : { "field" : "gender", "order" : { "_count" : "asc" } } } } }
|
也可以通过order指定一个单值的metric聚合,来排序。
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "aggs" : { "genders" : { "terms" : { "field" : "gender", "order" : { "avg_height" : "desc" } }, "aggs" : { "avg_height" : { "avg" : { "field" : "height" } } } } } }
|
- min_doc_count与shard_min_doc_count
聚合的字段可能存在一些频率很低的词条,如果这些词条数目比例很大,那么就会造成很多不必要的计算。
因此可以通过设置min_doc_count和shard_min_doc_count来规定最小的文档数目,只有满足这个参数要求的个数的词条才会被记录返回。
1 2 3 4 5 6 7 8 9 10 11
| { "aggs" : { "tags" : { "terms" : { "field" : "tags", "include" : ".*sport.*", "exclude" : "water_.*" } } } }
|
4. Histogram 直方图聚合
Elasticsearch支持直方图聚合,它在数字字段自动创建桶,并会扫描全部文档,把文档放入相应的桶中。这个数字字段既可以是文档中的某个字段,也可以通过脚本创建得出的。举个例子,有一个price字段,这个字段描述了商品的价格,现在想每隔5就创建一个桶,统计每隔区间都有多少个文档(商品)。如果有一个商品的价格为32,那么它会被放入30的桶中。
如果不想要显示count为0的桶,可以通过min_doc_count来设置。
1 2 3 4 5 6 7 8 9 10 11
| { "aggs" : { "prices" : { "histogram" : { "field" : "price", "interval" : 50, "min_doc_count" : 1 } } } }
|
指定最小值和最大值边界.默认情况下,ES中的histogram聚合起始都是自动的,比如price字段,如果没有商品的价钱在0-5之间,0这个桶就不会显示。如果最便宜的商品是11,那么第一个桶就是10.
可以通过设置extend_bounds强制规定最小值和最大值,但是要求必须min_doc_count不能大于0,不然即便是规定了边界,也不会返回。
1 2 3 4 5 6 7 8 9 10 11
| { "aggs" : { "prices" : { "histogram" : { "field" : "price", "interval" : 50, "order" : { "_key" : "desc" } } } } }
|
5. Date Histogram聚合
Date histogram的用法与histogram差不多,只不过区间上支持了日期的表达式。interval字段支持多种关键字:year, quarter, month, week, day, hour, minute, second。
1 2 3 4 5 6 7 8 9 10 11
| { "aggs":{ "articles_over_time":{ "date_histogram":{ "field":"date", "interval":"1M", "format":"yyyy-MM-dd" } } } }
|
1 2 3 4 5 6 7 8 9 10 11
| { "aggs":{ "by_day":{ "date_histogram":{ "field":"date", "interval":"day", "time_zone":"+08:00" } } } }
|
1 2 3 4 5 6 7 8 9 10
| {"aggs":{ "by_day":{ "date_histogram":{ "field":"date", "interval":"day", "offset":"+6h" } } } }
|
6. Range区间聚合
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "aggs":{ "grade_ranges":{ "range":{ "field":"grade", "ranges":[ {"to":60}, {"from":60,"to":80}, {"from":80}] } } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| { "aggs":{ "price_ranges":{ "range":{ "field":"price", "ranges":[ {"to":50}, {"from":50,"to":100}, {"from":100} ]}, "aggs":{ "price_stats":{ "stats":{ "field":"price" } } } } } }
|
7. DateRange日期范围聚合
相比于range聚合,date range就是范围可以由时间来指定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| { "aggs":{ "range":{ "date_range":{ "field":"date", "format":"MM-yyy", "ranges":[ {"to":"now-10M/M"}, {"from":"now-10M/M"} ] } } } }
|
8. 查询的种类
- Elasticsearch中的DSL主要由两部分组成:
Leaf query Cluase 叶查询子句
这种查询可以单独使用,针对某一特定的字段查询特定的值,比如match、term、range等
Compound query Cluase 复合查询子句
这种查询配合其他的叶查询或者复合查询,用于在逻辑上,组成更为复杂的查询,比如bool
查询在Query查询上下文和Filter过滤器上下文中,执行的操作是不一样的:
查询上下文:
在查询上下文中,查询会回答这个问题——“这个文档匹不匹配这个查询,它的相关度高么?”
如何验证匹配很好理解,如何计算相关度呢?之前说过,ES中索引的数据都会存储一个_score分值,分值越高就代表越匹配。另外关于某个搜索的分值计算还是很复杂的,因此也需要一定的时间。
查询上下文 是在 使用query进行查询时的执行环境,比如使用search的时候。
过滤器上下文:
在过滤器上下文中,查询会回答这个问题——“这个文档匹不匹配?”
答案很简单,是或者不是。它不会去计算任何分值,也不会关心返回的排序问题,因此效率会高一点。
过滤上下文 是在使用filter参数时候的执行环境,比如在bool查询中使用Must_not或者filter
8. 布尔查询Bool Query
query的时候,会先比较查询条件,然后计算分值,最后返回文档结果;
而filter则是先判断是否满足查询条件,如果不满足,会缓存查询过程(记录该文档不满足结果);满足的话,就直接缓存结果。
综上所述,filter快在两个方面:
1 对结果进行缓存
2 避免计算分值
bool查询也是采用more_matches_is_better的机制,因此满足must和should子句的文档将会合并起来计算分值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| { "bool" : { "must" : { "term" : { "user" : "kimchy" } }, "filter": { "term" : { "tag" : "tech" } }, "must_not" : { "range" : { "age" : { "from" : 10, "to" : 20 } } }, "should" : [ { "term" : { "tag" : "wow" } }, { "term" : { "tag" : "elasticsearch" } } ], "minimum_should_match" : 1, "boost" : 1.0 } }
|
如果想知道到底是bool里面哪个条件匹配,可以使用named query查询:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| { "bool" : { "should" : [ {"match" : { "name.first" : {"query" : "shay", "_name" : "first"} }}, {"match" : { "name.last" : {"query" : "banon", "_name" : "last"} }} ], "filter" : { "terms" : { "name.last" : ["banon", "kimchy"], "_name" : "test" } } } }
|