乾貨 | 通透理解Elasticsearch聚合
使用Elasticsearch的過程中,除了全文檢索,或多或少會做統計操作,而做統計操作勢必會使用Elasticsearch聚合操作。
類似mysql中group by的terms聚合用的最多,但當遇到複雜的聚合操作時,往往會捉襟見肘、不知所措…
這也是社群中聚合操作幾乎每天都會被提問的原因。
本文基於官方文件,梳理出聚合的以下幾個核心問題,目的:將Elasticsearch的聚合結合實際場景說透。
1、Elasticsearch聚合最直觀展示
區別於倒排索引的key value的全文檢索,聚合兩個示例如下:
如下圖,是基於某特定分類的聚合統計結果。
如下圖:是基於月份的聚合統計結果。
2、Elasticsearch聚合定義
聚合有助於基於搜尋查詢提供聚合資料。 它基於稱為聚合的簡單構建塊,可以組合以構建複雜的資料。
基本語法結構如下:
"aggregations" : { "<aggregation_name>" : { "<aggregation_type>" : { <aggregation_body> } [,"meta" : { [<meta_data_body>] } ]? [,"aggregations" : { [<sub_aggregation>]+ } ]? } [,"<aggregation_name_2>" : { ... } ]* }
3、Elasticsearch聚合分類
3.1 分類1:Metric聚合
基於一組文件進行聚合。所有的文件在一個檢索集合裡,文件被分成邏輯的分組。
類比Mysql中的: MIN(), MAX(), STDDEV(), SUM() 操作。
單值Metric | v SELECT AVG(price) FROM products 多值Metric | | v v SELECT MIN(price), MAX(price) FROM products Metric聚合的DSL類比實現: { "aggs":{ "avg_price":{ "avg":{ "field":"price" } } } }
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使用舉例:
基於size 分桶 ...、
SELECT size COUNT(*) FROM products GROUP BY size
+----------------------+
| size | COUNT(*) |
+----------------------+
| S | 123 | <--- set of rows with size = S
| M | 456 |
| ... | ... |
bucket聚合的DSL類比實現:
{
"query": {
"match": {
"title": "Beach"
}
},
"aggs": {
"by_size": {
"terms": {
"field": "size"
}
},
"by_material": {
"terms": {
"field": "material"
}
}
}
}
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中你可以寫:
SELECT 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,匯入完整資料
POST _bulk
{"index":{"_index":"cars","_type":"doc","_id":"1"}}
{"name":"bmw","date":"2017-06-01", "color":"red", "price":30000}
{"index":{"_index":"cars","_type":"doc","_id":"2"}}
{"name":"bmw","date":"2017-06-30", "color":"blue", "price":50000}
{"index":{"_index":"cars","_type":"doc","_id":"3"}}
{"name":"bmw","date":"2017-08-11", "color":"red", "price":90000}
{"index":{"_index":"cars","_type":"doc","_id":"4"}}
{"name":"ford","date":"2017-07-15", "color":"red", "price":20000}
{"index":{"_index":"cars","_type":"doc","_id":"5"}}
{"name":"ford","date":"2017-07-01", "color":"blue", "price":40000}
{"index":{"_index":"cars","_type":"doc","_id":"6"}}
{"name":"bmw","date":"2017-08-01", "color":"green", "price":10000}
{"index":{"_index":"cars","_type":"doc","_id":"7"}}
{"name":"jeep","date":"2017-07-08", "color":"red", "price":110000}
{"index":{"_index":"cars","_type":"doc","_id":"8"}}
{"name":"jeep","date":"2017-08-25", "color":"red", "price":230000}
3.2 步驟2:確認Mapping
GET cars/_mapping
3.3 步驟3:Matric聚合實現
求車的平均價錢。
POST cars/_search
{
"size": 0,
"aggs": {
"avg_grade": {
"avg": {
"field": "price"
}
}
}
}
3.4 步驟4:bucket聚合與子聚合實現
按照車品牌分組,組間按照車顏色再二次分組。
POST cars/_search
{
"size": 0,
"aggs": {
"name_aggs": {
"terms": {
"field": "name.keyword"
},
"aggs": {
"color_aggs": {
"terms": {
"field": "color.keyword"
}
}
}
}
}
}
3.5 步驟5:Pipeline聚合實現
按月統計銷售額,並統計出總銷售額大於200000的月份資訊。
POST /cars/_search
{
"size": 0,
"aggs": {
"sales_per_month": {
"date_histogram": {
"field": "date",
"interval": "month"
},
"aggs": {
"total_sales": {
"sum": {
"field": "price"
}
},
"sales_bucket_filter": {
"bucket_selector": {
"buckets_path": {
"totalSales": "total_sales"
},
"script": "params.totalSales > 200000"
}
}
}
}
}
}
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部分多驗證。
打造Elasticsearch基礎、進階、實戰第一公眾號!