掌握 ElasticSearch 聚合查詢:Aggregations 入門與實戰
- 一、引言 (Introduction)
- 二、數據準備 (Data Preparation)
- 2.1 創建索引 (Create Index)
- 2.2 批量導入數據 (Bulk Import Data)
- 三、聚合查詢基礎 (Aggregation Basics)
- 3.1 什么是聚合查詢?(What are Aggregations?)
- 3.2 聚合查詢的基本結構 (Basic Aggregation Structure)
- 3.3 聚合類型 (Aggregation Types)
- 四、指標聚合 (Metrics Aggregations)
- 4.1 `avg` (平均值)
- 4.2 `min` (最小值)
- 4.3 `max` (最大值)
- 4.4 `sum` (總和)
- 4.5 `stats` (統計信息)
- 4.6 `value_count` (值計數)
- 4.7 `cardinality` (基數/去重計數)
- 五、桶聚合 (Bucket Aggregations)
- 5.1 `terms` (詞條聚合)
- 5.2 嵌套桶聚合
- 六、管道聚合 (Pipeline Aggregations)
- 6.1 什么是管道聚合?
- 6.2 `min_bucket` (最小桶)
- 6.3 其他管道聚合
- 七、實戰案例 (Practical Examples)
- 案例 1:統計每個產品類別中,價格最高的產品的價格,并按最高價格降序排列
- 案例 2:找出每個月銷售額最高的日期
- 八、總結 (Conclusion)
一、引言 (Introduction)
在信息檢索和數據分析的世界中,我們常常需要做的不僅僅是找到匹配特定關鍵詞的文檔。很多時候,我們需要從海量數據中提取出更深層次的、有價值的洞察。例如:
- 我想知道我的電商網站上所有商品的平均價格是多少?
- 哪個產品類別下的商品數量最多?
- 我的網站上有多少種不同的商品品牌?
- 在過去的一年中,每個月的銷售總額是多少?
- 哪一天的銷售額是最高的?
這些問題都涉及對數據的統計和分析,而不僅僅是簡單的搜索。為了滿足這些需求,Elasticsearch 提供了強大的聚合查詢(Aggregations) 功能。聚合查詢就像一個多功能的瑞士軍刀,或者說是一個強大的數據分析工具箱,它允許你對數據進行各種分組、統計和計算,從而提取出隱藏在數據背后的關鍵信息。
你可以把聚合查詢想象成 SQL 中的 GROUP BY
子句和各種聚合函數(COUNT
, SUM
, AVG
, MIN
, MAX
)的組合,但 Elasticsearch 的聚合功能遠比 SQL 更加靈活和強大。
本文將帶你深入了解 Elasticsearch 7.10 版本中聚合查詢的基礎知識。通過本文,你將學習到:
- 聚合查詢的基本概念和工作原理。
- 三種核心的聚合類型:指標聚合(Metrics Aggregations)、桶聚合(Bucket Aggregations)和管道聚合(Pipeline Aggregations)。
- 如何使用各種指標聚合函數(
avg
,min
,max
,sum
,stats
,value_count
,cardinality
)。 - 如何使用
terms
聚合進行分組。 - 如何使用管道聚合對聚合結果進行二次聚合。
- 通過實戰案例學習如何在實際應用中運用聚合查詢,解決真實的數據分析問題。
二、數據準備 (Data Preparation)
在開始學習聚合查詢之前,我們需要先準備一些示例數據。我們將創建一個名為 product
的索引,并批量導入一些商品數據。
2.1 創建索引 (Create Index)
首先,我們創建一個名為 product
的索引,并定義其 mappings(映射)。Mappings 定義了索引中每個字段的數據類型以及如何進行索引和搜索。
PUT product
{"mappings": {"properties": {"createtime": {"type": "date"},"date": {"type": "date"},"desc": {"type": "text","fields": {"keyword": {"type": "keyword","ignore_above": 256}}},"lv": {"type": "text","fields": {"keyword": {"type": "keyword","ignore_above": 256}}},"name": {"type": "text","analyzer": "ik_max_word","fields": {"keyword": {"type": "keyword","ignore_above": 256}}},"price": {"type": "long"},"tags": {"type": "text","fields": {"keyword": {"type": "keyword","ignore_above": 256}}},"type": {"type": "text","fields": {"keyword": {"type": "keyword","ignore_above": 256}}}}}
}
2.2 批量導入數據 (Bulk Import Data)
接下來,我們使用 Elasticsearch 的 _bulk
API 來批量導入一些商品數據。_bulk
API 可以一次性執行多個索引、更新或刪除操作,效率更高。
POST /product/_bulk
{"index":{"_index": "product"}}
{"name": "小米手機", "desc": "手機中的戰斗機", "price": 3999, "lv": "旗艦機", "type": "手機", "createtime": "2020-10-01T08:00:00Z", "tags": [ "性價比", "發燒", "不卡頓" ]}
{"index":{"_index": "product"}}
{"name": "小米NFC手機", "desc": "支持全功能NFC,手機中的滑翔機", "price": 4999, "lv": "旗艦機", "type": "手機", "createtime": "2020-05-21T08:00:00Z", "tags": [ "性價比", "發燒", "公交卡" ]}
{"index":{"_index": "product"}}
{"name": "NFC手機", "desc": "手機中的轟炸機", "price": 2999, "lv": "高端機", "type": "手機", "createtime": "2020-06-20T08:00:00Z", "tags": [ "性價比", "快充", "門禁卡" ]}
{"index":{"_index": "product"}}
{"name": "小米耳機", "desc": "耳機中的黃燜雞", "price": 999, "lv": "百元機", "type": "耳機", "createtime": "2020-06-23T08:00:00Z", "tags": [ "降噪", "防水", "藍牙" ]}
{"index":{"_index": "product"}}
{"name": "紅米耳機", "desc": "耳機中的肯德基", "price": 399, "type": "耳機", "lv": "百元機", "createtime": "2020-07-20T08:00:00Z", "tags": [ "防火", "低音炮", "聽聲辨位" ]}
{"index":{"_index": "product"}}
{"name": "小米手機10", "desc": "充電賊快掉電更快,超級無敵望遠鏡,高刷電競屏", "price": null, "lv": "旗艦機", "type": "手機", "createtime": "2020-07-27T08:00:00Z", "tags": [ "120HZ刷新率", "120W快充", "120倍變焦" ]}
{"index":{"_index": "product"}}
{"name": "挨炮 SE2", "desc": "除了CPU,一無是處", "price": 3299, "lv": "旗艦機", "type": "手機", "createtime": "2020-07-21T08:00:00Z", "tags": [ "割韭菜", "割韭菜", "割新韭菜" ]}
{"index":{"_index": "product"}}
{"name": "XS Max", "desc": "聽說要出新款12手機了,終于可以換掉手中的4S了", "price": 4399, "lv": "旗艦機", "type": "手機", "createtime": "2020-08-19T08:00:00Z", "tags": [ "5V1A", "4G全網通", "大" ]}
{"index":{"_index": "product"}}
{"name": "小米電視", "desc": "70寸性價比只選,不要一萬八,要不要八千八,只要兩千九百九十八", "price": 2998, "lv": "高端機", "type": "電視", "createtime": "2020-08-16T08:00:00Z", "tags": [ "巨饃", "家庭影院", "游戲" ]}
{"index":{"_index": "product"}}
{"name": "紅米電視", "desc": "我比上邊那個更劃算,我也2998,我也70寸,但是我更好看", "price": 2999, "type": "電視", "lv": "高端機", "createtime": "2020-08-28T08:00:00Z", "tags": [ "大片", "藍光8K", "超薄" ]}
{"index":{"_index": "product"}}
{"name": "紅米電視", "desc": "我比上邊那個更劃算,我也2998,我也70寸,但是我更好看", "price": 2998, "type": "電視", "lv": "高端機", "createtime": "2020-08-28T08:00:00Z", "tags": [ "大片", "藍光8K", "超薄" ]}
代碼解釋:
POST /product/_bulk
: 使用_bulk
API 向product
索引發送批量請求。- 每一行是一個 JSON 對象,表示一個操作。
現在,我們已經準備好了數據,可以開始學習 Elasticsearch 的聚合查詢了!
三、聚合查詢基礎 (Aggregation Basics)
3.1 什么是聚合查詢?(What are Aggregations?)
聚合查詢(Aggregations)是 Elasticsearch 中一種強大的數據分析功能,它允許你對文檔數據進行各種統計分析。與搜索查詢(返回匹配的文檔)不同,聚合查詢返回的是聚合后的統計結果。
你可以將聚合查詢類比為 SQL 中的 GROUP BY
子句和聚合函數(如 COUNT
, SUM
, AVG
, MIN
, MAX
)。 例如,你可以使用 SQL 來計算每個部門的平均工資:
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department;
Elasticsearch 的聚合查詢提供了類似的功能,但更加靈活和強大。它可以處理更復雜的數據結構和分析場景,并且可以進行多層嵌套的聚合。
3.2 聚合查詢的基本結構 (Basic Aggregation Structure)
聚合查詢的基本語法結構如下:
GET /<index>/_search
{"size": 0,"aggs": {"<aggregation_name>": {"<aggregation_type>": {"<aggregation_parameters>"},"aggs": {"<nested_aggregation_name>": {"<nested_aggregation_type>": {"<nested_aggregation_parameters>"}}}}}
}
解釋:
GET /<index>/_search
: 這是 Elasticsearch 的搜索 API,我們在這里使用它來執行聚合查詢。"size": 0
: 這是一個可選參數。通常,在執行聚合查詢時,我們只關心聚合結果,而不關心具體的文檔內容。"size": 0
表示不返回任何文檔,只返回聚合結果。"aggs"
(或"aggregations"
): 這是聚合查詢的頂層關鍵字。所有聚合操作都定義在aggs
對象中。"<aggregation_name>"
: 這是你為聚合操作指定的名稱。這個名稱可以是任意的,它將作為聚合結果的一部分返回,方便你識別和引用。例如,你可以將計算平均價格的聚合命名為"avg_price"
。"<aggregation_type>"
: 這是聚合的類型。Elasticsearch 提供了多種聚合類型,每種類型都有不同的功能。常見的聚合類型包括:avg
(平均值)min
(最小值)max
(最大值)sum
(總和)terms
(詞條聚合)stats
(統計信息)- 等等…
"<aggregation_parameters>"
: 這是特定于聚合類型的參數。不同的聚合類型有不同的參數。例如,avg
聚合需要指定要計算平均值的字段,terms
聚合需要指定要分組的字段。"aggs": { ... }
(在<aggregation_name>
內部): 這是可選的嵌套聚合。你可以在一個聚合操作中嵌套另一個聚合操作,以實現更復雜的分析。例如,你可以先按產品類別分組,然后在每個類別中計算平均價格。
3.3 聚合類型 (Aggregation Types)
Elasticsearch 提供了三種主要的聚合類型:
-
Metrics Aggregations (指標聚合): 這類聚合主要用于計算單個數值指標。例如:
avg
: 計算平均值。min
: 計算最小值。max
: 計算最大值。sum
: 計算總和。stats
: 一次性計算多個統計值(avg
,min
,max
,sum
,count
)。value_count
: 計算非空字段的文檔數量。cardinality
: 計算字段的不同值的數量(去重)。
-
Bucket Aggregations (桶聚合): 這類聚合主要用于將文檔分組到不同的“桶”中。每個桶代表一個分組。例如:
terms
: 按字段值對文檔進行分組。date_histogram
: 按日期范圍對文檔進行分組。range
: 按自定義數值范圍對文檔進行分組。filter
: 根據指定的過濾條件將文檔分到一個桶filters
: 根據指定的多個過濾條件將文檔分到多個桶
-
Pipeline Aggregations (管道聚合): 這類聚合比較特殊,它們不直接操作文檔,而是對其他聚合的結果進行進一步的聚合。例如:
min_bucket
: 找出包含最小值的桶。max_bucket
: 找出包含最大值的桶。avg_bucket
: 計算桶的平均值。sum_bucket
: 計算桶的總和。stats_bucket
: 對桶進行統計分析derivative
: 計算導數(例如,計算銷售額的變化率)。
本文將重點介紹這三種聚合類型的基礎用法。接下來,我們將分別深入探討每種聚合類型,并通過示例演示如何在實際應用中使用它們。
四、指標聚合 (Metrics Aggregations)
指標聚合用于計算單個數值指標,例如平均值、最小值、最大值、總和等。這些指標可以幫助你了解數據的整體特征。
4.1 avg
(平均值)
avg
聚合用于計算指定字段的平均值。
示例: 計算 product
索引中所有產品的平均價格。
GET /product/_search
{"size": 0,"aggs": {"avg_price": {"avg": {"field": "price"}}}
}
代碼解釋:
"size": 0
: 不返回文檔,只返回聚合結果。"aggs"
: 聚合查詢的開始。"avg_price"
: 我們為這個聚合操作指定的名稱。"avg"
: 指定聚合類型為avg
(平均值)。"field": "price"
: 指定要計算平均值的字段為price
。
結果 (部分):
{..."aggregations": {"avg_price": {"value": 3008.8}}
}
結果中的 "value"
字段顯示了所有產品價格的平均值。
4.2 min
(最小值)
min
聚合用于計算指定字段的最小值。
示例: 計算 product
索引中所有產品的最低價格。
GET /product/_search
{"size": 0,"aggs": {"min_price": {"min": {"field": "price"}}}
}
結果 (部分):
{..."aggregations": {"min_price": {"value": 399.0}}
}
4.3 max
(最大值)
max
聚合用于計算指定字段的最大值。
示例: 計算 product
索引中所有產品的最高價格。
GET /product/_search
{"size": 0,"aggs": {"max_price": {"max": {"field": "price"}}}
}
結果 (部分):
{..."aggregations": {"max_price": {"value": 4999.0}}
}
4.4 sum
(總和)
sum
聚合用于計算指定字段的總和。
示例: 計算 product
索引中所有產品的價格總和。
GET /product/_search
{"size": 0,"aggs": {"sum_price": {"sum": {"field": "price"}}}
}
結果 (部分):
{..."aggregations": {"sum_price": {"value": 30088.0}}
}
4.5 stats
(統計信息)
stats
聚合可以一次性計算多個統計值,包括:
count
: 文檔數量。min
: 最小值。max
: 最大值。avg
: 平均值。sum
: 總和。
示例: 獲取 product
索引中所有產品的價格統計信息。
GET /product/_search
{"size": 0,"aggs": {"price_stats": {"stats": {"field": "price"}}}
}
結果 (部分):
{..."aggregations" : {"statistics" : {"count" : 10,"min" : 399.0,"max" : 4999.0,"avg" : 3008.8,"sum" : 30088.0}}
}
結果中一次性返回了count
,min
,max
,avg
和sum
4.6 value_count
(值計數)
value_count
聚合用于計算指定字段的 非空 值的文檔數量。
示例: 計算 product
索引中有多少個文檔具有 price
字段(即有多少個產品有價格信息)。
GET /product/_search
{"size": 0,"aggs": {"price_count": {"value_count": {"field": "price"}}}
}
結果 (部分):
{..."aggregations": {"price_count": {"value": 10}}
}
注意,由于有一個文檔的 price
字段為 null
,因此結果為 10,而不是 11。
4.7 cardinality
(基數/去重計數)
cardinality
聚合用于計算指定字段的不同值的數量(即去重計數)。
示例: 計算 product
索引中有多少種不同的產品等級 (lv
)。
GET /product/_search
{"size": 0,"aggs": {"lv_cardinality": {"cardinality": {"field": "lv.keyword"}}}
}
代碼解釋:
"lv_cardinality"
: 聚合的名稱。"cardinality"
: 指定聚合類型為cardinality
。"field": "lv.keyword"
: 指定要計算基數的字段為lv.keyword
。 這里使用.keyword
子字段是因為我們要對原始的、未分詞的lv
值進行去重計數。
結果 (部分):
{..."aggregations": {"lv_cardinality": {"value": 3}}
}
結果表明,product
索引中有 3 種不同的產品等級。
cardinality
聚合的計算結果是近似的,而不是完全精確的。對于低基數字段(即不同值較少),結果通常是準確的。但對于高基數字段(即不同值非常多),為了提高性能,Elasticsearch 使用了一種稱為 HyperLogLog++ 的算法進行近似計算。你可以通過precision_threshold
參數來控制精度和內存使用之間的權衡。如果需要完全精確的去重計數,并且數據集較小,可以考慮使用terms
聚合,并設置一個足夠大的size
值。但對于大數據集,cardinality
聚合通常是更好的選擇。
五、桶聚合 (Bucket Aggregations)
桶聚合(Bucket Aggregations)用于將文檔分組到不同的“桶”中。每個桶代表一個分組,可以根據不同的條件來創建桶。桶聚合本身不進行統計運算,其主要作用是分組。通常會在桶聚合內嵌套一個或者多個指標聚合,用于統計每個桶內的指標。
5.1 terms
(詞條聚合)
terms
聚合是最常用的桶聚合之一。它根據指定字段的值對文檔進行分組,每個不同的字段值都會創建一個桶。
示例: 按 tags.keyword
字段對 product
索引中的產品進行分組,并統計每個標簽下的文檔數量。
GET /product/_search
{"size": 0,"aggs": {"tag_bucket": {"terms": {"field": "tags.keyword","size": 10,"order": {"_count": "desc"}}}}
}
代碼解釋:
"size": 0
: 不返回文檔,只返回聚合結果。"aggs"
: 聚合查詢的開始。"tag_bucket"
: 我們為這個聚合操作指定的名稱。"terms"
: 指定聚合類型為terms
(詞條聚合)。"field": "tags.keyword"
: 指定要進行分組的字段為tags.keyword
。使用.keyword
子字段是因為我們要基于標簽的原始值進行分組,而不是分詞后的結果。"size": 10
: 指定返回的桶的最大數量。默認情況下,terms
聚合會返回文檔數量最多的前 10 個桶。"order": { "_count": "desc" }
: 指定桶的排序方式。這里按照每個桶中文檔數量 (_count
) 的降序 (desc
) 進行排序。
結果(部分):
{..."aggregations": {"tag_bucket": {"doc_count_error_upper_bound": 0,"sum_other_doc_count": 8,"buckets": [{"key": "性價比","doc_count": 3},{"key": "發燒","doc_count": 2},{"key": "大片","doc_count": 2},{"key": "藍光8K","doc_count": 2},{"key": "超薄","doc_count": 2},{"key": "割韭菜","doc_count": 2},{"key": "120W快充","doc_count": 1},{"key": "120HZ刷新率","doc_count": 1},{"key": "120倍變焦","doc_count": 1},{"key": "4G全網通","doc_count": 1}]}}
}
結果解釋:
"buckets"
: 這是一個數組,包含了根據tags.keyword
字段值分組后的桶。"key"
: 每個桶的鍵,即tags.keyword
字段的值(例如 “性價比”, “發燒”)。"doc_count"
: 每個桶中的文檔數量。"doc_count_error_upper_bound"
:由于terms
聚合默認情況下返回文檔數量最多的前N個桶,這個值表示因為桶數量限制,沒有被統計到的文檔數量的最大可能誤差值。"sum_other_doc_count"
: 由于terms
聚合默認情況下返回文檔數量最多的前N個桶,這個值表示未返回的其他桶中文檔數量的總和。
size
參數:
size
參數控制返回的桶的數量。 如果你想返回所有桶,可以將 size
設置為一個較大的值(例如,大于字段中不同值的數量)。 但是,請注意,如果字段的基數非常高(即有很多不同的值),返回所有桶可能會消耗大量內存。
order
參數:
order
參數控制桶的排序方式。除了按 _count
排序外,還可以:
- 按
_key
排序:"order": { "_key": "asc" }
(按字段值升序) 或"order": { "_key": "desc" }
(按字段值降序)。 - 按桶內指標聚合的結果排序(稍后在嵌套聚合中介紹)。
5.2 嵌套桶聚合
在實際應用中,我們經常需要進行多層級的聚合。例如,我們想先按type
進行分組,然后統計每個type
中price
的平均值。這種情況下我們就需要用到嵌套聚合。
GET /product/_search
{"size": 0,"aggs": {"type_bucket": {"terms": {"field": "type.keyword"},"aggs": {"avg_price": {"avg": {"field": "price"}}}}}
}
代碼解釋:
- 外層是一個
terms
聚合, 根據type.keyword
字段進行分組。 - 內層是一個
avg
聚合, 計算每個分組內price
字段的平均值。 - 內層聚合的結果會作為外層聚合每個桶的一個屬性。
結果(部分):
{..."aggregations": {"type_bucket": {"doc_count_error_upper_bound": 0,"sum_other_doc_count": 0,"buckets": [{"key" : "手機","doc_count" : 6,"avg_price" : {"value" : 3939.0}},{"key" : "耳機","doc_count" : 3,"avg_price" : {"value" : 1465.3333333333333}},{"key" : "電視","doc_count" : 2,"avg_price" : {"value" : 2998.5}}]}}
}
結果解釋:
"buckets"
: 這是一個數組,包含了根據type.keyword
字段值分組后的桶。"key"
: 每個桶的鍵,即type.keyword
字段的值(例如 “手機”, “耳機”)。"doc_count"
: 每個桶中的文檔數量。"avg_price"
: 每個桶中嵌套聚合的結果, 即該類型商品的平均價格。
通過嵌套聚合,我們可以輕松實現多層級的數據分析。我們可以根據需求自由組合不同的聚合類型,構建出非常復雜的聚合查詢。
六、管道聚合 (Pipeline Aggregations)
管道聚合(Pipeline Aggregations)是一種特殊的聚合類型。它們不像指標聚合和桶聚合那樣直接操作文檔,而是對其他聚合的結果進行進一步的聚合。這就像在數據處理流程中添加了一個額外的“管道”,對上游聚合的輸出進行處理。
6.1 什么是管道聚合?
管道聚合的核心思想是:輸入是另一個聚合(或多個聚合)的輸出,而不是文檔本身。這使得我們可以進行諸如以下操作:
- 找出平均價格最低的產品類別。
- 計算每個月銷售額的總和,然后找出銷售額最高的月份。
- 計算某個指標的導數或移動平均值。
管道聚合的關鍵參數是 buckets_path
,它用于指定要作為輸入的聚合的路徑。
6.2 min_bucket
(最小桶)
min_bucket
管道聚合用于找出包含最小值的桶。
示例: 找出平均價格最低的產品分類(基于之前嵌套聚合的結果)。
GET /product/_search
{"size": 0,"aggs": {"type_bucket": {"terms": {"field": "type.keyword"},"aggs": {"avg_price": {"avg": {"field": "price"}}}},"min_avg_price_bucket": {"min_bucket": {"buckets_path": "type_bucket>avg_price"}}}
}
代碼解釋:
"type_bucket"
: 這是一個terms
聚合,按產品類型 (type.keyword
) 分組。"avg_price"
: 這是一個嵌套的avg
聚合,計算每個產品類型的平均價格。"min_avg_price_bucket"
: 這是我們定義的管道聚合的名稱。"min_bucket"
: 指定聚合類型為min_bucket
。"buckets_path": "type_bucket>avg_price"
: 這是buckets_path
參數,它指定了要處理的聚合路徑。type_bucket
: 表示外層的terms
聚合。>
: 表示嵌套關系。avg_price
: 表示內層的avg
聚合。- 所以,
"type_bucket>avg_price"
表示我們要找到type_bucket
聚合中,avg_price
聚合結果最小的那個桶。
結果(部分):
{..."aggregations": {"type_bucket": {... // 省略了 type_bucket 的詳細結果},"min_avg_price_bucket": {"value": 1465.3333333333333,"keys": ["耳機"]}}
}
結果解釋:
"min_avg_price_bucket"
: 管道聚合的結果。"value"
: 最小值 (最低的平均價格)。"keys"
: 取得最小值的桶的key
值數組, 在本例中, 平均價格最低的分類是 “耳機”。
6.3 其他管道聚合
除了 min_bucket
之外,ElasticSearch 還提供了其他幾種管道聚合:
max_bucket
: 找出包含最大值的桶。用法與min_bucket
類似, 只是找出最大值。avg_bucket
: 計算所有桶中某個指標的平均值。sum_bucket
: 計算所有桶中某個指標的總和。stats_bucket
:一次性計算多個統計值, 類似于stats
指標聚合, 但是stats_bucket
是作用于桶聚合的結果之上。derivative
: 計算導數(例如,計算銷售額的變化率)。moving_avg
: 計算移動平均值(例如,計算過去 7 天的平均銷售額)。bucket_script
: 使用腳本對桶的指標進行自定義計算。bucket_selector
: 根據腳本過濾桶。bucket_sort
: 對桶進行排序。
這些管道聚合提供了更高級的數據分析功能。你可以在 ElasticSearch 的官方文檔中找到關于它們的詳細信息。
七、實戰案例 (Practical Examples)
現在,讓我們通過幾個更貼近實際應用的示例,來展示如何組合不同類型的聚合,以解決真實的數據分析問題。
案例 1:統計每個產品類別中,價格最高的產品的價格,并按最高價格降序排列
這個案例結合了 terms
桶聚合、max
指標聚合和排序。
GET product/_search
{"size": 0, "aggs": {"type_bucket": {"terms": {"field": "type.keyword","order": {"max_price": "desc"}},"aggs": {"max_price": {"max": {"field": "price"}}}}}
}
代碼解釋:
"type_buckets"
:terms
聚合,按type.keyword
字段(產品類型)分組。"order": { "max_price": "desc" }
: 按嵌套的max_price
聚合的結果(即每個類別中的最高價格)進行降序排序。"max_price"
: 嵌套的max
聚合,計算每個類別中的最高價格。
結果(部分):
{..."aggregations": {"type_buckets": {..."buckets": [{"key" : "手機","doc_count" : 6,"max_price" : {"value" : 4999.0}},{"key" : "電視","doc_count" : 2,"max_price" : {"value" : 2999.0}},{"key" : "耳機","doc_count" : 3,"max_price" : {"value" : 2998.0}}]}}
}
案例 2:找出每個月銷售額最高的日期
這個案例結合了 date_histogram
桶聚合、sum
指標聚合和 max_bucket
管道聚合。
GET /product/_search
{"size": 0,"aggs": {"sales_per_month": {"date_histogram": {"field": "createtime","calendar_interval": "month"},"aggs": {"sales_per_day": {"date_histogram": {"field": "createtime","calendar_interval": "day"},"aggs": {"daily_sales": {"sum": {"field": "price"}}}},"max_daily_sales": {"max_bucket": {"buckets_path": "sales_per_day>daily_sales"}}}}}
}
代碼解釋:
-
sales_per_month
(外層date_histogram
):- 按月對文檔進行分組(
"calendar_interval": "month"
)。 field
: “createtime”
- 按月對文檔進行分組(
-
sales_per_day
(內層date_histogram
):- 在每個月的桶內,再按天對文檔進行分組(
"calendar_interval": "day"
)。 field
: “createtime”
- 在每個月的桶內,再按天對文檔進行分組(
-
daily_sales
(指標聚合):- 在每個天的桶內,計算當天的銷售總額(
"sum": { "field": "price" }
)。
- 在每個天的桶內,計算當天的銷售總額(
-
max_daily_sales
(管道聚合):- 使用
max_bucket
管道聚合。 "buckets_path": "sales_per_day>daily_sales"
: 找出每個月內,daily_sales
(銷售總額) 最高的那個sales_per_day
(天) 桶。
- 使用
結果 (部分):
{..."aggregations": {"sales_per_month": {"buckets": [{"key_as_string": "2020-05-01T00:00:00.000Z","key": 1588291200000,"doc_count": 1,"sales_per_day": {...},"max_daily_sales": {"value": 4999.0,"keys": ["2020-05-21T00:00:00.000Z"]}},{"key_as_string": "2020-06-01T00:00:00.000Z","key": 1590969600000,"doc_count": 2,"sales_per_day": {...},"max_daily_sales": {"value": 2999.0,"keys": ["2020-06-20T00:00:00.000Z"]}},...]}}
}
結果顯示, 對于每個月, 銷售額最高的那一天的日期和銷售額都被找了出來. 例如, 在2020年5月, 銷售額最高的那一天是2020-05-21, 銷售額是4999.
這兩個案例展示了如何將不同類型的聚合組合起來,以解決更復雜的數據分析問題。 ElasticSearch 聚合的強大之處在于其靈活性和可組合性,你可以根據自己的需求設計出各種各樣的聚合查詢。\
八、總結 (Conclusion)
在本教程中,我們深入探討了 Elasticsearch 7.10 中聚合查詢的基礎知識。聚合查詢是 Elasticsearch 中進行數據分析的強大工具,它能夠幫助你從海量數據中提取出有價值的統計信息和洞察。
我們學習了:
- 聚合查詢的基本概念和結構: 了解了聚合查詢與搜索查詢的區別,以及聚合查詢的基本 JSON 結構。
- 三種核心的聚合類型:
- 指標聚合 (Metrics Aggregations): 用于計算單個數值指標,如平均值、最小值、最大值、總和、去重計數等。
- 桶聚合 (Bucket Aggregations): 用于將文檔分組到不同的桶中,如按字段值分組、按日期范圍分組等。
- 管道聚合 (Pipeline Aggregations): 用于對其他聚合的結果進行進一步的聚合,如找出最大值、最小值、計算導數等。
- 各種常用的聚合函數:
avg
,min
,max
,sum
,stats
,value_count
,cardinality
,terms
,min_bucket
,max_bucket
等。 - 如何使用嵌套聚合: 將不同類型的聚合組合起來,實現更復雜的數據分析。
- 實戰案例: 通過兩個實際案例,展示了如何運用聚合查詢解決真實的數據分析問題。
Elasticsearch 的聚合功能遠不止于此。本文只是一個入門指南,涵蓋了最基礎和最常用的部分。要充分發揮 Elasticsearch 聚合的威力,你需要不斷學習和實踐,探索更高級的聚合類型和用法。
進一步學習的建議:
- 閱讀 Elasticsearch 官方文檔: 官方文檔是學習 ElasticSearch 的最佳資源。關于聚合的詳細文檔,請參考
- 嘗試更多的聚合類型: 除了本文介紹的聚合類型外,ElasticSearch 還提供了許多其他聚合類型,如
date_histogram
、range
、filters
、geo_distance
、percentiles
、top_hits
等等。 - 練習、練習、再練習: 最好的學習方法就是實踐。嘗試使用不同的數據集和不同的聚合組合,來解決各種數據分析問題。
希望本文能幫助你入門 Elasticsearch 聚合查詢。祝你在數據分析的道路上不斷進步!