前言
說完了ES的索引與檢索,接着再介紹一個ES高級功能API – 聚合(Aggregations),聚合功能爲ES注入了統計分析的血統,使用戶在面對大數據提取統計指標時變得遊刃有餘。一樣的工做,你在Hadoop中可能須要寫mapreduce或Hive,在mongo中你必須得用大段的mapreduce腳本,而在ES中僅僅調用一個API就能實現了。html
開始以前,提醒老司機們注意,ES原有的聚合功能Facets在新版本中將被正式被移除,抓緊時間用Aggregations替換Facets吧。Facets真的很慢!android
1 關於Aggregations
Aggregations的部分特性相似於SQL語言中的group by,avg,sum等函數。但Aggregations API還提供了更加複雜的統計分析接口。json
掌握Aggregations須要理解兩個概念:數組
- 桶(Buckets):符合條件的文檔的集合,至關於SQL中的group by。好比,在users表中,按「地區」聚合,一我的將被分到北京桶或上海桶或其餘桶裏;按「性別」聚合,一我的將被分到男桶或女桶
- 指標(Metrics):基於Buckets的基礎上進行統計分析,至關於SQL中的count,avg,sum等。好比,按「地區」聚合,計算每一個地區的人數,平均年齡等
對照一條SQL來加深咱們的理解:緩存
SELECT COUNT(color) FROM table GROUP BY color
GROUP BY至關於作分桶的工做,COUNT是統計指標。app
下面介紹一些經常使用的Aggregations API。curl
2 Metrics
2.1 AVG
2.2 Cardinality
2.3 Stats
2.4 Extended Stats
2.5 Percentiles
2.6 Percentile Ranks函數
3 Bucket
3.1 Filter
3.2 Range
3.3 Missing
3.4 Terms
3.5 Date Range
3.6 Global Aggregation
3.7 Histogram
3.8 Date Histogram
3.9 IPv4 range
3.10 Return only aggregation resultsoop
4 聚合緩存
ES中常用到的聚合結果集能夠被緩存起來,以便更快速的系統響應。這些緩存的結果集和你掠過緩存直接查詢的結果是同樣的。由於,第一次聚合的條件與結果緩存起來後,ES會判斷你後續使用的聚合條件,若是聚合條件不變,而且檢索的數據塊未增更新,ES會自動返回緩存的結果。post
注意聚合結果的緩存只針對size=0的請求(參考3.10章節),還有在聚合請求中使用了動態參數的好比Date Range中的now(參考3.5章節),ES一樣不會緩存結果,由於聚合條件是動態的,即便緩存告終果也沒用了。
先加入幾條index數據,以下:
curl -XPUT 'localhost:9200/testindex/orders/2?pretty' -d '{ "zone_id": "1", "user_id": "100008", "try_deliver_times": 102, "trade_status": "TRADE_FINISHED", "trade_no": "xiaomi.21142736250938334726", "trade_currency": "CNY", "total_fee": 100, "status": "paid", "sdk_user_id": "69272363", "sdk": "xiaomi", "price": 1, "platform": "android", "paid_channel": "unknown", "paid_at": 1427370289, "market": "unknown", "location": "local", "last_try_deliver_at": 1427856948, "is_guest": 0, "id": "fa6044d2fddb15681ea2637335f3ae6b7f8e76fef53bd805108a032cb3eb54cd", "goods_name": "一小堆元寶", "goods_id": "ID_001", "goods_count": "1", "expires_in": 2592000, "delivered_at": 0, "debug_mode": true, "created_at": 1427362509, "cp_result": "exception encountered", "cp_order_id": "cp.order.id.test", "client_id": "9c98152c6b42c7cb3f41b53f18a0d868", "app_user_id": "fvu100006" }'
一、單值聚合
Sum求和,dsl參考以下:
[sfapp@cmos1 ekfile]$ curl 'http://10.202.11.117:9200/testindex/orders/_search?pretty' -d ' > { > "size": 0, > "aggs": { > "return_expires_in": { > "sum": { > "field": "expires_in" > } > } > } > }' { "took" : 3, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "failed" : 0 }, "hits" : { "total" : 2, "max_score" : 0.0, "hits" : [ ] }, "aggregations" : { "return_expires_in" : { "value" : 5184000.0 } } } [sfapp@cmos1 ekfile]$
返回expires_in之和,其中size=0 表示不須要返回參與查詢的文檔。
Min求最小值
[sfapp@cmos1 ekfile]$ curl 'http://10.202.11.117:9200/testindex/orders/_search?pretty' -d ' > { > "size": 0, > "aggs": { > "return_min_expires_in": { > "min": { > "field": "expires_in" > } > } > } > }' { "took" : 3, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "failed" : 0 }, "hits" : { "total" : 2, "max_score" : 0.0, "hits" : [ ] }, "aggregations" : { "return_min_expires_in" : { "value" : 2592000.0 } } } [sfapp@cmos1 ekfile]$
Max求最大值
[sfapp@cmos1 ekfile]$ curl 'http://10.202.11.117:9200/testindex/orders/_search?pretty' -d ' > { > "size": 0, > "aggs": { > "return_max_expires_in": { > "max": { > "field": "expires_in" > } > } > } > }' { "took" : 3, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "failed" : 0 }, "hits" : { "total" : 2, "max_score" : 0.0, "hits" : [ ] }, "aggregations" : { "return_max_expires_in" : { "value" : 2592000.0 } } } [sfapp@cmos1 ekfile]$
AVG求平均值
[sfapp@cmos1 ekfile]$ curl 'http://10.202.11.117:9200/testindex/orders/_search?pretty' -d ' > { > "size": 0, > "aggs": { > "return_avg_expires_in": { > "avg": { > "field": "expires_in" > } > } > } > }' { "took" : 4, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "failed" : 0 }, "hits" : { "total" : 2, "max_score" : 0.0, "hits" : [ ] }, "aggregations" : { "return_avg_expires_in" : { "value" : 2592000.0 } } } [sfapp@cmos1 ekfile]$
Cardinality 求基數(以下示例,查找性別的基數 M、F,共兩個)
{ "size": 0, "aggs": { "return_cardinality": { "cardinality": { "field": "gender" } } } }
結果爲:
二、多值聚合
percentiles 求百分比
查看官方文檔時候,沒看懂,下面是本身測試時的例子,按照性別(F,M)查看工資範圍的百分比
{ "size": 0, "aggs": { "states": { "terms": { "field": "gender" }, "aggs": { "banlances": { "percentile_ranks": { "field": "balance", "values": [ 20000, 40000 ] } } } } }
結果:
stats 統計
查看balance的統計狀況:
{ "size": 0, "aggs": { "balance_stats": { "stats": { "field": "balance" } } } }
返回結果:
extended_stats 擴展統計
{ "size": 0, "aggs": { "balance_stats": { "extended_stats": { "field": "balance" } } } }
結果:
更加複雜的查詢,後續慢慢在實踐中道來。
Terms聚合
記錄有多少F,多少M
{ "size": 0, "aggs": { "genders": { "terms": { "field": "gender" } } } }
返回結果以下:m記錄507條,f記錄493條
數據的不肯定性
使用terms聚合,結果可能帶有必定的誤差與錯誤性。
好比:
咱們想要獲取name字段中出現頻率最高的前5個。
此時,客戶端向ES發送聚合請求,主節點接收到請求後,會向每一個獨立的分片發送該請求。
分片獨立的計算本身分片上的前5個name,而後返回。當全部的分片結果都返回後,在主節點進行結果的合併,再求出頻率最高的前5個,返回給客戶端。
這樣就會形成必定的偏差,好比最後返回的前5箇中,有一個叫A的,有50個文檔;B有49。 可是因爲每一個分片獨立的保存信息,信息的分佈也是不肯定的。 有可能第一個分片中B的信息有2個,可是沒有排到前5,因此沒有在最後合併的結果中出現。 這就致使B的總數少計算了2,原本可能排到第一位,卻排到了A的後面。
size與shard_size
爲了改善上面的問題,就可使用size和shard_size參數。
- size參數規定了最後返回的term個數(默認是10個)
- shard_size參數規定了每一個分片上返回的個數
- 若是shard_size小於size,那麼分片也會按照size指定的個數計算
經過這兩個參數,若是咱們想要返回前5個,size=5;shard_size能夠設置大於5,這樣每一個分片返回的詞條信息就會增多,相應的偏差概率也會減少。
order排序
order指定了最後返回結果的排序方式,默認是按照doc_count排序。
{ "aggs" : { "genders" : { "terms" : { "field" : "gender", "order" : { "_count" : "asc" } } } } }
也能夠按照字典方式排序:
{ "aggs" : { "genders" : { "terms" : { "field" : "gender", "order" : { "_term" : "asc" } } } } }
固然也能夠經過order指定一個單值聚合,來排序。
{ "aggs" : { "genders" : { "terms" : { "field" : "gender", "order" : { "avg_balance" : "desc" } }, "aggs" : { "avg_balance" : { "avg" : { "field" : "balance" } } } } } }
同時也支持多值聚合,不過要指定使用的多值字段:
{ "aggs" : { "genders" : { "terms" : { "field" : "gender", "order" : { "balance_stats.avg" : "desc" } }, "aggs" : { "balance_stats" : { "stats" : { "field" : "balance" } } } } } }
返回結果:
min_doc_count與shard_min_doc_count
聚合的字段可能存在一些頻率很低的詞條,若是這些詞條數目比例很大,那麼就會形成不少沒必要要的計算。
所以能夠經過設置min_doc_count和shard_min_doc_count來規定最小的文檔數目,只有知足這個參數要求的個數的詞條纔會被記錄返回。
經過名字就能夠看出:
- min_doc_count:規定了最終結果的篩選
- shard_min_doc_count:規定了分片中計算返回時的篩選
script
桶聚合也支持腳本的使用:
{ "aggs" : { "genders" : { "terms" : { "script" : "doc['gender'].value" } } } }
以及外部腳本文件:
{ "aggs" : { "genders" : { "terms" : { "script" : { "file": "my_script", "params": { "field": "gender" } } } } } }
filter
filter字段提供了過濾的功能,使用兩種方式:include能夠匹配出包含該值的文檔,exclude則排除包含該值的文檔。
例如:
{
"aggs" : { "tags" : { "terms" : { "field" : "tags", "include" : ".*sport.*", "exclude" : "water_.*" } } } }
上面的例子中,最後的結果應該包含sport而且不包含water。
也支持數組的方式,定義包含與排除的信息:
{
"aggs" : { "JapaneseCars" : { "terms" : { "field" : "make", "include" : ["mazda", "honda"] } }, "ActiveCarManufacturers" : { "terms" : { "field" : "make", "exclude" : ["rover", "jensen"] } } } }
多字段聚合
一般狀況,terms聚合都是僅針對於一個字段的聚合。由於該聚合是須要把詞條放入一個哈希表中,若是多個字段就會形成n^2的內存消耗。
不過,對於多字段,ES也提供了下面兩種方式:
- 1 使用腳本合併字段
- 2 使用copy_to方法,合併兩個字段,建立出一個新的字段,對新字段執行單個字段的聚合。
collect模式
對於子聚合的計算,有兩種方式:
- depth_first 直接進行子聚合的計算
- breadth_first 先計算出當前聚合的結果,針對這個結果在對子聚合進行計算。
默認狀況下ES會使用深度優先,不過能夠手動設置成廣度優先,好比:
{
"aggs" : { "actors" : { "terms" : { "field" : "actors", "size" : 10, "collect_mode" : "breadth_first" }, "aggs" : { "costars" : { "terms" : { "field" : "actors", "size" : 5 } } } } } }
缺省值Missing value
缺省值指定了缺省的字段的處理方式:
{
"aggs" : { "tags" : { "terms" : { "field" : "tags", "missing": "N/A" } } } }