聚合分析
Metrics、bucket 与 pipeline。
聚合分析:Metrics、Bucket 与 Pipeline
这一课你将能做到
- 区分指标聚合(metrics)与桶聚合(bucket),写出题库常用的 avg/max/stats 与 terms/date_histogram
- 用嵌套聚合实现「按课程分桶 → 桶内按标签分桶 → 统计平均难度」的多层分析
- 使用管道聚合(pipeline)做桶间的派生计算(累计、环比),并知道聚合性能边界在哪里
为什么要学
ES 不只是搜索引擎,也是分析引擎。运营看板上的问题——「每个课程有多少题、平均难度多少、本月新增多少题、哪些标签题目最多」——不需要再把数据捞到 MySQL 里 GROUP BY。聚合直接在倒排索引的列式结构(doc_values)上计算,亿级数据也能秒级出结果,Kibana 的图表底层全是聚合 DSL。
人话:metrics 回答「一个数」(平均多少、最大多少);bucket 回答「分成几组,每组怎样」(先按课程分组,再算每组);pipeline 回答「基于其他聚合结果再算」(累计值、差值)。三者组合就是一个报表后端。
先建立图像
聚合分类:
指标聚合(对文档算值):
avg / sum / min / max / stats / cardinality(去重计数)/ percentiles
桶聚合(把文档分组):
terms 按词项分组(标签、课程)
date_histogram 按时间区间分组(每天/每月)
histogram 按数值区间分组(难度区间)
range 按自定义区间
管道聚合(对其他聚合结果再算):
derivative 相邻桶差值(环比)
cumulative_sum 累计求和
bucket_script 桶级表达式
avg_bucket 对桶结果求均值
执行顺序:先按 query 圈定文档,再对这些文档同时算聚合;桶里可以嵌套子聚合,形成树。
跟着做
步骤 1:指标聚合——题库整体情况
curl -u elastic:abcd1234 -X GET "http://localhost:9200/questions/_search" -H "Content-Type: application/json" -d'
{
"size": 0,
"aggs": {
"avg_difficulty": { "avg": { "field": "difficulty" } },
"max_difficulty": { "max": { "field": "difficulty" } },
"difficulty_stats": { "stats": { "field": "difficulty" } },
"tag_count": { "cardinality": { "field": "tags" } }
}
}'
要点:size: 0 表示不要文档、只要聚合结果;cardinality 是 HyperLogLog 近似去重,省内存,小数据量误差为零,海量时有可配置误差——做「标签有多少种」足够。
stats 一次返回 count/min/max/avg/sum 五个值;想要方差、标准差用 extended_stats。
步骤 2:terms 桶聚合——每个标签多少题
curl -u elastic:abcd1234 -X GET "http://localhost:9200/questions/_search" -H "Content-Type: application/json" -d'
{
"size": 0,
"aggs": {
"by_tag": {
"terms": {
"field": "tags",
"size": 10,
"order": { "_count": "desc" }
}
}
}
}'
注意:聚合字段是 keyword 类型(对 text 聚合会出问题,第 4 课讲过)。terms 聚合返回的是近似 Top N:每个分片各取自己的 Top N 再合并,N 之外的长尾可能丢失。需要精确可加大 size(有内存成本),或用 composite 聚合做全量分页。
步骤 3:多层嵌套——课程 → 标签 → 难度
curl -u elastic:abcd1234 -X GET "http://localhost:9200/questions/_search" -H "Content-Type: application/json" -d'
{
"size": 0,
"query": { "term": { "published": true } },
"aggs": {
"by_course": {
"terms": { "field": "courseId", "size": 20 },
"aggs": {
"avg_diff": { "avg": { "field": "difficulty" } },
"by_tag": {
"terms": { "field": "tags", "size": 5 },
"aggs": {
"tag_avg_diff": { "avg": { "field": "difficulty" } }
}
}
}
}
}
}'