天天看點

幹貨 | 通透了解Elasticsearch聚合

前言

本文基于官方文檔,梳理出聚合的以下幾個核心問題,目的:将Elasticsearch的聚合結合實際場景說透。

1、Elasticsearch聚合最直覺展示

差別于反向索引的key value的全文檢索,聚合兩個示例如下:

如下圖,是基于某特定分類的聚合統計結果。

幹貨 | 通透了解Elasticsearch聚合

如下圖:是基于月份的聚合統計結果。

幹貨 | 通透了解Elasticsearch聚合

2、Elasticsearch聚合定義

聚合有助于基于搜尋查詢提供聚合資料。 它基于稱為聚合的簡單建構塊,可以組合以建構複雜的資料。

基本文法結構如下:

"aggregations" : {
    "<aggregation_name>" : {
        "<aggregation_type>" : {
            <aggregation_body>
        }
        [,"meta" : {  [<meta_data_body>] } ]?
        [,"aggregations" : { [<sub_aggregation>]+ } ]?
    }
    [,"<aggregation_name_2>" : { ... } ]*
}           

3、Elasticsearch聚合分類

幹貨 | 通透了解Elasticsearch聚合

分類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
Cardinality——去重唯一值 Yes (Sample based) Yes (Exact)——類似:distinct
Extended Stats StdDev bounds missing
Geo Bounds for future blog post
Geo Centroid
Max
Percentiles Complex SQL or UDF
Percentile Ranks
Scripted No
Stats
Top Hits——很重要,易被忽視 Complex
Value Count

其中,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聚合對比

Childen——父子文檔
Date Histogram——基于時間分桶
Date Range
Filter n/a (yes)
Filters
Geo Distance
GeoHash grid
Global
Histogram
IPv4 Range
Missing
Nested
Range
Reverse Nested
Sampler
Significant Terms
Terms——最常用

分類3:Pipeline聚合

對聚合的結果而不是原始資料集進行操作。

想象一下,你有一個日間交易的網上商店,想要了解所有産品的按照庫存日期分組的平均價格。

在SQL中你可以寫:

SELECT in_stock_since, AVG(price) FROM products GROUP BY in_stock_since。           

ES使用舉例:

以下Demo實作更複雜,按月統計銷售額,并統計出月銷售額>200的資訊。

下一節詳細給出DSL,不再重複。

分類4:Matrix聚合

ES6.4官網釋義:此功能是實驗性的,可在将來的版本中完全更改或删除。

4、Elasticsearch聚合完整舉例

4.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}           

4.2 步驟2:确認Mapping

GET cars/_mapping           

4.3 步驟3:Matric聚合實作

求車的平均價錢。

POST cars/_search
{
  "size": 0,
  "aggs": {
    "avg_grade": {
      "avg": {
        "field": "price"
      }
    }
  }
}           

4.4 步驟4:bucket聚合與子聚合實作

按照車品牌分組,組間按照車顔色再二次分組。

POST cars/_search
{
  "size": 0,
  "aggs": {
    "name_aggs": {
      "terms": {
        "field": "name.keyword"
      },
      "aggs": {
        "color_aggs": {
          "terms": {
            "field": "color.keyword"
          }
        }
      }
    }
  }
}           

4.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"
          }
        }
      }
    }
  }
}           

5、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、

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html

2、

http://blog.ulf-wendel.de/2016/aggregation-features-elasticsearch-vs-mysql-vs-mongodb/

3、

https://elasticsearch.cn/article/629
幹貨 | 通透了解Elasticsearch聚合

打造Elasticsearch基礎、進階、實戰第一公衆号!