Elasticsearch 数组字段范围过滤后按用户分组count聚合如何实现?
Elasticsearch 数组字段区间过滤计数聚合方案
核心实现思路
- 先通过
terms聚合按customer_id字段对所有文档分组,保证所有用户都出现在统计结果中 - 嵌套
sum聚合配合自定义Painless脚本,遍历每个文档的purchase_date_list数组,累加符合20210101到当前日期区间的元素个数,最终得到每个用户的有效购买次数
完整查询DSL
假设你的索引中purchase_date_list存储的是yyyyMMdd格式的整数,查询语句如下:
GET 替换为你的实际索引名/_search { "size": 0, "aggs": { "按用户分组": { "terms": { "field": "customer_id", "size": 1000 }, "aggs": { "2021年至今购买次数": { "sum": { "script": { "source": """ int count = 0; def nowDate = new SimpleDateFormat("yyyyMMdd").format(new Date()) as int; for (def purchaseDate : doc['purchase_date_list']) { if (purchaseDate >= 20210101 && purchaseDate <= nowDate) { count++; } } return count; """, "lang": "painless" } } } } } } }
如果担心服务端时区和预期不一致,也可以把日期区间作为参数传入,避免脚本自动生成日期的误差:
GET 替换为你的实际索引名/_search { "size": 0, "aggs": { "按用户分组": { "terms": { "field": "customer_id", "size": 1000 }, "aggs": { "2021年至今购买次数": { "sum": { "script": { "source": """ int count = 0; for (def purchaseDate : doc['purchase_date_list']) { if (purchaseDate >= params.startDate && purchaseDate <= params.endDate) { count++; } } return count; """, "params": { "startDate": 20210101, "endDate": 20240520 // 替换为你需要的截止日期 } } } } } } } }
结果说明
返回结果的aggregations.按用户分组.buckets列表中,每个项的key为customer_id,2021年至今购买次数.value就是对应用户的统计值,和预期结果完全匹配:用户1返回2,用户2返回0,用户3返回1。
注意事项
- 如果
purchase_date_list是日期类型,只需要修改脚本中的判断逻辑,直接用日期对象的isAfter/isBefore方法判断即可 terms聚合的size参数需要根据你的用户总数调整,避免漏掉用户- 需要确保
customer_id、purchase_date_list字段开启了doc_values(默认配置已开启,支持聚合运算)
内容的提问来源于stack exchange,提问作者jiajun
相关产品推荐
相关产品推荐

