Elasticsearch Bucket聚合(桶聚合) 第一篇(常用桶聚合一覽)

本篇將開始介紹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。

其取值:

  1. BucketOrder.count(boolean asc)
    按匹配文檔格式升序/降序排序。
  2. BucketOrder.key(boolean asc)
    按key的升序或降序排序。
  3. BucketOrder.aggregation
    通過定義一個子聚合進行排序。
  4. 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. 1小時(1h)是指定時區內第一個小時的00:00分鐘到下一個小時的00:00分鐘之間的時間間隔,用來補償其間的任何閏秒,從而使經過該小時的分鐘數和秒數在開始和結束時相同。
  2. {n}h,表示時間間隔,等於 n 60 60 * 1000 毫秒的時間間隔。
  • days (d)
  1. 一天(1d)是在指定的時區內,從一天的開始到第二天的開始的時間間隔。
  2. {n}d,表示時間間隔,等於n 24 60 60 1000毫秒。
  • weeks (w)
  1. 1周(1w)爲開始日:of_week:hour:minute:second與一週的同一天及下一週的時間在指定時區的間隔。
  2. 不支持 {n}w。
  • months (M)
  1. 一個月(1M)是本月開始之間的時間間隔的一天與次月的同一天。
  2. 不支持{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技術內幕》作者。
本文來自 中間件興趣圈,瞭解相關信息可以關注 中間件興趣圈

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章