本文首發於公眾號:Hunter後端
原文連結:es筆記六之聚合操作之指標聚合
聚合操作,在 es 中的聚合可以分為大概四種聚合:
bucket
類似於分類分組,按照某個 key 將符合條件的資料都放到該類別的組中
mertic
計算一組檔案的相關值,比如最大,最小值
matrix
根據多個 key 從檔案中提取值生成矩陣,這個操作不支援指令碼(script)
pipeline
將其他聚合的結果再次聚合輸出
聚合是支援套娃(巢狀)操作的,你可以在聚合的結果上接著進行聚合操作,es 是不限制聚合的深度的。
本篇筆記目錄如下:
指標聚合操作的基本結構大致如下:
GET /bank/_search
{
"size": 0,
"aggs": {
"aggregation_name": {
"agg_name": {
"field": "field_name"
}
}
}
}
其中,aggregation_name 為聚合返回結果的名稱,由我們自己定義,agg_name 為聚合的引數,比如最大值最小值,平均值等,這個我們在下面介紹。
指標聚合
指標聚合是從檔案中提取欄位值出來進行計算得出結果,比如最大最小平均值等。
接下來將詳細介紹各種指標聚合操作。
GET /bank/_search
{
"size": 0,
"aggs": {
"avg_balance": {
"avg": {
"field": "balance"
}
}
}
}
其中,最外層的 aggs 表示是聚合操作,avg_balance 是聚合的名稱,avg 則表示是平均值聚合,裡面的 field 表示聚合的欄位是 balance 欄位
在這裡,如果不新增 size=0,除了會返回我們的聚合結果,還會返回聚合的源資料。
這個操作我們返回的結果如下:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1000,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"avg_balance" : {
"value" : 25714.837
}
}
}
我們聚合的結果在 aggregations 這個 key 下。
指令碼執行
指令碼執行的方式如下:
GET /bank/_search
{
"size": 0,
"aggs": {
"avg_balance": {
"avg": {
"script": {"source": "doc.balance.value"}
}
}
}
}
對結果處理
假設,我們需要對這個平均值結果進行處理,比如我們計算出來的這個值是 2000,我們想要對這個值進行修正,比如乘以 1.2。
當然,這個乘的操作我們可以獲取資料之後在系統裡進行操作,如果是直接在 es 的處理中,我們可以如下實現:
GET /bank/_search
{
"size": 0,
"aggs": {
"avg_corrected_balance": {
"avg": {
"field": "balance",
"script": {
"lang": "painless",
"source": "_value * params.correction",
"params": {"correction": 1.2}
}
}
},
"avg_balance": {
"avg": {
"script": {"source": "doc.balance.value"}
}
}
}
}
在上面的語句中,我們新增了一個 params 欄位,定義了一個 correction 的值,然後返回的結果乘以了這個值。
在這裡,我額外加了一個 avg_balance,是直接用的平均值聚合結果,主要是用來對比這兩個結果。
缺失值補充
有一些情況,我們在匯入資料的時候,可能某條資料的某個欄位是沒有值的,預設情況下他們是會被忽略的,不計入計算的,但是如果想要為其加一個預設值也是可以實現的,這裡我們用到 missing 這個引數來定義:
GET /bank/_search
{
"size": 0,
"aggs": {
"avg_balance": {
"avg": {
"field": "balance",
"missing": 0
}
}
}
}
是對某個欄位進行去重後統計總數,操作如下:
GET /bank/_search
{
"size": 0,
"aggs": {
"age_count": {
"cardinality": {
"field": "age"
}
}
}
}
需要注意的是,這個統計對於 text 欄位屬性是不生效的
有一個聚合統計彙總的引數 stats,可以將一般的聚合值進行彙總後返回,比如總數,最大值,最小值等,使用如下:
GET /bank/_search
{
"size": 0,
"aggs": {
"age_stats": {
"stats": {
"field": "age"
}
}
}
}
可以看到返回的值如下:
{
...
"aggregations" : {
"age_stats" : {
"count" : 1000,
"min" : 20.0,
"max" : 40.0,
"avg" : 30.171,
"sum" : 30171.0
}
}
}
如果還想獲得方差,標準差等資料,可以使用這個引數的擴充套件版 extended_stats,替換聚合的引數 stats 即可。
最大值最小值的關鍵字是 max 和 min,使用範例如下:
GET /bank/_search
{
"size": 0,
"aggs": {
"max_age": {
"max": {"field": "age"}
},
"min_age": {
"min": {"field": "age"}
}
}
}
使用指令碼的方式來實現:
GET /bank/_search
{
"size": 0,
"aggs": {
"max_age": {
"max": {"script": {"source": "doc.age.value"}}
}
}
}
使用 es 進行百分位的統計,用到的關鍵字是 percentiles
使用範例如下:
GET /bank/_search
{
"size": 0,
"aggs": {
"age_percentiles": {
"percentiles": {
"field": "age"
}
}
}
}
會輸出 [1, 5, 25, 75, 95, 99] 的統計數:
{
...
"aggregations" : {
"age_percentiles" : {
"values" : {
"1.0" : 20.0,
"5.0" : 21.0,
"25.0" : 25.0,
"50.0" : 30.8,
"75.0" : 35.0,
"95.0" : 39.0,
"99.0" : 40.0
}
}
}
}
我們也可以指定統計的百分位的數列表,比如我們只想知道 [75, 98, 99, 99.9] 的資料:
GET /bank/_search
{
"size": 0,
"aggs": {
"age_percentiles": {
"percentiles": {
"field": "age",
"percents": [75, 98, 99, 99.9]
}
}
}
}
我們直接使用是返回的百分位-資料的格式,我們也可以使用 {'key': xx, 'value': xx} 來返回一個列表,加上一個引數 keyed=false 即可
GET /bank/_search
{
"size": 0,
"aggs": {
"age_percentiles": {
"percentiles": {
"field": "age",
"keyed": false
}
}
}
}
返回的結果範例如下:
"age_percentiles" : {
"values" : [
...
{
"key" : 75.0,
"value" : 35.0
},
{
"key" : 95.0,
"value" : 39.0
},
{
"key" : 99.0,
"value" : 40.0
}
]
}
}
}
這個是和前面的百分位統計相反的操作。
前面是根據百分位獲取該百分位值,這個引數的作用是根據資料獲取在系統中的百分位,使用範例如下:
GET /bank/_search
{
"size": 0,
"aggs": {
"age_ranks": {
"percentile_ranks": {
"field": "age",
"values": [
30,
35,
40
]
}
}
}
}
對於字串型別的資料,有一個專門的引數來獲取相應的聚合統計值,為 string_stats
對 lastname 欄位的統計範例如下:
GET /bank/_search
{
"size": 0,
"aggs": {
"last_name_stats": {
"string_stats": {"field": "lastname.keyword"}
}
}
}
需要注意,如果我們需要進行統計的欄位如果是 text 欄位,那麼就需要加上 .keyword 來進行統計,如果是欄位屬性是 keyword,就不需要這樣處理。
經過統計返回的資料如下:
...
"aggregations" : {
"last_name_stats" : {
"count" : 1000,
"min_length" : 2,
"max_length" : 11,
"avg_length" : 6.122,
"entropy" : 4.726472133462717
}
}
}
以上資訊包括資料總數,lastname 欄位最長和最短長度,平均長度和熵值
比如我們需要對 bank 這個資料庫的 age 欄位進行 sum 的操作,可以如下操作:
GET /bank/_search
{
"size": 0,
"aggs": {
"age_sum": {
"sum": {"field": "age"}
}
}
}
在前面的每一個聚合操作裡,都可以進行 query 的條件篩選,比如獲取 age=21 的資料的 sum 值:
GET /bank/_search
{
"size": 0,
"query": {"match": {"age": "21"}},
"aggs": {
"age_sum": {
"sum": {"field": "age"}
}
}
}
count 是統計總數,使用範例如下:
GET /bank/_search
{
"size": 0,
"aggs": {
"age_count": {
"value_count": {
"field": "age"
}
}
}
}
top hit 操作是根據條件返回符合條件的前幾條資料,通過 size 控制返回的數量。
我們先來看下下面的這個操作:
GET /bank/_search
{
"size": 0,
"aggs": {
"top_ages": {
"terms": {
"field": "age",
"size": 30
}
}
}
}
這個操作其實就是一個桶聚合,它會在下一篇筆記中介紹,這裡我們直接用一下,它返回欄位為 age,以及它在檔案中的數量:
...
"aggregations" : {
"top_ages" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 31,
"doc_count" : 61
},
{
"key" : 39,
"doc_count" : 60
},
{
"key" : 26,
"doc_count" : 59
},
...
top_hits 的操作是在第一個 aggs 聚合操作條件下,進行再次聚合。
比如我們想要獲取各個 age 的資料中,按照 balance 欄位進行倒序排序的前三個,我們可以如下操作:
GET /bank/_search
{
"size": 0,
"aggs": {
"top_ages": {
"terms": {
"field": "age",
"size": 30
},
"aggs": {
"top_balance_hits": {
"top_hits": {
"size": 3,
"sort": [{"balance": {"order": "desc"}}]
}
}
}
}
}
}
然後在第一次聚合返回的結果中,就會多一個 top_balance_hits 欄位,也就是我們在查詢操作中指定的,其下會有三條按照 balance 欄位倒序返回的資料:
...
"aggregations" : {
"top_ages" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 31,
"doc_count" : 61,
"top_balance_hits" : {
"hits" : {
"total" : {
"value" : 61,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
...
]
},
{
"key" : 39,
"doc_count" : 60,
...
},
{
"key" : 26,
"doc_count" : 59,
...
},
...
如果想獲取更多相關文章,可掃碼關注閱讀: