使用Elasticsearch的过程中,除了全文检索,或多或少会做统计操作,而做统计操作势必会使用Elasticsearch聚合操作。
类似mysql中group by的terms聚合用的最多,但当遇到复杂的聚合操作时,往往会捉襟见肘、不知所措…..这也是社区中聚合操作几乎每天都会被提问的原因。
本文基于官方文档,梳理出聚合的以下几个核心问题,目的:将Elasticsearch的聚合结合实际场景说透。
1、Elasticsearch聚合最直观展示
区别于倒排索引的key value的全文检索,聚合两个示例如下:
如下图,是基于某特定分类的聚合统计结果。
2、Elasticsearch聚合定义
聚合是ES除了搜索功能外提供的针对ES数据做统计分析的功能。
搜索引擎的搜索部分侧重于过滤和搜索,而聚合侧重于数据统计和分析。
基本语法结构如下:
1"aggregations" : {
2 "
3 "
4
5 }
6 [,"meta" : { [
7 [,"aggregations" : { [
8 }
9 [,"
10}
3、Elasticsearch聚合分类
3.1 分类1:Metric聚合
基于一组文档进行聚合。所有的文档在一个检索集合里,文档被分成逻辑的分组。
类比Mysql中的: MIN(), MAX(), STDDEV(), SUM() 操作。
1 单值Metric
2 |
3 v
4SELECT AVG(price) FROM products
5
6
7 多值Metric
8 | |
9 v v
10SELECT MIN(price), MAX(price) FROM products
11Metric聚合的DSL类比实现:
12{
13 "aggs":{
14 "avg_price":{
15 "avg":{
16 "field":"price"
17 }
18 }
19 }
20}
Metric聚合操作对比:
Aggregation Elasticsearch MySQL
Avg Yes Yes
Cardinality——去重唯一值 Yes (Sample based) Yes (Exact)——类似:distinct
Extended Stats Yes StdDev bounds missing
Geo Bounds Yes for future blog post
Geo Centroid Yes for future blog post
Max Yes Yes
Percentiles Yes Complex SQL or UDF
Percentile Ranks Yes Complex SQL or UDF
Scripted Yes No
Stats Yes Yes
Top Hits——很重要,易被忽视 Yes Complex
Value Count Yes Yes
其中,Top hits子聚合用于返回分组中Top X匹配结果集,且支持通过source过滤选定字段值。
分类2:Bucketing聚合
基于检索构成了逻辑文档组,满足特定规则的文档放置到一个桶里,每一个桶关联一个key。
类比Mysql中的group by操作,Mysql使用举例:
1 基于size 分桶 ...、
2SELECT size COUNT() FROM products GROUP BY size
3
4+----------------------+
5| size | COUNT() |
6+----------------------+
7| S | 123 | <--- set of rows with size = S
8| M | 456 |
9| ... | ... |
bucket聚合的DSL类比实现:
1{
2 "query": {
3 "match": {
4 "title": "Beach"
5 }
6 },
7 "aggs": {
8 "by_size": {
9 "terms": {
10 "field": "size"
11 }
12 },
13 "by_material": {
14 "terms": {
15 "field": "material"
16 }
17 }
18 }
19}
Bucketing聚合对比
Aggregation Elasticsearch MySQL
Childen——父子文档 Yes for future blog post
Date Histogram——基于时间分桶 Yes Complex
Date Range Yes Complex
Filter Yes n/a (yes)
Filters Yes n/a (yes)
Geo Distance Yes for future blog post
GeoHash grid Yes for future blog post
Global Yes n/a (yes)
Histogram Yes Complex
IPv4 Range Yes Complex
Missing Yes Yes
Nested Yes for future blog post
Range Yes Complex
Reverse Nested Yes for future blog post
Sampler Yes Complex
Significant Terms Yes No
Terms——最常用 Yes Yes
分类3:Pipeline聚合
对聚合的结果而不是原始数据集进行操作。
想象一下,你有一个日间交易的网上商店,想要了解所有产品的按照库存日期分组的平均价格。
在SQL中你可以写:
1SELECT in_stock_since, AVG(price) FROM products GROUP BY in_stock_since。
ES使用举例:以下Demo实现更复杂,按月统计销售额,并统计出月销售额>200的信息。
下一节详细给出DSL,不再重复。
分类4:Matrix聚合
ES6.4官网释义:此功能是实验性的,可在将来的版本中完全更改或删除。
3、Elasticsearch聚合完整举例
3.1 步骤1:动态Mapping,导入完整数据
1POST _bulk
2{"index":{"_index":"cars","_type":"doc","_id":"1"}}
3{"name":"bmw","date":"2017-06-01", "color":"red", "price":30000}
4{"index":{"_index":"cars","_type":"doc","_id":"2"}}
5{"name":"bmw","date":"2017-06-30", "color":"blue", "price":50000}
6{"index":{"_index":"cars","_type":"doc","_id":"3"}}
7{"name":"bmw","date":"2017-08-11", "color":"red", "price":90000}
8{"index":{"_index":"cars","_type":"doc","_id":"4"}}
9{"name":"ford","date":"2017-07-15", "color":"red", "price":20000}
10{"index":{"_index":"cars","_type":"doc","_id":"5"}}
11{"name":"ford","date":"2017-07-01", "color":"blue", "price":40000}
12{"index":{"_index":"cars","_type":"doc","_id":"6"}}
13{"name":"bmw","date":"2017-08-01", "color":"green", "price":10000}
14{"index":{"_index":"cars","_type":"doc","_id":"7"}}
15{"name":"jeep","date":"2017-07-08", "color":"red", "price":110000}
16{"index":{"_index":"cars","_type":"doc","_id":"8"}}
17{"name":"jeep","date":"2017-08-25", "color":"red", "price":230000}
3.2 步骤2:确认Mapping
1GET cars/_mapping
3.3 步骤3:Matric聚合实现
求车的平均价钱。
1POST cars/_search
2{
3 "size": 0,
4 "aggs": {
5 "avg_grade": {
6 "avg": {
7 "field": "price"
8 }
9 }
10 }
11}
3.4 步骤4:bucket聚合与子聚合实现
按照车品牌分组,组间按照车颜色再二次分组。
1POST cars/_search
2{
3 "size": 0,
4 "aggs": {
5 "name_aggs": {
6 "terms": {
7 "field": "name.keyword"
8 },
9 "aggs": {
10 "color_aggs": {
11 "terms": {
12 "field": "color.keyword"
13 }
14 }
15 }
16 }
17 }
18}
3.5 步骤5:Pipeline聚合实现
按月统计销售额,并统计出总销售额大于200000的月份信息。
1POST /cars/_search
2{
3 "size": 0,
4 "aggs": {
5 "sales_per_month": {
6 "date_histogram": {
7 "field": "date",
8 "interval": "month"
9 },
10 "aggs": {
11 "total_sales": {
12 "sum": {
13 "field": "price"
14 }
15 },
16 "sales_bucket_filter": {
17 "bucket_selector": {
18 "buckets_path": {
19 "totalSales": "total_sales"
20 },
21 "script": "params.totalSales > 200000"
22 }
23 }
24 }
25 }
26 }
27}
4、Elasticsearch聚合使用指南
认知前提:知道Elasticsearch聚合远比Mysql中种类要多,可实现的功能点要多。
遇到聚合问题,基于4个分类,查询对应的官网API信息。
以最常见场景为例:
确定是否是分组group by 操作,如果是,使用bucket聚合中的terms聚合实现;
确定是否是按照时间分组操作,如果是,使用bucket聚合中date_histogram的聚合实现;
确定是否是分组,组间再分组操作,如果是,使用bucket聚合中terms聚合内部再terms或者内部top_hits子聚合实现;确定是否是分组,组间再分组操作,
确定是否是求最大值、最小值、平均值等,如果是,使用Metric聚合对应的Max, Min,AVG等聚合实现;
确定是否是基于聚合的结果条件进行判定后取结果,如果是,使用pipline聚合结合其他聚合综合实现;
多尝试,多在kibana的 dev tool部分多验证。
参考:
1、http://t.cn/R8Gk7V0
2、http://t.cn/EhxwB63
3、http://t.cn/EhxwDKR