查看原文
其他

干货 | 通透理解Elasticsearch聚合

铭毅天下 铭毅天下 2019-04-16

使用Elasticsearch的过程中,除了全文检索,或多或少会做统计操作,而做统计操作势必会使用Elasticsearch聚合操作。

类似mysql中group by的terms聚合用的最多,但当遇到复杂的聚合操作时,往往会捉襟见肘、不知所措…..这也是社区中聚合操作几乎每天都会被提问的原因。

本文基于官方文档,梳理出聚合的以下几个核心问题,目的:将Elasticsearch的聚合结合实际场景说透。

1、Elasticsearch聚合最直观展示

区别于倒排索引的key value的全文检索,聚合两个示例如下:
如下图,是基于某特定分类的聚合统计结果。


如下图:是基于月份的聚合统计结果。


2、Elasticsearch聚合定义

聚合是ES除了搜索功能外提供的针对ES数据做统计分析的功能。

搜索引擎的搜索部分侧重于过滤和搜索,而聚合侧重于数据统计和分析。

基本语法结构如下:

1"aggregations" : {
2    "<aggregation_name>" : {
3        "<aggregation_type>" : {
4            <aggregation_body>
5        }
6        [,"meta" : {  [<meta_data_body>] } ]?
7        [,"aggregations" : { [<sub_aggregation>]+ } ]?
8    }
9    [,"<aggregation_name_2>" : { ... } ]*
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聚合操作对比:

AggregationElasticsearchMySQL
AvgYesYes
Cardinality——去重唯一值Yes (Sample based)Yes (Exact)——类似:distinct
Extended StatsYesStdDev bounds missing
Geo BoundsYesfor future blog post
Geo CentroidYesfor future blog post
MaxYesYes
PercentilesYesComplex SQL or UDF
Percentile RanksYesComplex SQL or UDF
ScriptedYesNo
StatsYesYes
Top Hits——很重要,易被忽视YesComplex
Value CountYesYes

其中,Top hits子聚合用于返回分组中Top X匹配结果集,且支持通过source过滤选定字段值。

分类2:Bucketing聚合

基于检索构成了逻辑文档组,满足特定规则的文档放置到一个桶里,每一个桶关联一个key。

类比Mysql中的group by操作,Mysql使用举例:

1           基于size 分桶 ...、
2SELECT size COUNT(*) FROM products GROUP BY size 
3
4+----------------------+
5size     |  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聚合对比

AggregationElasticsearchMySQL
Childen——父子文档Yesfor future blog post
Date Histogram——基于时间分桶YesComplex
Date RangeYesComplex
FilterYesn/a (yes)
FiltersYesn/a (yes)
Geo DistanceYesfor future blog post
GeoHash gridYesfor future blog post
GlobalYesn/a (yes)
HistogramYesComplex
IPv4 RangeYesComplex
MissingYesYes
NestedYesfor future blog post
RangeYesComplex
Reverse NestedYesfor future blog post
SamplerYesComplex
Significant TermsYesNo
Terms——最常用YesYes

分类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信息。
以最常见场景为例:

  1. 确定是否是分组group by 操作,如果是,使用bucket聚合中的terms聚合实现;

  2. 确定是否是按照时间分组操作,如果是,使用bucket聚合中date_histogram的聚合实现;

  3. 确定是否是分组,组间再分组操作,如果是,使用bucket聚合中terms聚合内部再terms或者内部top_hits子聚合实现;确定是否是分组,组间再分组操作,

  4. 确定是否是求最大值、最小值、平均值等,如果是,使用Metric聚合对应的Max, Min,AVG等聚合实现;

  5. 确定是否是基于聚合的结果条件进行判定后取结果,如果是,使用pipline聚合结合其他聚合综合实现;

多尝试,多在kibana的 dev tool部分多验证。

参考:
1、http://t.cn/R8Gk7V0

2、http://t.cn/EhxwB63

3、http://t.cn/EhxwDKR

 加入知识星球,更短时间更快习得更多干货!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存