6.2 桶聚合:把文档分进格子 本节摘要:桶聚合(bucket aggregations)给每个文档发一张格子门票:terms 按词条分格、range 按区间分格、datehistogram 按时间切片分格;格子里还能再嵌套子聚合,一层套一层长成完整报表。本节讲三种主力桶的写法、嵌套语法、以及 terms 桶"分片各自取前 N 再归并"的取数边界——它决定报表数字什么时候会有小误差、怎么核对。 把文档分进格子 terms:按词条分格 最常用的桶是 terms:统计每种状态各有多少工单: 桶结果按文档数降序,size 限定返回桶数;被裁掉的文档记在 sumotherdoccount 里——它不是错误,是"其余"那一格。terms 桶吃 keyword 词条或数值,text 老规矩走子字段。
本节摘要:桶聚合(bucket aggregations)给每个文档发一张格子门票:terms 按词条分格、range 按区间分格、date_histogram 按时间切片分格;格子里还能再嵌套子聚合,一层套一层长成完整报表。本节讲三种主力桶的写法、嵌套语法、以及 terms 桶"分片各自取前 N 再归并"的取数边界——它决定报表数字什么时候会有小误差、怎么核对。
最常用的桶是 terms:统计每种状态各有多少工单:
GET tickets/_search { "size": 0, "aggs": { "by_status": { "terms": { "field": "status", "size": 10 } } } }
{ "aggregations": { "by_status": { "buckets": [ { "key": "pending", "doc_count": 1620 }, { "key": "resolved", "doc_count": 1288 }, { "key": "escalated", "doc_count": 356 } ], "sum_other_doc_count": 949 } } }
桶结果按文档数降序,size 限定返回桶数;被裁掉的文档记在 sum_other_doc_count 里——它不是错误,是"其余"那一格。terms 桶吃 keyword 词条或数值,text 老规矩走子字段。
数值分格用 range,时间报表用 date_histogram:
GET tickets/_search { "size": 0, "aggs": { "reply_buckets": { "range": { "field": "first_reply_minutes", "ranges": [ { "to": 15 }, { "from": 15, "to": 60 }, { "from": 60 } ] } } } }
GET tickets/_search { "size": 0, "aggs": { "per_day": { "date_histogram": { "field": "created_at", "calendar_interval": "day", "time_zone": "+08:00", "min_doc_count": 0 } } } }
date_histogram 有两个参数直接决定报表口径:time_zone 不设就按协调世界时切天,中国用户会把凌晨八小时算进前一天——按本地日切报表必设时区;min_doc_count 设零让空日子也出格,折线图不断轴。calendar_interval 按日历切(月有大小),fixed_interval 按固定时长切(严格等宽),报表口径要写清用的哪个。
桶的威力在嵌套——每格之内再挂子聚合:
GET tickets/_search { "size": 0, "aggs": { "per_day": { "date_histogram": { "field": "created_at", "calendar_interval": "day", "time_zone": "+08:00" }, "aggs": { "by_status": { "terms": { "field": "status" }, "aggs": { "avg_reply": { "avg": { "field": "first_reply_minutes" } } } } } } } }
请求的缩进结构就是结果树的形状:每天一桶,桶内按状态再分桶,最内层算平均响应分钟——一张"每日各状态工单量与平均响应"的二维表就此成型。嵌套可以有合理深度(两三层常见),但每层都是笛卡尔式扩张,层级越深、桶数越多、内存与耗时越大,报表设计时先算桶数量级。

背景:运营要"按客服分组、按天看处理量"的热力图,客服约二百人。操作:外层 date_histogram 按天、内层 terms 按 assignee,size 先设二十;上线第一周收到反馈"某些天的客服排名每天略有跳动"。排查:看响应里 doc_count_error_upper_bound 为 3,而第二十名附近几个客服日计数在 4 上下——误差上界与计数同量级,排序自然抖动。修复:客服总数二百,把 terms 的 size 提到二百五(大于基数),排序稳定;请求耗时可接受(每天二百桶乘以三十天,共六千桶)。解读:terms 的 size 不是"显示多少"而是"分片保留多少",基数已知且可控时,size 盖过基数即可消除抖动。变式:基数不可控(比如按城市)时,接受近似并在界面标注"前二十为精确排序",或改用按城市预聚合的汇总索引(第 9 章的思路)。
⚠️ 常见坑:date_histogram 忘设 time_zone,跨时区团队各看各的"天",数字对不上开三小时会。按本地日切报表,时区参数是口径的一部分,写进报表注释。
💡 关键直觉:桶是格子,嵌套是格子里的格子,而 size 是分片端的筛子。读任何 terms 报表前先看两件事:sum_other_doc_count 裁掉了多少、误差上界多大——数字的信用额度写在响应里。
| 考核点 | 达标标准 |
|---|---|
| 三桶选型 | terms、range、date_histogram 各配一个真实业务需求 |
| 时区口径 | 说出本地日报表必须带的参数与缺省行为 |
| 误差判读 | 用误差上界与头部桶差距判断排序可信度 |
| size 语义 | 区分"显示条数"与"分片保留数"两种 size |
格子有了、数算完了,下一节让格子之间互相运算:管道聚合把环比、排名、累计一次性补齐。