一、前言
Elasticsearch 不仅是一个强大的搜索引擎,它还提供了丰富的聚合(Aggregation)功能,支持对数据进行多维度分析和统计。通过聚合查询,我们可以实现类似 SQL 中 GROUP BY
的分组操作,并结合各种指标函数(如 SUM、AVG、COUNT 等)生成报表、折线图、饼图等可视化数据展示。
本文将详细介绍 Elasticsearch 中常见的几种聚合方式,包括:
- Terms 聚合(按字段值分组)
- Histogram 聚合(按数值间隔分组)
- Date Histogram 聚合(按时间间隔分组)
- Range 聚合(自定义范围分组)
- 指标聚合(如 AVG、SUM、COUNT)
同时也会讲解 Java 客户端中如何使用这些聚合查询,并提供完整的示例代码。
二、基本概念
1. 聚合查询流程
Elasticsearch 的聚合查询流程分为两个主要步骤:
(1)分桶(Bucketing)
将满足特定条件的文档划分到不同的“桶”中。每个桶代表一个数据分组。
类比 SQL:相当于
GROUP BY
操作
(2)指标计算(Metrics)
在每个桶内部进行统计计算,例如求和、求平均值、计数等。
类比 SQL:相当于
SUM()
、AVG()
、COUNT()
等函数
三、常用聚合类型详解
1. Terms 聚合 —— 类似SQL的group by,根据字段唯一值分组
Terms 聚合类似于 SQL 中的 GROUP BY
,根据字段的唯一值对数据进行分组。
示例 DSL 查询:
GET person_info/_search
{
"size": 0,
"aggs": {
"source_buckets": {
"terms": {
"field": "source.keyword"
}
}
}
}
返回结果示例:
"buckets": [
{ "key": "填表", "doc_count": 5340 },
{ "key": "普查", "doc_count": 56 },
{ "key": "网上下载", "doc_count": 39 }
]
Java 代码示例:
TermsAggregationBuilder termAggBuilder = AggregationBuilders.terms("buket_name").field("analysis.sensitive_words").size(100).minDocCount(0);
searchSourceBuilder.aggregation(termAggBuilder);
try {
searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new RuntimeException(e);
}
Aggregations aggregations = searchResponse.getAggregations();
if(aggregations!= null){
Terms terms = aggregations.get("buket_name");
List<? extends Terms.Bucket> buckets = terms.getBuckets();
JSONObject reslut = new JSONObject(true);
for (Terms.Bucket bucket : buckets) {
//分组的key
String key = bucket.getKeyAsString();
long docCount = bucket.getDocCount();
reslut.put(key,docCount);
}
return AjaxResult.success(reslut);
}else {
return AjaxResult.error("查询无数据");
}
解析聚合结果时,遍历 Terms.Bucket
即可获取每个分组的 key 和 count。
2. Histogram 聚合 —— 根据数值间隔分组,可做直方图
Histogram 聚合适用于数值型字段,按照指定的数值间隔进行分组,常用于绘制直方图或条形图。
示例 DSL 查询:
GET person_info/_search
{
"size": 0,
"aggs": {
"age_buckets": {
"histogram": {
"field": "age",
"interval": 5
}
}
}
}
返回结果示例:
"buckets": [
{ "key": 10, "doc_count": 814 },
{ "key": 15, "doc_count": 1612 },
{ "key": 20, "doc_count": 1290 }
]
Java 代码示例:
HistogramAggregationBuilder histogramAgg = AggregationBuilders.histogram("age_buckets")
.field("age")
.interval(5);
searchSourceBuilder.aggregation(histogramAgg);
3. Date Histogram 聚合 —— 根据时间间隔分组,可做时间折线图
Date Histogram 是专门处理时间字段的聚合方式,支持按天、月、小时等时间单位进行分组,非常适合做趋势图分析。
示例 DSL 查询:
GET person_info/_search
{
"size": 0,
"aggs": {
"buket_name": { //这是为了不返回数据,只返回聚合结果
"date_histogram": { // 聚合类型为: date_histogram
"field": "data", // 根据date字段分组
"calendar_interval": "month", // 分组间隔,详解在下边
"format" : "yyyy-MM-dd", // 设置返回结果中桶key的时间格式
"time_zone": "+08:00", //**设置时区,如果存入的时候没设置就不用填**
"min_doc_count": 0, // 没有数据的月份返回0
"extended_bounds": { //强制返回的日期区间,既需要填充0的范围
"min": "2000-01-01",
"max": "2003-01-01"
}
}
}
}
}
GET person_info/_search
{
"size": 0,
"aggs": {
"date_buckets": {
"date_histogram": {
"field": "data",
"calendar_interval": "month",
"format": "yyyy-MM-dd",
"time_zone": "+08:00",
"min_doc_count": 0,
"extended_bounds": {
"min": "2000-01-01",
"max": "2003-01-01"
}
}
}
}
}
参数说明:
参数 | 说明 |
---|---|
field | 要聚合的时间字段名 |
calendar_interval | 使用日历感知的时间间隔(如 day, month 等) |
fixed_interval | 固定时间间隔(如 2h、5m) |
format | 返回桶 key 的时间格式 |
time_zone | 设置时区,解决时间偏移问题 |
min_doc_count | 最小文档数,设置为 0 表示返回空桶 |
extended_bounds | 强制返回指定时间范围内的所有桶(包括无数据的) |
fixed_interval
和 calendar_interval
区别
对比项 | calendar_interval | fixed_interval |
---|---|---|
是否支持复合单位 | ❌ 不支持(只能用 1d、1M) | ✅ 支持任意倍数(如 2h、5m) |
是否考虑日历逻辑 | ✅ 按自然日历切分(如每月第一天) | ❌ 固定时间长度,不考虑日历 |
支持单位 | minute、hour、day、week、month、quarter、year | ms、s、m、h、d |
推荐场景 | 按自然月、自然天聚合 | 按固定时间间隔(如每 2 小时)聚合 |
⚠️ 注意:
interval
参数在 ES 7.2+ 已被弃用,推荐使用fixed_interval
或calendar_interval
。
Java 代码示例:
DateHistogramAggregationBuilder dateHisAgg = AggregationBuilders.dateHistogram("date_buckets")
.field("data_time")
.fixedInterval(DateHistogramInterval.hours(2))
//.calendarInterval(DateHistogramInterval.HOUR);
.timeZone(ZoneOffset.of("+08:00"))
.format("yyyy-MM-dd HH:mm:ss");
searchSourceBuilder.aggregation(dateHisAgg);
4. Range 聚合 —— 自定义数值范围分组
Range 聚合允许我们自定义多个区间,将数据划分到不同的区间中。
示例 DSL 查询:
GET /xxx/_search
{
"size": 0,
"aggs": {
"warn_range": {
"range": {
"field": "alarmNum",
"ranges": [
{ "to": 24 },
{ "from": 24, "to": 32 },
{ "from": 32 }
]
}
}
}
}
Java 代码示例:
RangeAggregationBuilder rangeAgg = AggregationBuilders.range("warn_range")
.field("alarmNum")
.addUnboundedTo(24)
.addRange(24, 32)
.addUnboundedFrom(32);
searchSourceBuilder.aggregation(rangeAgg);
四、指标聚合(Metrics)
指标聚合是对桶内数据进行统计计算的操作,常见类型如下:
指标类型 | 说明 |
---|---|
avg | 计算平均值 |
sum | 计算总和 |
min | 获取最小值 |
max | 获取最大值 |
value_count | 统计总数(类似 SQL 的 COUNT) |
cardinality | 统计去重后的数量(类似 SQL 的 COUNT(DISTINCT)) |
示例 DSL 查询:
POST /exams/_search?size=0
{
"aggs": {
"avg_grade": {
"avg": {
"field": "grade"
}
}
}
}
Java 代码示例:
AvgAggregationBuilder avgAgg = AggregationBuilders.avg("avg_grade").field("grade");
searchSourceBuilder.aggregation(avgAgg);
五、多层嵌套聚合与排序
Elasticsearch 支持多层嵌套聚合,即在一个桶的基础上再进行细分,形成树状结构的聚合。
示例 DSL 查询:
GET /project_zcy/_search
{
"size": 0,
"query": {
"term": {
"project_status.keyword": {
"value": "执行中"
}
}
},
"aggs": {
"institute_agg": {
"terms": {
"field": "institute_code.keyword"
},
"aggs": {
"name_agg": {
"terms": {
"field": "institute_name.keyword"
},
"aggs": {
"total_alarm": {
"sum": {
"field": "alarmNum"
}
},
"count": {
"value_count": {
"field": "institute_code.keyword"
}
}
}
}
}
}
}
}
排序方式:
_count
:按文档数量排序_term
:按字段值排序(仅 terms)_key
:按键值排序(仅 histogram)
"order": {
"_count": "desc"
}
六、限制返回桶的数量(size)
默认情况下,Elasticsearch 返回最多 10 个桶。可以通过 size
参数调整:
"terms": {
"field": "product",
"size": 5
}
七、总结
Elasticsearch 提供了非常灵活和强大的聚合功能,能够满足从基础分组统计到复杂数据分析的各种需求。掌握好聚合查询,不仅能帮助你快速构建数据看板,还能为后续的数据挖掘和业务决策提供有力支撑。
八、扩展建议
- 使用 Kibana 可视化工具查看聚合结果,效果更直观。
- 多层聚合配合指标统计可用于构建复杂的业务报表。
- 结合
moving_avg
、derivative
等高级聚合,可实现时间序列预测和变化率分析。