Elasticsearch:查找tree_path含指定method_id的文档并聚合求助
问题:Elasticsearch按method_id匹配tree_path节点并聚合
索引映射
{ "mappings": { "properties": { "method_id": { "type": "long" }, "tree_path": { "type": "text", "fields": { "keyword": { "type": "keyword" } } } } } }
示例文档
// 文档1 { "tree_path": "/1/2/3/4", "method_id": 4 } // 文档2 { "tree_path": "/1/2/3", "method_id": 3 } // 文档3 { "tree_path": "/1/2", "method_id": 2 }
需求说明
- 输入:一组
method_id列表(比如[2,3]) - 目标:对每个
method_id,找出所有tree_path任意位置包含该method_id的文档,然后执行聚合操作 - 补充规则:文档自身的
method_id对应tree_path的最后一个节点
我的问题
我写了下面的查询,但无法从父桶获取method_id来匹配tree_path:
{ "query": { "bool": { "filter": [ { "terms":{ "method_id":[2,3] } } ] } }, "aggs": { "filter_method_id_agg":{ "filter": { "terms": { "method_id": [2,3] } }, "aggs": { "by_method_id_agg":{ "terms": { "field": "method_id" }, "aggs": { "by_tree_path":{ "filter":{ "term":{ "tree_path": "_key" } }, "aggs":{ // 这里是我要执行的数值字段聚合(比如求和) } } } } } } } }
问题分析
- 外层查询过滤错误:原查询只保留了
method_id等于输入值的文档,但需求是要匹配tree_path包含输入method_id的所有文档,不管文档自身的method_id是什么(比如method_id=4的文档,tree_path里包含2和3,也应该被纳入统计)。 - 子聚合匹配逻辑无效:
"tree_path": "_key"会把_key当成字符串字面量,而不是引用父桶的method_id值,根本达不到匹配tree_path的目的。
修正后的查询方案
方案1:手动指定每个method_id的匹配规则(适合固定输入列表)
用filters聚合为每个输入的method_id单独创建桶,每个桶内精准匹配tree_path包含该id的文档:
{ "size": 0, // 不需要返回原始文档,只取聚合结果 "aggs": { "by_target_method_id": { "filters": { "filters": { "method_id_2": { "bool": { "should": [ {"wildcard": {"tree_path.keyword": "*/2/*"}}, {"wildcard": {"tree_path.keyword": "*/2"}} ], "minimum_should_match": 1 } }, "method_id_3": { "bool": { "should": [ {"wildcard": {"tree_path.keyword": "*/3/*"}}, {"wildcard": {"tree_path.keyword": "*/3"}} ], "minimum_should_match": 1 } } } }, "aggs": { // 替换为你的实际聚合操作,比如求和 "sum_my_numeric_field": { "sum": { "field": "your_numeric_field" } } } } } }
方案2:动态匹配输入列表(适合可变输入)
通过terms聚合结合脚本,自动为每个输入的method_id匹配对应的tree_path:
{ "size": 0, "query": { "bool": { "should": [ {"wildcard": {"tree_path.keyword": "*/2/*"}}, {"wildcard": {"tree_path.keyword": "*/2"}}, {"wildcard": {"tree_path.keyword": "*/3/*"}}, {"wildcard": {"tree_path.keyword": "*/3"}} ], "minimum_should_match": 1 } }, "aggs": { "target_method_buckets": { "terms": { "field": "method_id", "include": [2,3] // 只保留输入的method_id对应的桶 }, "aggs": { "matching_tree_docs": { "filter": { "script": { "source": """ def path = doc['tree_path.keyword'].value; def targetId = params._key.toString(); // 匹配路径中间或末尾的目标id return path.contains('/' + targetId + '/') || path.endsWith('/' + targetId); """, "params": { "_key": "{{_key}}" } } }, "aggs": { "sum_my_numeric_field": { "sum": { "field": "your_numeric_field" } } } } } } } }
关键注意点
- 用
tree_path.keyword匹配:避免分词导致的错误匹配,同时提升查询效率。 - 匹配规则覆盖两种场景:
*/X/*匹配路径中间的节点,*/X匹配路径末尾的节点,防止误匹配(比如避免把/12/当成包含2)。
内容的提问来源于stack exchange,提问作者Ronit Tsysar
相关产品推荐
相关产品推荐

