bigywater.tech
Elasticsearch · 85

Parent-Child 聚合

真实用join字段建立问题与回答的父子关系,验证children聚合默认跨父文档拉平统计、parent_id查询实现按单个父文档分组、has_child查询根据子文档条件反向过滤父文档这三种真实不同的parent-child聚合用法。

children聚合跨全部问题
真实把3个回答拉平统计,avg_score=6.0
parent_id限定到q1
真实只统计2个回答,avg_score变7.5
has_child查询
真实根据子文档条件反向筛选出q1,排除q2

做的事:真实用join字段建父子关系,2个问题真实共有3个回答,用children聚合统计全部回答的平均分

代码mapping: {"join_field":{"type":"join","relations":{"question":"answer"}}} 真实索引: q1问题 / a1(父q1,score5) / a2(父q1,score10) / q2问题 / a3(父q2,score3) POST /pc_test/_search {"size":0,"query":{"term":{"join_field":"question"}},"aggs":{ "to_answers":{"children":{"type":"answer"},"aggs":{"avg_score":{"avg":{"field":"score"}}}} }}

验证:children聚合真实把两个问题下的全部回答拉平统计,不区分来自哪个问题

实测 · 本机 Elasticsearch 8.15.0 执行输出
children聚合的doc_count: 3   ← 真实3个回答总数
avg_score(跨全部问题统计): 6.0   ← 真实(5+10+3)/3=6.0

这组真实结果展示了children聚合的默认行为:跟本系列上一篇验证过的nested聚合类似,它真实把搜索上下文从父文档切换到子文档集合,统计出来的doc_count真实是子文档(回答)的总数,不是父文档(问题)的数量;如果不额外加分组条件,avg_score真实是跨所有问题、所有回答混在一起算出的整体平均分,没法直接看出"q1的回答平均分"和"q2的回答平均分"分别是多少。

真实用parent_id查询真实只限定q1这一个问题下的回答,看avg_score会不会相应改变

代码POST /pc_test/_search {"size":0,"query":{"parent_id":{"type":"answer","id":"q1"}}, "aggs":{"avg_score":{"avg":{"field":"score"}}}}

验证:限定到q1之后,avg_score真实变成了7.5,精确等于q1的两个回答(5和10)的平均值

实测 · 本机执行输出
parent_id限定q1: hits=2(真实只剩q1的两个回答a1和a2)
avg_score: 7.5   ← 真实(5+10)/2=7.5,q2的回答完全没被算进来
这组真实对比验证了parent_id查询是真实实现"按单个父文档分组统计"的手段:它先真实把搜索范围收窄到指定父文档的子文档,再在这个收窄后的范围内做metric聚合,真实得到的avg_score从6.0(全部3个回答)精确变成了7.5(只有q1的2个回答)。如果业务上需要真实对每一个问题各自算出回答平均分(而不是全部问题混在一起算一个总平均分),需要真实像本次这样针对每个具体的父文档id分别发起查询,或者结合terms聚合在父文档层面先分组再用children聚合统计——这正是parent-child关系聚合比nested聚合更复杂的地方:nested的子文档物理上紧跟在父文档后面存储,而parent-child关系的父子文档真实是完全独立的两种文档类型,跨父文档做分组统计真实需要额外的查询条件配合,不像nested聚合那样天然在父文档内部完成。

真实用has_child查询反过来找出"至少有一个高分回答的问题"

代码POST /pc_test/_search {"query":{"has_child":{"type":"answer","query":{"range":{"score":{"gte":10}}}}}}

验证:真实只命中了q1,q2因为唯一的回答分数只有3,真实被正确排除

实测 · 本机执行输出
→ 命中: q1 "how does ES work"
  ← q2真实没有出现,因为它唯一的回答a3分数只有3,不满足>=10的条件

这组真实结果展示了has_child查询如何真实根据子文档的条件反过来筛选父文档:q1真实有一个分数10的回答满足条件,被真实判定为匹配;q2的唯一回答分数真实只有3,不满足门槛,整个q2问题真实没有出现在结果里。这正是parent-child关系真实区别于nested的另一个维度——它不仅支持用children聚合统计子文档,还真实支持用has_child/has_parent这类查询,根据对方的条件反向过滤,这种双向关联查询能力是nested字段真实不具备的。

分步演示:Parent-Child聚合从跨父文档拉平统计到按单个父文档分组再到反向过滤的完整验证

每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。

children聚合默认拉平统计,需要parent_id或has_child才能按父文档分组或反向过滤
这一步新验证的parent-child查询方式

    本文用到的工具

    • 本机真实部署的 Elasticsearch 8.15.0(单节点,tar包直接启动)
    • 真实的 join 字段、children 聚合与 has_child 查询
    京ICP备2026067502号-1京公网安备11010502063487号