本篇將開始介紹Elasticsearch Bucket聚合(桶聚合)。
Buket Aggregations(桶聚合)不像metrics Aggregations(度量聚合)那樣計算字段上的度量,而是創建文檔桶,每個文件桶有效地定義一個文檔集。除了bucket本身之外,bucket聚合還計算並返回“落入”每個bucket的文檔的數量。
與度量聚合相反,桶聚合可以嵌套子聚合。這些子聚合將爲它們的“父”桶聚合創建的桶進行聚合。
ES Bucket Aggregations對標關係型數據庫的(group by)。
首先我們來介紹桶聚合兩個常用參數intervals、time_zone的含義。
1、Intervals
定義桶的間隔,其可選值如下:
- seconds
1, 5, 10, 30的倍數。 - minutes
1, 5, 10, 30的倍數。 - hours
1, 3, 12的倍數。 - days
1,7的倍數。 - months
1, 3的倍數。 - years
1, 5, 10, 20, 50, 100的倍數。
2、Time Zone
對於日期類型,可以使用time_zone來指定時區,可選值可以是相對ISO 8601 utc的相對值,例如+01:00或-08:00,也可以是時區ID,例如America/Los_Angeles。
3、Histogram Aggregation
直方圖聚合,Date Histogram Aggregation是其特例。
動態將文檔中的值按照特定的間隔構建桶,並計算落在該桶的數量,文檔中的值根據如下函數進行近似匹配:
bucket_key = Math.floor((value - offset) / interval) * interval + offset,
其中interval必須是正小數(包含正整數),offset爲[0,interval)。
主要支持的參數如下:
- keyed
響應結果返回組織方式(數組或對象),具體示例請參考日期類直方圖聚合。 - doc_count
匹配的文檔數量。 - offset 偏移量
更改每個bucket(桶)的開始時間,例如將offset設置爲"10",則上例中返回的一個桶的key爲:[10,30),如果offset設置爲5,則第一個桶的key爲[15,30)。 - order
默認按照key的升序進行排序,可以通過order字段來指定排序,其值爲BucketOrder。
其取值:
- BucketOrder.count(boolean asc)
按匹配文檔格式升序/降序排序。 - BucketOrder.key(boolean asc)
按key的升序或降序排序。 - BucketOrder.aggregation
通過定義一個子聚合進行排序。 - BucketOrder.compound(List< BucketOrder> orders)
創建一個桶排序策略,該策略根據多個條件對桶進行排序。
- min_doc_count
表示只顯示匹配的文檔大於等於min_doc_count的桶。
具體JAVA的示例將在Date Histogram Aggregation中詳細介紹。
4、Date Histogram Aggregation
日期字段直方圖聚合。
4.1 interval 取值
- milliseconds (ms)
毫秒,固定長度,支持倍數,通常使用1000的倍數。 - seconds (s)
秒 - minutes (m)
分鐘。所有的分鐘從00秒開始
1m,表示在指定時區的第一分鐘00s到下一分鐘00s之間的時間段。
{n}m,表示時間間隔,等於n 60 1000 毫秒。
- hours (h)
小時,其分鐘與秒都從00開始。
- 1小時(1h)是指定時區內第一個小時的00:00分鐘到下一個小時的00:00分鐘之間的時間間隔,用來補償其間的任何閏秒,從而使經過該小時的分鐘數和秒數在開始和結束時相同。
- {n}h,表示時間間隔,等於 n 60 60 * 1000 毫秒的時間間隔。
- days (d)
- 一天(1d)是在指定的時區內,從一天的開始到第二天的開始的時間間隔。
- {n}d,表示時間間隔,等於n 24 60 60 1000毫秒。
- weeks (w)
- 1周(1w)爲開始日:of_week:hour:minute:second與一週的同一天及下一週的時間在指定時區的間隔。
- 不支持 {n}w。
- months (M)
- 一個月(1M)是本月開始之間的時間間隔的一天與次月的同一天。
- 不支持{n}M
- quarters (q)
季度,不支持{n}q。 - years (y)
年, 不支持{n}y。
4.2 示例
{
"aggs" : {
"sales_over_time" : {
"date_histogram" : {
"field" : "date",
"interval" : "month"
}
}
}
}
對應的JAVA示例如下:
/**
* 日期直方圖聚合
*/
public static void test_Date_Histogram_Aggregation() {
RestHighLevelClient client = EsClient.getClient();
try {
//構建日期直方圖聚合 時間間隔,示例中按月統計
DateHistogramInterval interval = new DateHistogramInterval("1M");
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("aggregations_index02");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
AggregationBuilder aggregationBuild = AggregationBuilders.dateHistogram("createTime_histogram")
.field("createTime")
.dateHistogramInterval(interval)
// .format("yyyy-MM-dd") // 對key的格式化
;
sourceBuilder.aggregation(aggregationBuild);
sourceBuilder.size(0);
sourceBuilder.query(
QueryBuilders.termQuery("sellerId", 24)
);
searchRequest.source(sourceBuilder);
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(result);
} catch (Throwable e) {
e.printStackTrace();
} finally {
EsClient.close(client);
}
}
對應的返回值:
{
... //省略常規響應
"aggregations":{
"date_histogram#createTime_histogram":{
"buckets":[
"key_as_string":"2015-12-01 00:00:00",
"key":1448928000000,
"doc_count":6
},
{
"key_as_string":"2016-01-01 00:00:00",
"key":1451606400000,
"doc_count":4
}
]
}
}
}
其相應的參數已在上面詳述,在此不重複介紹。
4.3 Date Histogram聚合支持的常用參數
除Histogram Aggregation羅列的參數後,還額外支持如下參數:
- timeZone 時區指定。
- offset 偏移量
更改每個bucket(桶)的開始時間,例如將offset設置爲"1h",則上例中返回的一個桶的開始時間:"2015-12-01 00:00:00",則更改爲"2015-12-01 01:00:00" - format
key格式化,將key使用format格式化後的值設置爲key_as_string字段。 - keyed
返回結果格式化,默認爲false,則buckets返回值爲數組,如果keyed=true,則對應的返回結果如下:
"aggregations":{
"date_histogram#createTime_histogram":{
"buckets":{
"2015-12-01 00:00:00":{
"key_as_string":"2015-12-01 00:00:00",
"key":1448928000000,
"doc_count":6
},
"2016-01-01 00:00:00":{
"key_as_string":"2016-01-01 00:00:00",
"key":1451606400000,
"doc_count":4
}
}
}
}
}
5、Date Range Aggregation
日期範圍聚合,每個範圍定義[from,to),from,to可支持date mesh格式。
其使用示例如下,其他與 Date Histogram類似。
/**
* 日期範圍聚合
*/
public static void test_Date_range_Aggregation() {
RestHighLevelClient client = EsClient.getClient();
try {
//構建日期直方圖聚合 時間間隔,示例中按月統計
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("aggregations_index02");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
AggregationBuilder aggregationBuild = AggregationBuilders.dateRange("createTime_date_range")
.field("createTime")
.format("yyyy-MM-dd")
.addRange("quarter_01", "2016-01", "2016-03")
.addRange("quarter_02", "2016-03", "2016-06")
.addRange("quarter_03", "2016-06", "2016-09")
.addRange("quarter_04", "2016-09", "2016-12")
// .format("yyyy-MM-dd") // 對key的格式化
;
sourceBuilder.aggregation(aggregationBuild);
sourceBuilder.size(0);
sourceBuilder.query(
QueryBuilders.termQuery("sellerId", 24)
);
searchRequest.source(sourceBuilder);
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(result);
} catch (Throwable e) {
e.printStackTrace();
} finally {
EsClient.close(client);
}
}
6、Filter Aggregation
聚合中支持首先根據過濾上下文對所有文檔進行刷選,然後再進行聚合計算,例如:
POST /sales/_search?size=0
{
"aggs" : {
"t_shirts" : {
"filter" : { "term": { "type": "t-shirt" } },
"aggs" : {
"avg_price" : { "avg" : { "field" : "price" } }
}
}
}
}
其對應的JAVA代碼如下:
/**
* 日期範圍聚合
*/
public static void test_filter_Aggregation() {
RestHighLevelClient client = EsClient.getClient();
try {
//構建日期直方圖聚合 時間間隔,示例中按月統計
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("aggregations_index02");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
AggregationBuilder aggregationBuild = AggregationBuilders.filter("t_shirts", QueryBuilders.termQuery("status", "1"))
.subAggregation(AggregationBuilders.avg("avg").field("num"))
;
sourceBuilder.aggregation(aggregationBuild);
sourceBuilder.size(0);
sourceBuilder.query(
QueryBuilders.termQuery("sellerId", 24)
);
searchRequest.source(sourceBuilder);
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(result);
} catch (Throwable e) {
e.printStackTrace();
} finally {
EsClient.close(client);
}
}
其返回結果如下:
{
... //省略
"aggregations":{
"filter#t_shirts":{
"doc_count":2,
"avg#avg":{
"value":1
}
}
}
}
{
... //省略
"aggregations":{
"filter#t_shirts":{
"doc_count":2,
"avg#avg":{
"value":1
}
}
}
}
7、Filters Aggregation
定義一個多桶聚合,其中每個桶與一個過濾器相關聯。每個bucket將收集與其關聯過濾器匹配的所有文檔。
public static void test_filters_aggregation() {
RestHighLevelClient client = EsClient.getClient();
try {
//構建日期直方圖聚合 時間間隔,示例中按月統計
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("aggregations_index02");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
AggregationBuilder aggregationBuild = AggregationBuilders.filters("create_filters",
QueryBuilders.termQuery("status", 1),
QueryBuilders.termQuery("buyerId", 1))
.subAggregation(AggregationBuilders.avg("avg").field("num"))
;
sourceBuilder.aggregation(aggregationBuild);
sourceBuilder.size(0);
sourceBuilder.query(
QueryBuilders.termQuery("sellerId", 24)
);
searchRequest.source(sourceBuilder);
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(result);
} catch (Throwable e) {
e.printStackTrace();
} finally {
EsClient.close(client);
}
}
其返回結果:
{
... // 省略
"aggregations":{
"filters#create_filters":{
"buckets":[
{
"doc_count":2,
"avg#avg":{
"value":1
}
},
{
"doc_count":0,
"avg#avg":{
"value":null
}
}
]
}
}
}
溫馨提示,每一個filter代表一個桶(聚合)。
8、Global Aggregation
全局聚合,會忽略所有的查詢條件,具體從下述例子進行說明:
POST /sales/_search?size=0
{
"query" : {
"match" : { "type" : "t-shirt" }
},
"aggs" : {
"all_products" : {
"global" : {},
"aggs" : {
"avg_price" : { "avg" : { "field" : "price" } }
}
},
"t_shirts": { "avg" : { "field" : "price" } }
}
}
其聚合的文檔集不是匹配該查詢的文檔"query" : {"match" : { "type" : "t-shirt" } },而是針對所有的文檔進行聚合。
對應的JAVA實例如下:
public static void test_global_aggregation() {
RestHighLevelClient client = EsClient.getClient();
try {
//構建日期直方圖聚合 時間間隔,示例中按月統計
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("aggregations_index02");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
AggregationBuilder aggregationBuild = AggregationBuilders.global("all_producers")
.subAggregation(AggregationBuilders
.avg("num_avg_aggregation")
.field("num"))
;
sourceBuilder.aggregation(aggregationBuild);
sourceBuilder.size(0);
sourceBuilder.query(
QueryBuilders.termQuery("sellerId", 24)
);
searchRequest.source(sourceBuilder);
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(result);
} catch (Throwable e) {
e.printStackTrace();
} finally {
EsClient.close(client);
}
}
對應的返回值如下:
{
"took":151,
"timed_out":false,
"_shards":{
"total":5,
"successful":5,
"skipped":0,
"failed":0
},
"hits":{
"total":39, // @1
"max_score":0,
"hits":[
]
},
"aggregations":{
"global#all_producers":{
"doc_count":1286, // @2
"avg#num_avg_aggregation":{
"value":1.3157076205287714
}
}
}
}
結果@1:表示符合查詢條件的總個數。
結構@2:表示參與聚合的文檔數量,等於當前庫中文檔總數。
9、IP Range Aggregation
ip類型特有的範圍聚合,與其他聚合使用類似,就不重複介紹了。
10、Missing Aggregation
統計缺少某個字段的文檔個數。
JAVA示例如下:
AggregationBuilder aggregationBuild = AggregationBuilders.missing("missing_num_count")
.field("num");
11、Range Aggregation
基於多桶值源的聚合,允許用戶定義一組範圍——每個範圍表示一個桶。在聚合過程中,將根據每個bucket範圍和相關/匹配文檔的“bucket”檢查從每個文檔中提取的值。注意,此聚合包含from值,並排除每個範圍的to值。
GET /_search
{
"aggs" : {
"price_ranges" : {
"range" : {
"field" : "price",
"ranges" : [
{ "to" : 100.0 },
{ "from" : 100.0, "to" : 200.0 },
{ "from" : 200.0 }
]
}
}
}
}
對應的JAVA示例如下:
public static void test_range_aggregation() {
RestHighLevelClient client = EsClient.getClient();
try {
//構建日期直方圖聚合 時間間隔,示例中按月統計
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("aggregations_index02");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
AggregationBuilder aggregationBuild = AggregationBuilders.range("num_range_aggregation")
.field("num")
.addRange(0, 5)
.addRange(5,10)
.addUnboundedFrom(10)
;
sourceBuilder.aggregation(aggregationBuild);
sourceBuilder.size(0);
sourceBuilder.query(
QueryBuilders.termQuery("sellerId", 24)
);
searchRequest.source(sourceBuilder);
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(result);
} catch (Throwable e) {
e.printStackTrace();
} finally {
EsClient.close(client);
}
}
其返回結果如下:
{
// 省略
"aggregations":{
"range#num_range_aggregation":{
"buckets":[
{
"key":"0.0-5.0",
"from":0,
"to":5,
"doc_count":38
},
{
"key":"5.0-10.0",
"from":5,
"to":10,
"doc_count":0
},
{
"key":"10.0-*",
"from":10,
"doc_count":1
}
]
}
}
}
Range Aggregations支持嵌套聚合,使用subAggregations來支持嵌套聚合,根據官網示例如下:
GET /_search
{
"aggs" : {
"price_ranges" : {
"range" : { // @1
"field" : "price",
"ranges" : [
{ "to" : 100 },
{ "from" : 100, "to" : 200 },
{ "from" : 200 }
]
},
"aggs" : { // @2
"price_stats" : {
"stats" : { "field" : "price" }
}
}
}
}
}
首先通過@1定義範圍聚合,然後對每個桶中 的文檔再執行子聚合@2,其返回結果如下:
{
...
"aggregations": {
"price_ranges": {
"buckets": [
{
"key": "*-100.0",
"to": 100.0,
"doc_count": 2,
"price_stats": {
"count": 2,
"min": 10.0,
"max": 50.0,
"avg": 30.0,
"sum": 60.0
}
},
{
"key": "100.0-200.0",
"from": 100.0,
"to": 200.0,
"doc_count": 2,
"price_stats": {
"count": 2,
"min": 150.0,
"max": 175.0,
"avg": 162.5,
"sum": 325.0
}
},
{
"key": "200.0-*",
"from": 200.0,
"doc_count": 3,
"price_stats": {
"count": 3,
"min": 200.0,
"max": 200.0,
"avg": 200.0,
"sum": 600.0
}
}
]
}
}
}
本文詳細介紹了ES 桶聚合,並給出JAVA示例,下一篇將重點關注ES桶聚合之term聚合。
原文發佈時間爲:2019-03-10
本文作者:丁威,《RocketMQ技術內幕》作者。
本文來自 中間件興趣圈,瞭解相關信息可以關注 中間件興趣圈。