Elasticsearch 聚合操作(Aggregations)详解:从基础到高级分析
聚合操作是 Elasticsearch 中用于数据分析的核心功能,能够对查询结果进行统计、分组、计算等汇总操作,类似 SQL 中的 GROUP BY
、SUM
、AVG
等,但功能更强大、更灵活。根据操作方式和用途,聚合可分为四大类:分组聚合(Bucketing)、度量聚合(Metric)、矩阵聚合(Matrix) 和 管道聚合(Pipeline)。
聚合操作基础语法
Elasticsearch 聚合通过 aggs
(或全称 aggregations
)关键字定义,基本结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "aggs": { "<聚合名称>": { "<聚合类型>": { <聚合参数> // 聚合的具体配置(如字段、间隔等) }, "aggs": { // 子聚合(可选,基于当前聚合结果进一步分析) "<子聚合名称>": { ... } } } }, "size": 0 // 可选,设置为0仅返回聚合结果,不返回原始文档 }
|
- 核心特点:支持多层嵌套(子聚合),可基于分组结果再进行度量,或基于度量结果再进行计算。
- 适用场景:统计分析(如销售额总和)、分组统计(如按类别统计销量)、趋势分析(如按日统计访问量)等。
度量聚合(Metric)
度量聚合用于对文档中的数值型字段进行计算,返回单个或多个统计值(如平均值、最大值)。分为单值度量(返回一个值)和多值度量(返回多个相关值)。
单值度量聚合
(1)平均值(avg)
计算指定字段的平均值。
1 2 3 4 5 6 7 8 9
| GET products/_search { "size": 0, "aggs": { "avg_price": { "avg": { "field": "price" } } } }
|
响应示例:
1 2 3 4 5
| { "aggregations": { "avg_price": { "value": 250.5 } } }
|
(2)基数(cardinality)
计算指定字段的不同值的近似数量(类似 COUNT(DISTINCT 字段)
)。
1 2 3 4 5 6 7 8 9 10 11 12
| GET products/_search { "size": 0, "aggs": { "distinct_categories": { "cardinality": { "field": "category.keyword", "precision_threshold": 1000 } } } }
|
- 适用场景:统计不同类别、不同用户的数量(性能优于精确计数,适合大数据量)。
(3)最大值(max)、最小值(min)、求和(sum)
1 2 3 4 5 6 7 8 9
| GET products/_search { "size": 0, "aggs": { "max_price": { "max": { "field": "price" } }, "min_price": { "min": { "field": "price" } }, "total_sales": { "sum": { "field": "sales" } } } }
|
多值度量聚合
(1)统计(stats)
一次性返回字段的计数、最大值、最小值、平均值、总和。
1 2 3 4 5 6 7
| GET products/_search { "size": 0, "aggs": { "price_stats": { "stats": { "field": "price" } } } }
|
响应示例:
1 2 3 4 5 6 7 8 9 10 11
| { "aggregations": { "price_stats": { "count": 100, "min": 50.0, "max": 500.0, "avg": 250.5, "sum": 25050.0 } } }
|
(2)百分比(percentiles)
计算数值字段的百分比分布(默认返回 1%、5%、25%、50%、75%、95%、99% 分位值)。
1 2 3 4 5 6 7 8 9 10 11 12
| GET logs/_search { "size": 0, "aggs": { "load_time_percentiles": { "percentiles": { "field": "load_time", "percents": [90, 95, 99] } } } }
|
响应示例:
1 2 3 4 5 6 7 8 9 10 11
| { "aggregations": { "load_time_percentiles": { "values": { "90.0": 150.0, "95.0": 200.0, "99.0": 300.0 } } } }
|
(3)最高命中(top_hits)
返回每个分组中相关性最高的文档(如每个类别中销量最高的商品)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| GET products/_search { "size": 0, "aggs": { "by_category": { "terms": { "field": "category.keyword" }, "aggs": { "top_selling": { "top_hits": { "size": 1, "_source": ["name", "price", "sales"], "sort": [{ "sales": { "order": "desc" } }] } } } } } }
|
分组聚合(Bucketing)
分组聚合基于字段值或条件将文档划分为多个 “桶(Bucket)”,每个桶包含满足条件的文档。支持子聚合,可对每个桶进一步分析。
常用分组聚合
(1)索引词分组(terms)
按字段的精确值分组(类似 GROUP BY 字段
),适用于 keyword
或 integer
等字段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| GET products/_search { "size": 0, "aggs": { "by_category": { "terms": { "field": "category.keyword", "size": 10, "order": { "avg_price": "desc" } }, "aggs": { "avg_price": { "avg": { "field": "price" } } } } } }
|
响应示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| { "aggregations": { "by_category": { "buckets": [ { "key": "electronics", "doc_count": 50, "avg_price": { "value": 300.0 } }, { "key": "clothing", "doc_count": 30, "avg_price": { "value": 150.0 } } ] } } }
|
(2)直方图(histogram)
按数值间隔分组(如价格每 50 元一组),适用于连续数值字段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| GET products/_search { "size": 0, "aggs": { "price_ranges": { "histogram": { "field": "price", "interval": 50 }, "aggs": { "total_sales": { "sum": { "field": "sales" } } } } } }
|
(3)日期直方图(date_histogram)
按时间间隔分组(如按天、月统计),适用于 date
类型字段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| GET logs/_search { "size": 0, "aggs": { "daily_visits": { "date_histogram": { "field": "visit_time", "interval": "day", "format": "yyyy-MM-dd", "missing": "1970-01-01" }, "aggs": { "pv": { "value_count": { "field": "user_id" } } } } } }
|
(4)范围分组(range)
按自定义范围分组(如价格 0-100、100-200、200 以上)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| GET products/_search { "size": 0, "aggs": { "price_buckets": { "range": { "field": "price", "ranges": [ { "to": 100 }, { "from": 100, "to": 200 }, { "from": 200 } ] } } } }
|
特殊分组聚合
(1)过滤分组(filter)
仅对满足过滤条件的文档进行聚合(类似 WHERE
子句)。
1 2 3 4 5 6 7 8 9 10 11 12
| GET products/_search { "size": 0, "aggs": { "expensive_products": { "filter": { "range": { "price": { "gte": 200 } } }, "aggs": { "avg_sales": { "avg": { "field": "sales" } } } } } }
|
(2)嵌套分组(nested)
对嵌套类型(nested
)的字段进行分组(需指定嵌套路径)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| GET orders/_search { "size": 0, "aggs": { "nested_products": { "nested": { "path": "products" }, "aggs": { "by_product": { "terms": { "field": "products.name.keyword" }, "aggs": { "total_quantity": { "sum": { "field": "products.quantity" } } } } } } } }
|
管道聚合(Pipeline)
管道聚合不直接处理文档,而是基于其他聚合的结果进行二次计算,分为父聚合(基于子聚合结果)和兄弟聚合(基于同级聚合结果)。
移动平均(moving_avg)
对时序数据计算滑动窗口的平均值(如按日销量计算 5 天移动平均)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| GET sales/_search { "size": 0, "aggs": { "daily_sales": { "date_histogram": { "field": "date", "interval": "day" }, "aggs": { "total": { "sum": { "field": "amount" } }, "avg_5d": { "moving_avg": { "buckets_path": "total", "window": 5 } } } } } }
|
差值(derivative)
计算相邻分组的差值(如每日销量较前一天的变化量)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| GET sales/_search { "size": 0, "aggs": { "daily_sales": { "date_histogram": { "field": "date", "interval": "day" }, "aggs": { "total": { "sum": { "field": "amount" } }, "day_over_day": { "derivative": { "buckets_path": "total" } } } } } }
|
分组选择器(bucket_selector)
过滤掉不满足条件的分组(如只保留销售额≥1000 的日期)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| GET sales/_search { "size": 0, "aggs": { "daily_sales": { "date_histogram": { "field": "date", "interval": "day" }, "aggs": { "total": { "sum": { "field": "amount" } }, "filter_high_sales": { "bucket_selector": { "buckets_path": { "sales": "total" }, "script": "params.sales >= 1000" } } } } } }
|
聚合操作的关键注意事项
- 字段类型限制:
- 度量聚合仅支持数值型字段(
integer
、double
、date
等)。
- 分组聚合中,
terms
需使用 keyword
类型(不分词),否则可能因分词导致分组混乱。
- 性能优化:
- 大数据量下,
terms
聚合可通过 size
限制返回分组数,避免内存溢出。
- 基数聚合(
cardinality
)使用近似算法,通过 precision_threshold
平衡精度与性能。
- 嵌套聚合深度:
过度嵌套(如多层 terms
聚合)可能导致性能下降,建议控制嵌套层级(通常不超过 3 层)。
- 与查询结合:
聚合默认基于所有文档,若需过滤文档,可在 query
中添加条件(聚合仅作用于匹配的文档)
v1.3.10