概要
Elasticsearch的聚合查詢,跟資料庫的聚合查詢效果是一樣的,我們可以將二者拿來對比學習,如求和、求平均值、求最大最小等等。
基礎概念
bucket
資料分組,一些資料按照某個欄位進行bucket劃分,這個欄位值相同的資料放到一個bucket中。可以理解成Java中的Map<String, List>結構,類似於Mysql中的group by後的查詢結果。
metric:
對一個資料分組執行的統計,比如計算最大值,最小值,平均值等 類似於Mysql中的max(),min(),avg()函式的值,都是在group by後使用的。
案例
我們還是以英文兒歌為案例背景,回顧一下索引結構:
PUT /music
{
"mappings": {
"children": {
"properties": {
"id": {
"type": "keyword"
},
"author_first_name": {
"type": "text",
"analyzer": "english"
},
"author_last_name": {
"type": "text",
"analyzer": "english"
},
"author": {
"type": "text",
"analyzer": "english",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"language": {
"type": "text",
"analyzer": "english",
"fielddata": true
},
"tags": {
"type": "text",
"analyzer": "english"
},
"length": {
"type": "long"
},
"likes": {
"type": "long"
},
"isRelease": {
"type": "boolean"
},
"releaseDate": {
"type": "date"
}
}
}
}
}
複製程式碼
統計目前收錄的哪種語言的歌曲最多
GET /music/children/_search
{
"size": 0,
"aggs": {
"song_qty_by_language": {
"terms": {
"field": "language"
}
}
}
}
複製程式碼
語法解釋:
- size:0 表示只要統計後的結果,原始資料不展現
- aggs:固定語法 ,聚合分析都要宣告aggs
- song_qty_by_language:聚合的名稱,可以隨便寫,建議規範命名
- terms:按什麼欄位進行分組
- field:具體的欄位名稱
響應結果如下:
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 5,
"max_score": 0,
"hits": []
},
"aggregations": {
"song_qty_by_language": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "english",
"doc_count": 5
}
]
}
}
}
複製程式碼
語法解釋:
- hits: 由於請求時設定了size:0,hits就是空的
- aggregations:聚合查詢的結果
- song_qty_by_language:請求時宣告的名稱
- buckets:根據指定欄位查詢後得到的資料分組集合,[]內的是每一個資料分組,其中key為每個bucket的對應指定欄位的值,doc_count為統計的數量。
預設按doc_count降序排序。
按語種統計每種歌曲的平均時長
GET /music/children/_search
{
"size": 0,
"aggs": {
"lang": {
"terms": {
"field": "language"
},
"aggs": {
"length_avg": {
"avg": {
"field": "length"
}
}
}
}
}
}
複製程式碼
這裡演示的是兩層aggs聚合查詢,先按語種統計,得到資料分組,再在資料分組裡算平均時長。
多個aggs巢狀語法也是如此,注意一下aggs程式碼塊的位置即可。
統計最長時長、最短時長等的歌曲
最常用的統計:count,avg,max,min,sum,語法含義與mysql相同。
GET /music/children/_search
{
"size": 0,
"aggs": {
"color": {
"terms": {
"field": "language"
},
"aggs": {
"length_avg": {
"avg": {
"field": "length"
}
},
"length_max": {
"max": {
"field": "length"
}
},
"length_min": {
"min": {
"field": "length"
}
},
"length_sum": {
"sum": {
"field": "length"
}
}
}
}
}
}
複製程式碼
按時長分段統計歌曲平均時長
以30秒為一段,看各段區間的平均值。
histogram語法位置跟terms一樣,作範圍分割槽,搭配interval引數一起使用 interval:30表示分的區間段為[0,30),[30,60),[60,90),[90,120)
段的閉合關係是左開右閉,如果資料在某段區間內沒有,也會返回空的區間。
GET /music/children/_search
{
"size": 0,
"aggs": {
"sales_price_range": {
"histogram": {
"field": "length",
"interval": 30
},
"aggs": {
"length_avg": {
"avg": {
"field": "length"
}
}
}
}
}
}
複製程式碼
這種資料的結果可以用來生成柱狀圖或折線圖。
按上架日期分段統計新歌數量
按月統計
date histogram與histogram語法類似,搭配date interval指定區間間隔 extended_bounds表示最大的時間範圍。
GET /music/children/_search
{
"size": 0,
"aggs": {
"sales": {
"date_histogram": {
"field": "releaseDate",
"interval": "month",
"format": "yyyy-MM-dd",
"min_doc_count": 0,
"extended_bounds": {
"min": "2019-10-01",
"max": "2019-12-31"
}
}
}
}
}
複製程式碼
interval的值可以天、周、月、季度、年等。我們可以延伸一下,比如統計今年每個季度的新發布歌曲的點贊數量
GET /music/children/_search
{
"size": 0,
"aggs": {
"sales": {
"date_histogram": {
"field": "releaseDate",
"interval": "quarter",
"format": "yyyy-MM-dd",
"min_doc_count": 0,
"extended_bounds": {
"min": "2019-01-01",
"max": "2019-12-31"
}
},
"aggs": {
"lang_qty": {
"terms": {
"field": "language"
},
"aggs": {
"like_sum": {
"sum": {
"field": "likes"
}
}
}
},
"total" :{
"sum": {
"field": "likes"
}
}
}
}
}
}
複製程式碼
帶上過濾條件
聚合查詢可以和query搭配使用,相當於mysql中where與group by聯合使用
查詢條件
GET /music/children/_search
{
"size": 0,
"query": {
"match": {
"language": "english"
}
},
"aggs": {
"sales": {
"terms": {
"field": "language"
}
}
}
}
複製程式碼
過濾條件
GET /music/children/_search
{
"size": 0,
"query": {
"constant_score": {
"filter": {
"term": {
"language": "english"
}
}
}
},
"aggs": {
"sales": {
"terms": {
"field": "language"
}
}
}
}
複製程式碼
global bucket查詢
global:就是global bucket,會將所有的資料納入聚合scope,不受前面的query或filter影響。
global bucket適用於同時統計指定條件的資料與全部資料的對比,如我們創造的場景:指定作者的歌與全部歌曲的點贊數量對比。
GET /music/children/_search
{
"size": 0,
"query": {
"match": {
"author": "Jean Ritchie"
}
},
"aggs": {
"likes": {
"sum": {
"field": "likes"
}
},
"all": {
"global": {},
"aggs": {
"all_likes": {
"sum": {
"field": "likes"
}
}
}
}
}
}
複製程式碼
統計近2月,近1月的點贊數
aggs.filter針對是聚合裡的資料
bucket filter:對不同的bucket下的aggs,進行filter
類似於mysql的中having語法
GET /music/children/_search
{
"size": 0,
"aggs": {
"recent_60d": {
"filter": {
"range": {
"releaseDate": {
"gte": "now-60d"
}
}
},
"aggs": {
"recent_60d_likes_sum": {
"sum": {
"field": "likes"
}
}
}
},
"recent_30d": {
"filter": {
"range": {
"releaseDate": {
"gte": "now-30d"
}
}
},
"aggs": {
"recent_30d_likes_sum": {
"avg": {
"field": "likes"
}
}
}
}
}
}
複製程式碼
統計排序
預設按doc_count降序排序,排序規則可以改,order裡面可以指定aggs的別名,如length_avg,類似於mysql的order by cnt asc。
GET /music/children/_search
{
"size": 0,
"aggs": {
"group_by_lang": {
"terms": {
"field": "language",
"order": {
"length_avg": "desc"
}
},
"aggs": {
"length_avg": {
"avg": {
"field": "length"
}
}
}
}
}
}
複製程式碼
小結
本篇主要介紹常用的聚合查詢,均以示例為主,瞭解基本寫法後可以快速閱讀,有不好理解的地方,多與我們熟悉的資料庫查詢SQL作比較,謝謝。
專注Java高併發、分散式架構,更多技術乾貨分享與心得,請關注公眾號:Java架構社群 可以掃左邊二維碼新增好友,邀請你加入Java架構社群微信群共同探討技術