6.3 Bucket Aggregations (桶聚合)


文档摘要

6.3 Bucket Aggregations (桶聚合) Elasticsearch 数据分析:深入探索桶聚合 (Bucket Aggregations) 1. 什么是桶聚合 (Bucket Aggregations)? 简单来说,桶聚合是一种将文档数据分组的机制。它根据文档中字段的值或其他条件,将文档分配到不同的桶中。每个桶都代表一个特定的数据子集,我们可以对每个桶内的文档进行进一步的分析,例如计算数量、求平均值、查找最大最小值等等。 与指标聚合 (Metric Aggregations) 专注于计算数值指标不同,桶聚合的核心在于分组。它为我们提供了一种从不同维度审视数据的视角,帮助我们理解数据的分布、趋势和模式。

6.3 Bucket Aggregations (桶聚合)

Elasticsearch 数据分析:深入探索桶聚合 (Bucket Aggregations)

1. 什么是桶聚合 (Bucket Aggregations)?

简单来说,桶聚合是一种将文档数据分组的机制。它根据文档中字段的值或其他条件,将文档分配到不同的桶中。每个桶都代表一个特定的数据子集,我们可以对每个桶内的文档进行进一步的分析,例如计算数量、求平均值、查找最大最小值等等。

与指标聚合 (Metric Aggregations) 专注于计算数值指标不同,桶聚合的核心在于分组。它为我们提供了一种从不同维度审视数据的视角,帮助我们理解数据的分布、趋势和模式。

mermaid

上图展示了桶聚合的基本流程:

  1. 文档集合 (Document Set): 这是我们进行聚合分析的原始数据。

  2. 桶聚合 (Bucket Aggregation): 根据预定义的规则,将文档分配到不同的桶中。

  3. 桶 (Buckets): 代表不同的数据分组,例如不同的类别、不同的时间段等。

  4. 桶内的文档 (Documents in Buckets): 每个桶包含符合分组条件的文档子集。

  5. 指标聚合 (Metric Aggregation - Optional): 可以对每个桶内的文档进一步进行指标计算,例如统计每个桶内的文档数量、平均值等。

2. 常见的桶聚合类型

Elasticsearch 提供了多种桶聚合类型,以满足不同的分组需求。以下是一些最常用的桶聚合类型:

  • terms 聚合: 基于字段的值进行分组,例如按照商品类别、用户标签等进行分组。

  • range 聚合: 基于数值范围进行分组,例如按照年龄段、价格区间等进行分组。

  • date_range 聚合: 基于日期范围进行分组,例如按照时间段、季度等进行分组。

  • histogram 聚合: 基于数值的直方图进行分组,将数值划分为固定大小的间隔。

  • date_histogram 聚合: 基于日期的直方图进行分组,将日期划分为固定的时间间隔,例如每天、每周、每月等。

  • filter 聚合: 基于查询过滤器进行分组,将匹配特定查询条件的文档放入一个桶中。

  • filters 聚合: 允许多个过滤器,每个过滤器对应一个桶。

  • missing 聚合: 将缺少指定字段的文档放入一个桶中。

  • nested 聚合: 用于嵌套文档,在嵌套文档内部进行聚合。

  • reverse_nested 聚合: 从嵌套文档返回到父文档进行聚合。

  • significant_terms 聚合: 用于发现文档集合中显著的词项,常用于日志分析、异常检测等场景。

接下来,我们将逐一介绍这些桶聚合类型,并通过代码示例进行详细的讲解。

3. terms 聚合: 基于词项分组

terms 聚合是最常用的桶聚合之一,它基于字段的值进行分组。例如,我们可以使用 terms 聚合来统计每个商品类别的销量、每个用户的订单数量等。

示例场景: 假设我们有一个商品索引 products,包含字段 category (商品类别), price (价格), sales (销量)。我们想要统计每个商品类别的总销量。

Elasticsearch Query:

GET products/_search { "size": 0, "aggs": { "categories": { "terms": { "field": "category" }, "aggs": { "total_sales": { "sum": { "field": "sales" } } } } } }

代码详解:

  • "size": 0: 表示我们只关注聚合结果,不需要返回文档数据。

  • "aggs": { ... }: 定义聚合操作。

  • "categories": { ... }: 定义一个名为 categories 的聚合,可以自定义名称。

  • "terms": { "field": "category" }: 指定使用 terms 聚合,并指定要分组的字段为 category

  • "aggs": { "total_sales": { ... } }: 在 terms 聚合的内部,我们可以嵌套其他的聚合操作。这里我们嵌套了一个 sum 指标聚合,用于计算每个类别下的总销量。

  • "sum": { "field": "sales" }: sum 聚合计算指定字段 sales 的总和。

Elasticsearch Response (示例):

{ "took": 5, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 1000, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "categories": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "Electronics", "doc_count": 300, "total_sales": { "value": 15000 } }, { "key": "Books", "doc_count": 250, "total_sales": { "value": 10000 } }, { "key": "Clothing", "doc_count": 200, "total_sales": { "value": 8000 } }, { "key": "Home Goods", "doc_count": 250, "total_sales": { "value": 12000 } } ] } } }

响应解释:

  • "aggregations": { "categories": { ... } }: 返回了名为 categories 的聚合结果。

  • "buckets": [ ... ]: buckets 数组包含了各个分组的结果。

  • "key": "Electronics": key 字段表示桶的键值,这里是商品类别 "Electronics"。

  • "doc_count": 300: doc_count 表示该类别下文档的数量,即有 300 个商品属于 "Electronics" 类别。

  • "total_sales": { "value": 15000 }: total_sales 是我们嵌套的 sum 聚合的结果,表示 "Electronics" 类别的总销量为 15000。

terms 聚合常用参数:

  • field: 指定要分组的字段。

  • size: 限制返回的桶的数量,默认返回前 10 个桶。

  • order: 指定桶的排序方式,可以按照 _count (文档数量), _term (词项值), 或嵌套聚合的指标进行排序。

  • min_doc_count: 设置桶的最小文档数量,少于该数量的桶将被过滤掉。

  • include / exclude: 使用正则表达式或词项列表来包含或排除特定的词项。

4. range 聚合: 基于数值范围分组

range 聚合允许我们根据数值范围将文档分组到不同的桶中。例如,我们可以按照价格区间、年龄段等进行分组。

示例场景: 继续使用商品索引 products,我们想要统计不同价格区间的商品数量。

Elasticsearch Query:

GET products/_search { "size": 0, "aggs": { "price_ranges": { "range": { "field": "price", "ranges": [ { "to": 50 }, { "from": 50, "to": 100 }, { "from": 100 } ] } } } }

代码详解:

  • "range": { ... }: 指定使用 range 聚合。

  • "field": "price": 指定要进行范围分组的字段为 price

  • "ranges": [ ... ]: 定义价格范围的数组。

    • { "to": 50 }: 表示价格小于 50 的范围 (0 - 50)。

    • { "from": 50, "to": 100 }: 表示价格在 50 到 100 之间的范围 (50 - 100)。

    • { "from": 100 }: 表示价格大于等于 100 的范围 (100 - ∞)。

Elasticsearch Response (示例):

{ "took": 3, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 1000, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "price_ranges": { "buckets": [ { "key": "*-50.0", "to": 50.0, "doc_count": 400 }, { "key": "50.0-100.0", "from": 50.0, "to": 100.0, "doc_count": 350 }, { "key": "100.0-*", "from": 100.0, "doc_count": 250 } ] } } }

响应解释:

  • "buckets": [ ... ]: 返回了价格范围分组的结果。

  • "key": "*-50.0": key 字段表示范围的键值,这里是价格小于 50 的范围。

  • "to": 50.0: to 字段表示范围的上限。

  • "doc_count": 400: doc_count 表示该价格范围内的商品数量为 400。

range 聚合常用参数:

  • field: 指定要进行范围分组的字段。

  • ranges: 定义范围数组,每个范围可以包含 from (下限, 包含) 和 to (上限, 不包含) 字段。可以省略 fromto 表示无限范围。

5. date_range 聚合: 基于日期范围分组

date_range 聚合与 range 聚合类似,但它专门用于日期字段。我们可以根据日期范围进行分组,例如按照时间段、季度、年份等进行分组。

示例场景: 假设我们有一个订单索引 orders,包含字段 order_date (订单日期)。我们想要统计不同时间段的订单数量。

Elasticsearch Query:

GET orders/_search { "size": 0, "aggs": { "order_time_ranges": { "date_range": { "field": "order_date", "format": "yyyy-MM-dd", "ranges": [ { "to": "2023-01-01" }, { "from": "2023-01-01", "to": "2023-04-01" }, { "from": "2023-04-01" } ] } } } }

代码详解:

  • "date_range": { ... }: 指定使用 date_range 聚合。

  • "field": "order_date": 指定要进行日期范围分组的字段为 order_date

  • "format": "yyyy-MM-dd": 指定日期字段的格式,确保 Elasticsearch 能正确解析日期。

  • "ranges": [ ... ]: 定义日期范围的数组,日期可以使用字符串表示。

Elasticsearch Response (示例):

{ "took": 4, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 500, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "order_time_ranges": { "buckets": [ { "key": "*-2023-01-01T00:00:00.000Z", "to_as_string": "2023-01-01T00:00:00.000Z", "to": 1672531200000, "doc_count": 150 }, { "key": "2023-01-01T00:00:00.000Z-2023-04-01T00:00:00.000Z", "from_as_string": "2023-01-01T00:00:00.000Z", "from": 1672531200000, "to_as_string": "2023-04-01T00:00:00.000Z", "to": 1680307200000, "doc_count": 200 }, { "key": "2023-04-01T00:00:00.000Z-*", "from_as_string": "2023-04-01T00:00:00.000Z", "from": 1680307200000, "doc_count": 150 } ] } } }

响应解释:

  • key, from, to, doc_count 的含义与 range 聚合类似。

  • from_as_string, to_as_string: 以字符串形式返回日期范围的边界值。

date_range 聚合常用参数:

  • field: 指定要进行日期范围分组的字段。

  • ranges: 定义日期范围数组,日期可以使用字符串或日期数学表达式表示。

  • format: 指定日期字段的格式。

  • time_zone: 指定时区。

6. histogram 聚合: 数值直方图分组

histogram 聚合将数值字段划分为固定大小的间隔 (interval),并统计每个间隔内的文档数量,从而生成数值的直方图。

示例场景: 继续使用商品索引 products,我们想要生成商品价格的直方图,间隔为 50。

Elasticsearch Query:

GET products/_search { "size": 0, "aggs": { "price_histogram": { "histogram": { "field": "price", "interval": 50 } } } }

代码详解:

  • "histogram": { ... }: 指定使用 histogram 聚合.

  • "field": "price": 指定要进行直方图分组的字段为 price.

  • "interval": 50: 指定直方图的间隔大小为 50。

Elasticsearch Response (示例):

{ "took": 2, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 1000, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "price_histogram": { "buckets": [ { "key": 0.0, "doc_count": 350 }, { "key": 50.0, "doc_count": 300 }, { "key": 100.0, "doc_count": 200 }, { "key": 150.0, "doc_count": 100 }, { "key": 200.0, "doc_count": 50 } ] } } }

响应解释:

  • "buckets": [ ... ]: 返回了直方图分组的结果。

  • "key": 0.0: key 字段表示间隔的起始值,这里是 0。

  • "doc_count": 350: doc_count 表示价格在 [0, 50) 范围内的商品数量为 350。

histogram 聚合常用参数:

  • field: 指定要进行直方图分组的字段。

  • interval: 指定直方图的间隔大小。

  • offset: 设置直方图的偏移量,调整间隔的起始位置。

  • min_doc_count: 设置桶的最小文档数量。

  • extended_bounds: 强制直方图包含指定的最小和最大边界值,即使没有文档落在这些边界值内。

7. date_histogram 聚合: 日期直方图分组

date_histogram 聚合与 histogram 聚合类似,但它专门用于日期字段。我们可以按照固定的时间间隔 (例如每天、每周、每月) 将日期字段分组,生成日期直方图。

示例场景: 继续使用订单索引 orders,我们想要统计每月的订单数量。

Elasticsearch Query:

GET orders/_search { "size": 0, "aggs": { "monthly_orders": { "date_histogram": { "field": "order_date", "calendar_interval": "month" } } } }

代码详解:

  • "date_histogram": { ... }: 指定使用 date_histogram 聚合。

  • "field": "order_date": 指定要进行日期直方图分组的字段为 order_date

  • "calendar_interval": "month": 指定时间间隔为 "month" (月)。也可以使用 "day", "week", "quarter", "year" 等时间单位。

Elasticsearch Response (示例):

{ "took": 3, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 500, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "monthly_orders": { "buckets": [ { "key_as_string": "2023-01-01T00:00:00.000Z", "key": 1672531200000, "doc_count": 100 }, { "key_as_string": "2023-02-01T00:00:00.000Z", "key": 1675209600000, "doc_count": 120 }, { "key_as_string": "2023-03-01T00:00:00.000Z", "key": 1677628800000, "doc_count": 150 }, { "key_as_string": "2023-04-01T00:00:00.000Z", "key": 1680307200000, "doc_count": 130 } ] } } }

响应解释:

  • "buckets": [ ... ]: 返回了日期直方图分组的结果。

  • "key_as_string": "2023-01-01T00:00:00.000Z": key_as_string 以字符串形式返回时间间隔的起始日期。

  • "key": 1672531200000: key 以时间戳 (毫秒) 形式返回时间间隔的起始日期。

  • "doc_count": 100: doc_count 表示 2023 年 1 月份的订单数量为 100。

date_histogram 聚合常用参数:

  • field: 指定要进行日期直方图分组的字段。

  • calendar_interval: 指定日历时间间隔,例如 "year", "quarter", "month", "week", "day"。

  • fixed_interval: 指定固定时间间隔,例如 "1h" (1 小时), "30m" (30 分钟), "1d" (1 天)。

  • offset: 设置时间间隔的偏移量。

  • format: 指定日期格式。

  • time_zone: 指定时区。

  • min_doc_count: 设置桶的最小文档数量。

  • extended_bounds: 强制直方图包含指定的最小和最大日期边界值。

8. filterfilters 聚合: 基于过滤器分组

filter 聚合允许我们根据一个查询过滤器将文档分组到一个桶中。 filters 聚合则允许我们定义多个过滤器,每个过滤器对应一个桶。

示例场景: 假设我们想要统计价格大于 100 的商品数量,以及价格小于等于 100 的商品数量。

Elasticsearch Query (使用 filters 聚合):

GET products/_search { "size": 0, "aggs": { "price_filters": { "filters": { "filters": { "expensive": { "range": { "price": { "gt": 100 } } }, "affordable": { "range": { "price": { "lte": 100 } } } } } } } }

代码详解:

  • "filters": { ... }: 指定使用 filters 聚合。

  • "filters": { ... }: 定义过滤器对象,每个过滤器可以自定义名称 (例如 "expensive", "affordable"),并使用标准的 Elasticsearch 查询 DSL 定义过滤器条件。

  • "expensive": { "range": { "price": { "gt": 100 } } }: 定义名为 "expensive" 的过滤器,条件是价格大于 100。

  • "affordable": { "range": { "price": { "lte": 100 } } }: 定义名为 "affordable" 的过滤器,条件是价格小于等于 100。

Elasticsearch Response (示例):

{ "took": 2, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 1000, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "price_filters": { "buckets": { "expensive": { "doc_count": 250 }, "affordable": { "doc_count": 750 } } } } }

响应解释:

  • "buckets": { ... }: 返回了过滤器分组的结果,每个过滤器名称对应一个桶。

  • "expensive": { "doc_count": 250 }: "expensive" 过滤器对应的桶,包含 250 个文档。

  • "affordable": { "doc_count": 750 }: "affordable" 过滤器对应的桶,包含 750 个文档。

filterfilters 聚合常用参数:

  • filters (for filters aggregation): 定义过滤器对象,每个过滤器可以是查询 DSL 或命名过滤器。

  • filter (for filter aggregation): 直接定义一个查询过滤器。

9. missing 聚合: 缺失字段分组

missing 聚合将缺少指定字段的文档分组到一个桶中。这对于分析数据完整性或处理缺失值非常有用。

示例场景: 假设我们的商品索引 products 中,部分商品缺少 description (商品描述) 字段。我们想要统计缺少描述的商品数量。

Elasticsearch Query:

GET products/_search { "size": 0, "aggs": { "missing_description": { "missing": { "field": "description" } } } }

代码详解:

  • "missing": { ... }: 指定使用 missing 聚合。

  • "field": "description": 指定要检查缺失的字段为 description

Elasticsearch Response (示例):

{ "took": 2, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 1000, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "missing_description": { "doc_count": 50 } } }

响应解释:

  • "missing_description": { "doc_count": 50 }: missing_description 聚合结果,doc_count 表示缺少 description 字段的商品数量为 50。

missing 聚合常用参数:

  • field: 指定要检查缺失的字段。

10. 嵌套聚合 (Nested Aggregations)

桶聚合的强大之处在于可以嵌套使用。我们可以在一个桶聚合内部嵌套另一个桶聚合或指标聚合,进行更深层次的分析。在前面的 terms 聚合示例中,我们已经看到了嵌套指标聚合 (sum) 的用法。桶聚合之间也可以相互嵌套,例如:

  • 先按类别分组,再在每个类别内部按价格区间分组。

  • 先按日期分组,再在每个日期内部按用户来源分组。

嵌套聚合可以构建复杂的多维度分析模型,帮助我们从多个角度理解数据。

总结

桶聚合是 Elasticsearch 聚合分析的核心组件,它提供了强大的文档分组能力,为我们深入挖掘数据价值提供了坚实的基础。本文详细介绍了常见的桶聚合类型,并通过丰富的代码示例和解释,帮助你理解和掌握这些关键技术。 熟练运用桶聚合,你将能够轻松构建强大的数据分析应用,从 Elasticsearch 中提取出更有价值的洞察。 掌握这些桶聚合类型,并灵活运用嵌套聚合,你将能够应对各种复杂的数据分析场景,充分发挥 Elasticsearch 的数据分析能力。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U