Elasticsearch嵌套聚合条件路径:按评分聚合仅取IP或InstanceId
解决嵌套字段聚合时仅保留IP/InstanceId其中一个的问题
嘿,我完全懂你遇到的这个困扰——你的嵌套字段里有IP、InstanceId和rating三个属性,现在按rating聚合时,桶列表里会同时混着这两个标识字段,但你希望每个文档只贡献其中一个(要么IP,要么InstanceId)到聚合结果里,对吧?
下面给你两种可行的实现方式,你可以根据自己的Elasticsearch版本和实际需求来选:
方式一:用脚本生成统一标识(最直接)
核心思路是在嵌套聚合内部,通过脚本动态生成一个统一标识字段:优先取IP的值,如果当前嵌套文档没有IP,就用InstanceId。这样每个嵌套文档只会对应一个标识,不会同时出现两个。
直接上示例DSL:
{ "aggs": { "traverse_nested": { "nested": { "path": "你的嵌套字段名" // 替换成你实际的nested字段路径 }, "aggs": { "unique_identifier_buckets": { "terms": { "script": { "source": "doc['你的嵌套字段名.IP'].size() > 0 ? doc['你的嵌套字段名.IP'].value : doc['你的嵌套字段名.InstanceId'].value" } }, "aggs": { "rating_aggregation": { // 这里换成你需要的rating聚合类型,比如stats/avg/max等 "stats": { "field": "你的嵌套字段名.rating" } } } } } } } }
小提示
- 如果你的ES版本低于7.10,把脚本里的
doc['field'].size() > 0换成doc.containsKey('field')就能正常判断字段是否存在了。 - 要是你想优先保留InstanceId而非IP,只需要调换脚本里的判断顺序就行。
方式二:用过滤器拆分分组(更清晰区分两类标识)
如果你想明确分开统计IP和InstanceId各自的rating情况,不想把它们混在同一个terms桶里,可以先用filters聚合把数据分成“有IP的组”和“有InstanceId的组”,再分别做聚合:
示例DSL如下:
{ "aggs": { "traverse_nested": { "nested": { "path": "你的嵌套字段名" }, "aggs": { "split_by_identifier_type": { "filters": { "filters": { "ip_documents": { "exists": { "field": "你的嵌套字段名.IP" } }, "instance_id_documents": { "exists": { "field": "你的嵌套字段名.InstanceId" } } } }, "aggs": { "identifier_buckets": { "terms": { "script": { "source": "doc['你的嵌套字段名.IP'].size() > 0 ? doc['你的嵌套字段名.IP'].value : doc['你的嵌套字段名.InstanceId'].value" } }, "aggs": { "rating_stats": { "stats": { "field": "你的嵌套字段名.rating" } } } } } } } } } }
这种方式的好处是,你能清晰看到两类标识各自的rating统计结果,而且每个标识只会出现在对应的分组桶里,不会交叉。
额外注意
确保你的IP和InstanceId字段在映射里是可搜索的(别设成index: false),不然脚本没法读取它们的值哦。
内容的提问来源于stack exchange,提问作者John Diaz




