Elasticsearch(Es)聚合查詢(指標聚合、桶聚合)
Elasticsearch 的聚合功能十分強大,可在數據上做復雜的分析統計。它提供的聚合分析功能有指標聚合(metrics aggregations)、桶聚合(bucket aggregations)、管道聚合(pipeline aggregations)和矩陣聚合(matrix aggregations)四大類。
聚合的具體結構
所有的聚合,無論它們是什么類型,都遵從以下的規則。
- 使用查詢中同樣的 JSON 請求來定義它們,而且你是使用鍵 aggregations 或者是 aggs 來進行標記。需要給每個聚合起一個名字,指定它的類型以及和該類型相關的選項。
- 它們運行在查詢的結果之上。和查詢不匹配的文檔不會計算在內,除非你使用 global 聚集將不匹配的文檔囊括其中。
- 可以進一步過濾查詢的結果,而不影響聚集。
以下是聚合的基本結構:
"aggregations" : { <!-- 最外層的聚合鍵,也可以縮寫為 aggs -->
"<aggregation_name>" : { <!-- 聚合的自定義名字 -->
"<aggregation_type>" : { <!-- 聚合的類型,指標相關的,如 max、min、avg、sum,桶相關的 terms、filter 等 -->
<aggregation_body> <!-- 聚合體:對哪些字段進行聚合,可以取字段的值,也可以是腳本計算的結果 -->
}
[,"meta" : { [<meta_data_body>] } ]? <!-- 元 -->
[,"aggregations" : { [<sub_aggregation>]+ } ]? <!-- 在聚合里面在定義子聚合 -->
}
[,"<aggregation_name_2>" : { ... } ]* <!-- 聚合的自定義名字 2 -->
}
- 在最上層有一個 aggregations 的鍵,可以縮寫為 aggs。
- 在下面一層,需要為聚合指定一個名字。可以在請求的返回中看到這個名字。在同一個請求中使用多個聚合時,這一點非常有用,它讓你可以很容易地理解每組結果的含義。
- 最后,必須要指定聚合的類型。
關於聚合分析的值來源,可以取字段的值,也可以是腳本計算的結果。
但是用腳本計算的結果時,需要注意腳本的性能和安全性;盡管多數聚集類型允許使用腳本,但是腳本使得聚集變得緩慢,因為腳本必須在每篇文檔上運行。為了避免腳本的運行,可以在索引階段進行計算。
此外,腳本也可以被人可能利用進行惡意代碼攻擊,盡量使用沙盒(sandbox)內的腳本語言。
示例
查詢所有球員的平均年齡是多少,並對球員的平均薪水加 188(也可以理解為每名球員加 188 后的平均薪水)。
POST /player/_search?size=0
{
"aggs": {
"avg_age": {
"avg": {
"field": "age"
}
},
"avg_salary_188": {
"avg": {
"script": {
"source": "doc.salary.value + 188"
}
}
}
}
}
指標聚合
指標聚合(又稱度量聚合)主要從不同文檔的分組中提取統計數據,或者,從來自其他聚合的文檔桶來提取統計數據。
這些統計數據通常來自數值型字段,如最小或者平均價格。用戶可以單獨獲取每項統計數據,或者也可以使用 stats 聚合來同時獲取它們。更高級的統計數據,如平方和或者是標准差,可以通過 extended stats 聚合來獲取。
Max Aggregation
Max Aggregation 用於最大值統計。例如,統計 sales 索引中價格最高的是哪本書,並且計算出對應的價格的 2 倍值,查詢語句如下:
GET /sales/_search?size=0
{
"aggs" : {
"max_price" : {
"max" : {
"field" : "price"
}
},
"max_price_2" : {
"max" : {
"field" : "price",
"script": {
"source": "_value * 2.0"
}
}
}
}
}
指定的 field,在腳本中可以用 _value 取字段的值。
聚合結果如下:
{
...
"aggregations": {
"max_price": {
"value": 188.0
},
"max_price_2": {
"value": 376.0
}
}
}
Min Aggregation
Min Aggregation 用於最小值統計。例如,統計 sales 索引中價格最低的是哪本書,查詢語句如下:
GET /sales/_search?size=0
{
"aggs" : {
"min_price" : {
"min" : {
"field" : "price"
}
}
}
}
聚合結果如下:
{
...
"aggregations": {
"min_price": {
"value": 18.0
}
}
}
Avg Aggregation
Avg Aggregation 用於計算平均值。例如,統計 exams 索引中考試的平均分數,如未存在分數,默認為 60 分,查詢語句如下:
GET /exams/_search?size=0
{
"aggs" : {
"avg_grade" : {
"avg" : {
"field" : "grade",
"missing": 60
}
}
}
}
如果指定字段沒有值,可以通過 missing 指定默認值;若未指定默認值,缺失該字段值的文檔將被忽略(計算)。
聚合結果如下:
{
...
"aggregations": {
"avg_grade": {
"value": 78.0
}
}
}
除了常規的平均值聚合計算外,elasticsearch 還提供了加權平均值的聚合計算,詳情參見 Elasticsearch 指標聚合之 Weighted Avg Aggregation。
Sum Aggregation
Sum Aggregation 用於計算總和。例如,統計 sales 索引中 type 字段中匹配 hat 的價格總和,查詢語句如下:
GET /exams/_search?size=0
{
"query" : {
"constant_score" : {
"filter" : {
"match" : { "type" : "hat" }
}
}
},
"aggs" : {
"hat_prices" : {
"sum" : { "field" : "price" }
}
}
}
聚合結果如下:
{
...
"aggregations": {
"hat_prices": {
"value": 567.0
}
}
}
Value Count Aggregation
Value Count Aggregation 可按字段統計文檔數量。例如,統計 books 索引中包含 author 字段的文檔數量,查詢語句如下:
GET /books/_search?size=0
{
"aggs" : {
"doc_count" : {
"value_count" : { "field" : "author" }
}
}
}
聚合結果如下:
{
...
"aggregations": {
"doc_count": {
"value": 5
}
}
}
Cardinality Aggregation
Cardinality Aggregation 用於基數統計,其作用是先執行類似 SQL 中的 distinct 操作,去掉集合中的重復項,然后統計排重后的集合長度。例如,在 books 索引中對 language 字段進行 cardinality 操作可以統計出編程語言的種類數,查詢語句如下:
GET /books/_search?size=0
{
"aggs" : {
"all_lan" : {
"cardinality" : { "field" : "language" }
},
"title_cnt" : {
"cardinality" : { "field" : "title.keyword" }
}
}
}
假設 title 字段為文本類型(text),去重時需要指定 keyword,表示把 title 作為整體去重,即不分詞統計。
聚合結果如下:
{
...
"aggregations": {
"all_lan": {
"value": 8
},
"title_cnt": {
"value": 18
}
}
}
Stats Aggregation
Stats Aggregation 用於基本統計,會一次返回 count、max、min、avg 和 sum 這 5 個指標。例如,在 exams 索引中對 grade 字段進行分數相關的基本統計,查詢語句如下:
GET /exams/_search?size=0
{
"aggs" : {
"grades_stats" : {
"stats" : { "field" : "grade" }
}
}
}
聚合結果如下:
{
...
"aggregations": {
"grades_stats": {
"count": 2,
"min": 50.0,
"max": 100.0,
"avg": 75.0,
"sum": 150.0
}
}
}
Extended Stats Aggregation
Extended Stats Aggregation 用於高級統計,和基本統計功能類似,但是會比基本統計多出以下幾個統計結果,sum_of_squares(平方和)、variance(方差)、std_deviation(標准差)、std_deviation_bounds(平均值加/減兩個標准差的區間)。在 exams 索引中對 grade 字段進行分數相關的高級統計,查詢語句如下:
GET /exams/_search?size=0
{
"aggs" : {
"grades_stats" : {
"extended_stats" : { "field" : "grade" }
}
}
}
聚合結果如下:
{
...
"aggregations": {
"grades_stats": {
"count": 2,
"min": 50.0,
"max": 100.0,
"avg": 75.0,
"sum": 150.0,
"sum_of_squares": 12500.0,
"variance": 625.0,
"std_deviation": 25.0,
"std_deviation_bounds": {
"upper": 125.0,
"lower": 25.0
}
}
}
}
Percentiles Aggregation
Percentiles Aggregation 用於百分位統計。百分位數是一個統計學術語,如果將一組數據從大到小排序,並計算相應的累計百分位,某一百分位所對應數據的值就稱為這一百分位的百分位數。默認情況下,累計百分位為 [ 1, 5, 25, 50, 75, 95, 99 ]。以下例子給出了在 latency 索引中對 load_time 字段進行加載時間的百分位統計,查詢語句如下:
GET latency/_search
{
"size": 0,
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"field" : "load_time"
}
}
}
}
需要注意的是,如上的 load_time 字段必須是數字類型。
聚合結果如下:
{
...
"aggregations": {
"load_time_outlier": {
"values" : {
"1.0": 5.0,
"5.0": 25.0,
"25.0": 165.0,
"50.0": 445.0,
"75.0": 725.0,
"95.0": 945.0,
"99.0": 985.0
}
}
}
}
百分位的統計也可以指定 percents 參數指定百分位,如下:
GET latency/_search
{
"size": 0,
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"field" : "load_time",
"percents": [60, 80, 95]
}
}
}
}
Percentiles Ranks Aggregation
Percentiles Ranks Aggregation 與 Percentiles Aggregation 統計恰恰相反,就是想看當前數值處在什么范圍內(百分位), 假如你查一下當前值 500 和 600 所處的百分位,發現是 90.01 和 100,那么說明有 90.01 % 的數值都在 500 以內,100 % 的數值在 600 以內。
GET latency/_search
{
"size": 0,
"aggs" : {
"load_time_ranks" : {
"percentile_ranks" : {
"field" : "load_time",
"values" : [500, 600]
}
}
}
}
同樣 load_time 字段必須是數字類型。
返回結果大概類似如下:
{
...
"aggregations": {
"load_time_ranks": {
"values" : {
"500.0": 90.01,
"600.0": 100.0
}
}
}
}
可以設置 keyed 參數為 true,將對應的 values 作為桶 key 一起返回,默認是 false。
GET latency/_search
{
"size": 0,
"aggs": {
"load_time_ranks": {
"percentile_ranks": {
"field": "load_time",
"values": [500, 600],
"keyed": true
}
}
}
}
返回結果如下:
{
...
"aggregations": {
"load_time_ranks": {
"values": [
{
"key": 500.0,
"value": 90.01
},
{
"key": 600.0,
"value": 100.0
}
]
}
}
}
桶聚合
bucket 可以理解為一個桶,它會遍歷文檔中的內容,凡是符合某一要求的就放入一個桶中,分桶相當於 SQL 中的 group by。從另外一個角度,可以將指標聚合看成單桶聚合,即把所有文檔放到一個桶中,而桶聚合是多桶型聚合,它根據相應的條件進行分組。
| 種類 | 描述/場景 |
|---|---|
| 詞項聚合(Terms Aggregation) | 用於分組聚合,讓用戶得知文檔中每個詞項的頻率,它返回每個詞項出現的次數。 |
| 差異詞項聚合(Significant Terms Aggregation) | 它會返回某個詞項在整個索引中和在查詢結果中的詞頻差異,這有助於我們發現搜索場景中有意義的詞。 |
| 過濾器聚合(Filter Aggregation) | 指定過濾器匹配的所有文檔到單個桶(bucket),通常這將用於將當前聚合上下文縮小到一組特定的文檔。 |
| 多過濾器聚合(Filters Aggregation) | 指定多個過濾器匹配所有文檔到多個桶(bucket)。 |
| 范圍聚合(Range Aggregation) | 范圍聚合,用於反映數據的分布情況。 |
| 日期范圍聚合(Date Range Aggregation) | 專門用於日期類型的范圍聚合。 |
| IP 范圍聚合(IP Range Aggregation) | 用於對 IP 類型數據范圍聚合。 |
| 直方圖聚合(Histogram Aggregation) | 可能是數值,或者日期型,和范圍聚集類似。 |
| 時間直方圖聚合(Date Histogram Aggregation) | 時間直方圖聚合,常用於按照日期對文檔進行統計並繪制條形圖。 |
| 空值聚合(Missing Aggregation) | 空值聚合,可以把文檔集中所有缺失字段的文檔分到一個桶中。 |
| 地理點范圍聚合(Geo Distance Aggregation) | 用於對地理點(geo point)做范圍統計。 |
Terms Aggregation
Terms Aggregation 用於詞項的分組聚合。最為經典的用例是獲取 X 中最頻繁(top frequent)的項目,其中 X 是文檔中的某個字段,如用戶的名稱、標簽或分類。由於 terms 聚集統計的是每個詞條,而不是整個字段值,因此通常需要在一個非分析型的字段上運行這種聚集。原因是, 你期望“big data”作為詞組統計,而不是“big”單獨統計一次,“data”再單獨統計一次。
用戶可以使用 terms 聚集,從分析型字段(如內容)中抽取最為頻繁的詞條。還可以使用這種信息來生成一個單詞雲。
{
"aggs": {
"profit_terms": {
"terms": { // terms 聚合 關鍵字
"field": "profit",
......
}
}
}
}
在 terms 分桶的基礎上,還可以對每個桶進行指標統計,也可以基於一些指標或字段值進行排序。示例如下:
{
"aggs": {
"item_terms": {
"terms": {
"field": "item_id",
"size": 1000,
"order":[{
"gmv_stat": "desc"
},{
"gmv_180d": "desc"
}]
},
"aggs": {
"gmv_stat": {
"sum": {
"field": "gmv"
}
},
"gmv_180d": {
"sum": {
"script": "doc['gmv_90d'].value*2"
}
}
}
}
}
}
返回的結果如下:
{
...
"aggregations": {
"hospital_id_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 260,
"buckets": [
{
"key": 23388,
"doc_count": 18,
"gmv_stat": {
"value": 176220
},
"gmv_180d": {
"value": 89732
}
},
{
"key": 96117,
"doc_count": 16,
"gmv_stat": {
"value": 129306
},
"gmv_180d": {
"value": 56988
}
},
...
]
}
}
}
默認情況下返回按文檔計數從高到低的前 10 個分組,可以通過 size 參數指定返回的分組數。
Filter Aggregation
Filter Aggregation 是過濾器聚合,可以把符合過濾器中的條件的文檔分到一個桶中,即是單分組聚合。
{
"aggs": {
"age_terms": {
"filter": {"match":{"gender":"F"}},
"aggs": {
"avg_age": {
"avg": {
"field": "age"
}
}
}
}
}
}
Filters Aggregation
Filters Aggregation 是多過濾器聚合,可以把符合多個過濾條件的文檔分到不同的桶中,即每個分組關聯一個過濾條件,並收集所有滿足自身過濾條件的文檔。
{
"size": 0,
"aggs": {
"messages": {
"filters": {
"filters": {
"errors": { "match": { "body": "error" } },
"warnings": { "match": { "body": "warning" } }
}
}
}
}
}
在這個例子里,我們分析日志信息。聚合會創建兩個關於日志數據的分組,一個收集包含錯誤信息的文檔,另一個收集包含告警信息的文檔。而且每個分組會按月份划分。
{
...
"aggregations": {
"messages": {
"buckets": {
"errors": {
"doc_count": 1
},
"warnings": {
"doc_count": 2
}
}
}
}
}
Range Aggregation
Range Aggregation 范圍聚合是一個基於多組值來源的聚合,可以讓用戶定義一系列范圍,每個范圍代表一個分組。在聚合執行的過程中,從每個文檔提取出來的值都會檢查每個分組的范圍,並且使相關的文檔落入分組中。注意,范圍聚合的每個范圍內包含 from 值但是排除 to 值。
{
"aggs": {
"age_range": {
"range": {
"field": "age",
"ranges": [{
"to": 25
},
{
"from": 25,
"to": 35
},
{
"from": 35
}]
},
"aggs": {
"bmax": {
"max": {
"field": "balance"
}
}
}
}
}
}
}
返回結果如下:
{
...
"aggregations": {
"age_range": {
"buckets": [{
"key": "*-25.0",
"to": 25,
"doc_count": 225,
"bmax": {
"value": 49587
}
},
{
"key": "25.0-35.0",
"from": 25,
"to": 35,
"doc_count": 485,
"bmax": {
"value": 49795
}
},
{
"key": "35.0-*",
"from": 35,
"doc_count": 290,
"bmax": {
"value": 49989
}
}]
}
}
}
