6.1 聚合框架介绍 6.1 Elasticsearch 聚合框架介绍 Elasticsearch 的聚合框架 (Aggregations Framework) 提供了一种强大的方式来对搜索结果进行分组和计算统计信息。它允许你从大量数据中提取有意义的洞察,而无需编写复杂的查询或脚本。聚合可以帮助你回答诸如“最畅销的产品是什么?”、“平均订单金额是多少?”、“每个地区的客户数量是多少?”等问题。 6.1.1 聚合的概念 聚合本质上是一个数据汇总的过程,它将匹配查询的文档集合分组,并对每个组执行计算。这些计算可以包括计数、求和、平均值、最小值、最大值、百分位数等。 可以把聚合想象成 SQL 中的 语句,但它远不止于此。
Elasticsearch 的聚合框架 (Aggregations Framework) 提供了一种强大的方式来对搜索结果进行分组和计算统计信息。它允许你从大量数据中提取有意义的洞察,而无需编写复杂的查询或脚本。聚合可以帮助你回答诸如“最畅销的产品是什么?”、“平均订单金额是多少?”、“每个地区的客户数量是多少?”等问题。
聚合本质上是一个数据汇总的过程,它将匹配查询的文档集合分组,并对每个组执行计算。这些计算可以包括计数、求和、平均值、最小值、最大值、百分位数等。
可以把聚合想象成 SQL 中的 GROUP BY 语句,但它远不止于此。Elasticsearch 聚合框架提供了更丰富的功能,可以嵌套聚合,创建复杂的分析管道。
一个聚合请求通常包含以下几个部分:
查询 (Query):可选的,用于过滤需要进行聚合的数据。如果没有查询,则聚合将应用于索引中的所有文档。
聚合 (Aggregation):定义要执行的聚合操作。一个请求可以包含多个聚合。
子聚合 (Sub-aggregation):聚合可以嵌套,允许你对分组后的数据再次进行聚合。
用 Mermaid 图表示如下:
Elasticsearch 提供了多种聚合类型,可以大致分为以下几类:
指标聚合 (Metric Aggregations):计算文档集合的统计指标,例如平均值、最小值、最大值、总和、标准差等。
桶聚合 (Bucket Aggregations):将文档分组到不同的桶中,每个桶代表一个文档子集。
管道聚合 (Pipeline Aggregations):基于其他聚合的结果进行计算,例如计算移动平均值或百分比变化。
矩阵聚合 (Matrix Aggregations):生成矩阵形式的结果,例如计算文档之间的相关性。
为了演示聚合框架的使用,我们假设有一个包含产品销售数据的索引,其中包含以下字段:
product_name:产品名称
category:产品类别
price:产品价格
quantity_sold:销售数量
region:销售地区
首先,我们需要创建并索引一些示例数据:
PUT /sales_data { "mappings": { "properties": { "product_name": { "type": "keyword" }, "category": { "type": "keyword" }, "price": { "type": "float" }, "quantity_sold": { "type": "integer" }, "region": { "type": "keyword" } } } } POST /sales_data/_doc { "product_name": "Laptop", "category": "Electronics", "price": 1200.0, "quantity_sold": 10, "region": "North America" } POST /sales_data/_doc { "product_name": "Smartphone", "category": "Electronics", "price": 800.0, "quantity_sold": 15, "region": "North America" } POST /sales_data/_doc { "product_name": "T-Shirt", "category": "Clothing", "price": 25.0, "quantity_sold": 50, "region": "Europe" } POST /sales_data/_doc { "product_name": "Jeans", "category": "Clothing", "price": 75.0, "quantity_sold": 30, "region": "Europe" } POST /sales_data/_doc { "product_name": "Coffee Maker", "category": "Home Appliances", "price": 50.0, "quantity_sold": 20, "region": "Asia" } POST /sales_data/_doc { "product_name": "Blender", "category": "Home Appliances", "price": 40.0, "quantity_sold": 25, "region": "Asia" }
现在,我们可以使用聚合来分析这些数据。
我们可以使用 sum 聚合来计算所有产品的总销售额:
GET /sales_data/_search { "size": 0, // 不需要返回原始文档 "aggs": { "total_revenue": { "sum": { "field": "price" } } } }
代码解释:
size: 0:指定我们不希望返回任何原始文档,只关注聚合结果。
aggs:定义聚合操作。
total_revenue:聚合的名称,可以自定义。
sum:聚合类型,计算指定字段的总和。
field: "price":指定要计算总和的字段。
预期结果:
{ "took": 1, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 6, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "total_revenue": { "value": 2390.0 } } }
结果显示总销售额为 2390.0。
我们可以使用 terms 聚合将文档按类别分组,然后使用 avg 聚合计算每个类别的平均价格:
GET /sales_data/_search { "size": 0, "aggs": { "categories": { "terms": { "field": "category" }, "aggs": { "average_price": { "avg": { "field": "price" } } } } } }
代码解释:
categories:顶层聚合的名称。
terms:桶聚合类型,用于按指定字段的值进行分组。
field: "category":指定用于分组的字段。
average_price:子聚合的名称。
avg:指标聚合类型,计算指定字段的平均值。
预期结果:
{ "took": 1, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 6, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "categories": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "Electronics", "doc_count": 2, "average_price": { "value": 1000.0 } }, { "key": "Clothing", "doc_count": 2, "average_price": { "value": 50.0 } }, { "key": "Home Appliances", "doc_count": 2, "average_price": { "value": 45.0 } } ] } } }
结果显示每个类别的平均价格。例如,电子产品的平均价格为 1000.0。
这个例子将展示更复杂的嵌套聚合。
GET /sales_data/_search { "size": 0, "aggs": { "regions": { "terms": { "field": "region" }, "aggs": { "filtered_products": { "filter": { "range": { "price": { "gte": 1000 } } }, "aggs": { "product_count": { "cardinality": { "field": "product_name" } } } } } } } }
代码解释:
regions:顶层聚合,按地区分组。
terms:按地区字段进行分组。
filtered_products:子聚合,使用 filter 聚合过滤价格大于等于 1000 的产品。
range: 设置价格范围,gte表示大于等于1000
product_count:嵌套在 filtered_products 中的聚合,使用 cardinality 聚合计算不同产品的数量。 cardinality 用于计算近似的不同值数量,适用于大数据集。
预期结果:
{ "took": 2, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 6, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "regions": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "North America", "doc_count": 2, "filtered_products": { "doc_count": 1, "product_count": { "value": 1 } } }, { "key": "Europe", "doc_count": 2, "filtered_products": { "doc_count": 0, "product_count": { "value": 0 } } }, { "key": "Asia", "doc_count": 2, "filtered_products": { "doc_count": 0, "product_count": { "value": 0 } } } ] } } }
结果显示北美地区销售额超过 1000 的产品数量为 1,欧洲和亚洲为 0。
terms 聚合: 最常用的桶聚合之一,用于按指定字段的值进行分组。 可以指定 size 参数来限制返回的桶的数量。
range 聚合: 用于按数值范围进行分组。 可以指定多个范围。
date_range 聚合: 用于按日期范围进行分组。 类似于 range 聚合,但适用于日期字段。
histogram 聚合: 用于按数值的直方图进行分组。 可以指定 interval 参数来定义直方图的间隔。
date_histogram 聚合: 用于按日期的直方图进行分组。 类似于 histogram 聚合,但适用于日期字段。
avg 聚合: 计算指定字段的平均值。
sum 聚合: 计算指定字段的总和。
min 聚合: 计算指定字段的最小值。
max 聚合: 计算指定字段的最大值。
stats 聚合: 一次性计算平均值、最小值、最大值、总和和计数。
extended_stats 聚合: 在 stats 聚合的基础上,还计算标准差、方差等。
percentiles 聚合: 计算指定字段的百分位数。
cardinality 聚合: 计算不同值的近似数量。
Elasticsearch 按照以下顺序执行聚合:
查询: 首先执行查询,过滤出需要进行聚合的文档。
桶聚合: 然后执行桶聚合,将文档分组到不同的桶中。
指标聚合: 最后对每个桶执行指标聚合,计算统计信息。
Elasticsearch 的聚合框架是一个强大的工具,可以帮助你从大量数据中提取有价值的洞察。通过组合不同的聚合类型和嵌套聚合,你可以创建复杂的分析管道,回答各种业务问题。 熟练掌握聚合框架是 Elasticsearch 数据分析的关键。 通过上述代码实践和内容详解,相信你对 Elasticsearch 的聚合框架有了更深入的理解。