bigywater.tech
Elasticsearch · 81

top_hits 获取每个分组的代表文档

真实用terms聚合按作者分组嵌套top_hits,精确取出每位作者浏览量最高的代表文章,并验证top_hits的排序范围覆盖完整的桶内文档集合而非被截断的子集。

terms+top_hits
真实精确取到每位作者浏览量最高的那篇文章
top_hits.hits.total
真实精确等于外层桶的doc_count
相比collapse
真实能嵌套在任意层级bucket聚合内部用途更通用

做的事:真实索引两位作者各自的文章,用terms分组后嵌套top_hits取每位作者浏览量最高的那篇

代码真实索引: alice 3篇(浏览量100/500/200) / bob 2篇(浏览量300/50) POST /th_test/_search {"size":0,"aggs":{"by_author":{"terms":{"field":"author"},"aggs":{ "top_post":{"top_hits":{"size":1,"sort":[{"views":{"order":"desc"}}],"_source":["title","views"]}} }}}}

验证:top_hits真实精确拿到了每位作者浏览量最高的那一篇,不是随便一篇

实测 · 本机 Elasticsearch 8.15.0 执行输出
alice doc_count=3 -> {'title': 'alice post 2 (most viewed)', 'views': 500}
bob   doc_count=2 -> {'title': 'bob post 1 (most viewed)', 'views': 300}

这组真实结果验证了top_hits最核心的用法:它本质上是一个可以嵌套在任何bucket聚合内部的metric聚合,真实在每个桶对应的那部分文档子集里,按指定的sort条件重新排序并真实取出前size条完整文档——本次实测alice的3篇文章里浏览量最高的真实是500那篇,bob的2篇里真实是300那篇,top_hits精确把这两篇"代表作"挑了出来,而不是简单取每组遇到的第一条或者最后一条。

真实验证top_hits内部的hits.total是否精确反映了这个桶真正的文档数量

代码POST /th_test/_search {"size":0,"aggs":{"by_author":{"terms":{"field":"author"},"aggs":{ "top_post":{"top_hits":{"size":1,"sort":[{"views":{"order":"desc"}}]}} }}}}

验证:top_hits.hits.total真实精确等于外层桶的doc_count,证明它看到的是完整的桶内文档集合

实测 · 本机执行输出
alice: doc_count=3  top_hits.hits.total={'value': 3, 'relation': 'eq'}   ← 真实完全一致
bob:   doc_count=2  top_hits.hits.total={'value': 2, 'relation': 'eq'}   ← 真实完全一致
这组真实对比排除了一个容易产生的疑虑:top_hits既然只返回size条文档,会不会它排序和挑选代表文档的过程也只是基于一个被截断的子集?本次实测证明并非如此——top_hits内部的hits.total真实精确等于terms聚合给这个桶统计出的doc_count,说明它真实是在这个桶对应的完整文档子集范围内做排序和挑选,只是最终返回的时候按size截断,不会出现"因为只看了桶内前几条文档就误判出代表作"这种真实的准确性问题。这也是top_hits相比本系列第59篇验证过的collapse更灵活的地方:collapse只能在顶层对命中结果做字段折叠,top_hits则真实可以嵌套在任意层级的bucket聚合内部,为每一个分组(哪怕是多字段组合、嵌套date_histogram这种复杂的分组结构)各自挑出符合业务排序规则的代表文档,用途真实比collapse更通用。

分步演示:top_hits从精确挑选代表文档到验证排序范围完整性的完整验证

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

top_hits在完整的桶内文档集合中精确排序挑选代表文档
这一步新验证的top_hits特性

    本文用到的工具

    • 本机真实部署的 Elasticsearch 8.15.0(单节点,tar包直接启动)
    • 真实的 top_hits 聚合与 terms 嵌套结构
    京ICP备2026067502号-1京公网安备11010502063487号