Elasticsearch DSL 中的 aggs 聚合
一、聚合的概述
Elasticsearch 的 聚合(Aggregations) 功能用于对数据进行多维分析和统计,支持从简单的指标计算到复杂的分组分析。以下是聚合的基本结构:
{
"aggs": { // 也可以使用"aggregations"
"agg_name": { // 自定义聚合名称
"agg_type": { // 聚合类型
"agg_body": ... // 聚合体
}
}
}
}
二、聚合的核心类型
Elasticsearch 的聚合分为三大类:
类型 | 功能 | 典型应用 |
---|---|---|
指标聚合(Metric) | 计算数值型字段的统计值(如平均值、最大值等) | 平均价格、总销售额、最大值/最小值 |
桶聚合(Bucket) | 将文档分组到不同的“桶”中进行统计 | 按类别分组、按时间区间分桶、按范围分桶 |
管道聚合(Pipeline) | 对已有聚合结果进行二次计算 | 移动平均值、累计求和、差值计算 |
三、指标聚合
常见指标聚合有如下:
- avg:计算字段的平均值。
- sum:计算字段的总和。
- max/min:获取字段的最大值/最小值。
- stats:一次性返回 count, min, max, avg, sum。
- cardinality:统计字段的唯一值数量(近似去重)。
- percentiles :计算数值字段中指定百分位数的值。
- percentile_ranks:计算给定值在数据集中所处的百分位位置。
- top_hits:聚合结果中返回每个分组中最相关或最匹配的文档。
3.1 avg - 平均值
{
"aggs": {
"avg_price": {
"avg": { "field": "price" }
}
}
}
3.2 sum - 求和
{
"aggs": {
"total_sales": {
"sum": { "field": "sales" }
}
}
}
3.3 max/min - 最大/最小值
{
"aggs": {
"max_age": {
"max": { "field": "age" }
}
}
}
3.4 stats - 基本统计
{
"aggs": {
"price_stats": {
"stats": { "field": "price" }
}
}
}
3.5 cardinality - 基数统计(去重计数)
{
"aggs": {
"unique_users": {
"cardinality": { "field": "user_id" }
}
}
}
3.6 percentiles - 百分位数
{
"aggs": {
"load_time_percentiles": {
"percentiles": {
"field": "load_time",
"percents": [95, 99, 99.9]
}
}
}
}
3.7 percentile_ranks - 百分位排名
{
"aggs": {
"load_time_ranks": {
"percentile_ranks": {
"field": "load_time",
"values": [500, 1000]
}
}
}
}
3.8 top_hits - 返回每组顶部文档
{
"aggs": {
"top_tags": {
"terms": { "field": "tags" },
"aggs": {
"top_tag_hits": {
"top_hits": {
"size": 1,
"sort": [{ "date": { "order": "desc" } }]
}
}
}
}
}
}
四、桶聚合
常见桶聚合有如下:
- terms:按字段值分组(类似 SQL 的 GROUP BY)。
- date_histogram:按时间区间分桶(如按天、月统计)。
- range:按数值范围分桶(如价格区间)。
- histogram:按固定间隔分桶(如每 100 为一个区间)。
- nested:处理嵌套对象的聚合。
4.1 terms - 按词项分组
{
"aggs": {
"genres": {
"terms": {
"field": "genre",
"size": 10,
"order": { "_count": "desc" }
}
}
}
}
4.2 range - 按范围分组
{
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 50 },
{ "from": 50, "to": 100 },
{ "from": 100 }
]
}
}
}
}
4.3 date_range - 日期范围分组
{
"aggs": {
"date_ranges": {
"date_range": {
"field": "date",
"format": "yyyy-MM-dd",
"ranges": [
{ "to": "now-10d/d" },
{ "from": "now-10d/d", "to": "now" },
{ "from": "now" }
]
}
}
}
}
4.4 histogram - 直方图
{
"aggs": {
"prices": {
"histogram": {
"field": "price",
"interval": 50,
"extended_bounds": {
"min": 0,
"max": 500
}
}
}
}
}
4.5 nested - 嵌套对象聚合
{
"aggs": {
"comments": {
"nested": { "path": "comments" },
"aggs": {
"by_user": {
"terms": { "field": "comments.user" }
}
}
}
}
}
五、管道聚合
常见管道聚合有如下:
- avg_bucket:计算多个桶的平均值。
- cumulative_sum:累计求和。
- derivative:计算相邻桶的差值(如环比增长)。
- moving_avg:计算移动平均值。
5.1 avg_bucket - 计算桶平均值
{
"aggs": {
"sales_per_month": {
"date_histogram": {
"field": "date",
"calendar_interval": "month"
},
"aggs": {
"sales": { "sum": { "field": "price" } }
}
},
"avg_monthly_sales": {
"avg_bucket": {
"buckets_path": "sales_per_month>sales"
}
}
}
}
5.2 cumulative_sum - 累计和
{
"aggs": {
"sales_per_month": {
"date_histogram": {
"field": "date",
"calendar_interval": "month"
},
"aggs": {
"sales": { "sum": { "field": "price" } },
"cumulative_sales": {
"cumulative_sum": { "buckets_path": "sales" }
}
}
}
}
}
5.3 derivative - 计算导数
{
"aggs": {
"sales_per_month": {
"date_histogram": {
"field": "date",
"calendar_interval": "month"
},
"aggs": {
"sales": { "sum": { "field": "price" } },
"sales_deriv": {
"derivative": { "buckets_path": "sales" }
}
}
}
}
}
5.4 moving_avg - 移动平均
{
"aggs": {
"sales_per_month": {
"date_histogram": {
"field": "date",
"calendar_interval": "month"
},
"aggs": {
"sales": { "sum": { "field": "price" } },
"moving_avg": {
"moving_avg": { "buckets_path": "sales" }
}
}
}
}
}
六、聚合参数优化
-
控制返回桶数量
- size:指定返回的桶数量(默认10)。
"terms": { "field": "category.keyword", "size": 20 }
- size:指定返回的桶数量(默认10)。
-
过滤空桶
- min_doc_count:过滤文档数不足的桶。
"terms": { "field": "category.keyword", "min_doc_count": 5 }
- min_doc_count:过滤文档数不足的桶。
-
排序桶
- order:按子聚合结果或文档数排序。
"terms": { "field": "category.keyword", "order": { "avg_price": "desc" } // 按平均价格降序 }
- order:按子聚合结果或文档数排序。
七、性能优化建议
- 使用 keyword 类型字段分桶:避免对 text 字段直接聚合(需开启 fielddata,性能差)。
- 限制聚合范围:结合查询条件减少待聚合数据量。
- 避免深度分桶:高基数(唯一值多)字段分桶可能导致内存问题。
- 使用近似算法:如 cardinality 聚合的 precision_threshold 参数平衡精度与性能。
- 启用 eager_global_ordinals:对高基数字段预加载优化。
八、完整示例
GET /sales/_search
{
"size": 0, // 不返回原始文档,仅聚合结果
"query": { // 限定只分析 2023 年全年的订单数据(从 2023-01-01 到 2023-12-31)
"range": { "order_date": { "gte": "2023-01-01", "lte": "2023-12-31" } }
},
"aggs": {
"sales_by_category": {
"terms": {
"field": "category.keyword", // 按商品类别(category.keyword)分组
"size": 5, // 返回销售额最高的 5 个类别
"order": { "total_sales": "desc" } // 按每个类别的总销售额(total_sales)降序排列
},
"aggs": {
"total_sales": { "sum": { "field": "sales" } }, // 计算每个类别的销售总额(sum)
"avg_price": { "avg": { "field": "price" } }, // 计算每个类别的商品平均价格(avg)
"top_products": { // 列出每个类别中销量最高的 3 个产品名称
"terms": { "field": "product_name.keyword", "size": 3 }
}
}
},
"monthly_trend": {
"date_histogram": { // 按月(month)统计销售数据
"field": "order_date", // 基于订单日期(order_date)字段
"calendar_interval": "month"
},
"aggs": {
"monthly_sales": { "sum": { "field": "sales" } }, // 计算每月的销售总额(sum)
"sales_growth": { "derivative": { "buckets_path": "monthly_sales" } } // 计算销售额的月度环比增长(derivative)
}
}
}
}
- 预期返回结果
- 按类别统计:
- 销售额最高的 5 个商品类别
- 每个类别的总销售额和平均价格
- 每个类别最畅销的 3 个产品
- 按月趋势统计:
- 2023 年每月的销售总额
- 每月相比上月的销售额变化(增长/下降)
- 按类别统计: