运营的报表需求来了
搜索和翻页都稳了,运营的周会材料升级成数据分析:各品牌有多少商品、均价多少;商品价格分几个段卖得好;上架量按月什么趋势。老王原计划写定时任务导 MySQL 慢慢算,同事提了一句:ES 的聚合(aggregation)就是干这个的。搜索靠倒排,聚合靠正排,第 2 篇埋的 doc values 伏笔,这篇开始收利息。
两大家族:分桶与指标
聚合就两个家族。分桶(bucket)负责「把文档按规则扔进格子」:按品牌扔、按价格段扔、按月份扔;指标(metric)负责「对每个格子算数」:算平均价、总销量、最大值。两者嵌套起来,就是一张报表:
GET /product/_search
{
"size": 0,
"aggs": {
"brands": {
"terms": { "field": "brand.keyword", "size": 10 },
"aggs": {
"avg_price": { "avg": { "field": "price" } },
"total_sales": { "sum": { "field": "sales" } }
}
}
}
}terms 按品牌分桶,桶里再嵌 avg 和 sum 两个指标,一次请求拿回「前十品牌加各自均价销量」。size 设 0 是关键细节:只要聚合结果,不要命中文档,省掉整个返回文档的开销。聚合和 query 可以同台:query 先筛出「上架商品」,aggs 只在这个结果集里统计——先过滤后聚合,这本身就是个常用模式。
价格段、时间轴与去重
价格段分布用 range 聚合,边界自己画:
"price_bands": {
"range": {
"field": "price",
"ranges": [
{ "to": 10 }, { "from": 10, "to": 50 }, { "from": 50 }
]
}
}上架趋势用 date_histogram,按月分桶自动处理大小月和时区,桶里嵌 terms 看每月哪个品类卖得多,一张运营驾驶舱的骨架就出来了。想知道「有多少个不同品牌」用 cardinality 做去重计数——注意它是近似算法,亿级数据下误差在百分之一内,报表够用,财务对账就别用它。还有一族 pipeline 聚合能在聚合结果上再算数(比如算环比),进阶需求再碰。
聚合字段用 keyword,别用 text
老王顺手想对 name 聚合,ES 报错提醒 Fielddata is disabled——这就是第 5 篇那笔账的后续。规则:terms 聚合认词条,text 字段的词条是切碎的,聚出来的桶没有业务意义(按「燕」「麦」分桶毫无用处)。所以字符串聚合一律走 keyword 子字段:brand.keyword、tags.keyword。ES 报错其实是在拦你,真要用 text 聚合可以打开 fielddata,但那会把切碎的词条全装进堆内存,属于自残操作,别开。
性能上再记两条:聚合走的是 doc values 正排(列式存储,第 2 篇讲过),不算分不走倒排,所以聚合天然快;terms 的 size 别贪大,一次拉一千个桶,正排扫描和返回体积都跟着涨,报表页先问自己真的要看前一千个品牌吗。
小结
聚合一句话:bucket 分格子,metric 算格子,先 query 过滤再 aggs 统计,字符串聚合走 keyword,去重计数是近似值。运营的周会报表,从此一条 API 请求搞定。
读和查都顺了,老王开始关心写:商品同步的高峰期,bulk 写入把集群 CPU 干到报警。下一篇讲写入调优:bulk 批量、副本与刷新的写入三角,让导入快五倍的实操清单。
评论 (0)