如何创建基于两个字段的Elasticsearch过滤聚合,统计ID总出现次数?
基于两个keyword字段统计Top10 ID计数(Elasticsearch 7.12.1)
因为你使用的ES版本是7.12.1,不支持7.13才引入的combined_fields,可以通过以下两种方式实现需求:
方法一:使用Painless脚本动态合并字段(无需修改索引)
直接在聚合中通过脚本将两个字段的ID收集、去重后,再做terms聚合统计Top10。这种方式无需修改现有索引结构,适合快速验证需求:
GET /你的索引名/_search { "size": 0, // 不需要返回文档内容,只看聚合结果 "aggs": { "top_10_combined_ids": { "terms": { "script": { "source": """ def ids = []; // 处理field1非空的情况 if (doc['field1'].size() > 0) { ids.add(doc['field1'].value); } // 处理field2非空的情况 if (doc['field2'].size() > 0) { ids.add(doc['field2'].value); } // 去重后返回数组,确保同一文档的重复ID只统计一次 return ids.stream().distinct().collect(Collectors.toList()); """, "lang": "painless" }, "size": 10, // 返回Top10的ID "shard_size": 20 // 可选,提升分片间TopN结果的准确性 } } } }
说明
- 脚本会遍历每个文档的
field1和field2,收集非空ID并去重,避免同一文档中两个字段出现相同ID时重复计数 shard_size参数可根据数据规模调整,设置为比size更大的值能减少分片间结果合并时的误差
方法二:预先生成合并字段(性能更优)
如果数据量较大,脚本聚合的性能可能不足,建议在索引时预先生成一个合并字段,将field1和field2的ID去重后存入,后续直接基于该字段做聚合:
1. 添加合并字段的映射
PUT /你的索引名/_mapping { "properties": { "combined_ids": { "type": "keyword" // 保持与原字段一致的类型 } } }
2. 批量更新已有文档
POST /你的索引名/_update_by_query { "script": { "source": """ def ids = []; if (ctx._source.field1 != null) { ids.add(ctx._source.field1); } if (ctx._source.field2 != null) { ids.add(ctx._source.field2); } ctx._source.combined_ids = ids.stream().distinct().collect(Collectors.toList()); """, "lang": "painless" } }
3. 基于合并字段做Top10聚合
GET /你的索引名/_search { "size": 0, "aggs": { "top_10_ids": { "terms": { "field": "combined_ids", "size": 10 } } } }
说明
- 这种方式将合并逻辑提前到索引阶段,聚合时直接使用原生的terms聚合,性能远优于脚本聚合
- 后续新增文档时,需要在写入逻辑中同步生成
combined_ids字段
内容的提问来源于stack exchange,提问作者AndrewS
相关产品推荐
相关产品推荐

