Elasticsearch多索引搜索如何按索引分组返回指定条数结果
Elasticsearch跨索引分组返回指定条数结果实现方案
方案选型
不要用nested查询、joining queries,这类查询是用来处理文档嵌套、跨文档关联关系的,不适配当前场景。正确实现方式是使用terms聚合搭配top_hits子聚合,基于ES内置的_index元字段做分组,每个分组下返回指定数量的匹配文档即可。
查询语句示例
以下示例实现跨index1、index2搜索,每个索引最多返回3条匹配结果,你可以根据自己的业务替换查询条件、索引列表:
GET /index1,index2/_search { "query": { // 替换为实际业务查询逻辑,支持match、multi_match、term等所有ES查询语法 "multi_match": { "query": "搜索关键词", "fields": ["title", "content"] } }, "size": 0, "aggs": { "group_by_index": { "terms": { "field": "_index", "size": 10 // 配置为实际查询覆盖的索引总数量,避免索引数超过默认阈值时分组被截断 }, "aggs": { "top_docs": { "top_hits": { "size": 3, // 每个索引返回的结果条数 "sort": [ {"_score": {"order": "desc"}} // 配置每个索引内结果的排序规则,默认按匹配度倒排 ], "_source": ["title", "content", "update_time"] // 按需指定返回字段,减少冗余数据传输 } } } } } }
返回结果转换
ES返回的聚合结果以桶为单位组织,每个桶对应一个索引的结果集,你只需要在业务代码中做一层简单转换,就能得到你期望的结构:
- 遍历返回结果中
aggregations.group_by_index.buckets数组 - 每个桶的
key字段值就是索引名 - 每个桶内
top_docs.hits.hits数组就是该索引下的匹配文档,取每个元素的_source字段(需要文档ID、匹配分的话取对应字段即可)作为条目值
ES原始返回的聚合结构参考:
{ "hits": { "total": {"value": 20}, "hits": [] // 因为全局size设为0,这里是空数组 }, "aggregations": { "group_by_index": { "buckets": [ { "key": "index1", "doc_count": 12, "top_docs": { "hits": { "hits": [ {"_id": "1", "_score": 9.2, "_source": {"title": "test1"}}, {"_id": "2", "_score": 8.7, "_source": {"title": "test2"}}, {"_id": "3", "_score": 8.1, "_source": {"title": "test3"}} ] } } }, { "key": "index2", "doc_count": 8, "top_docs": { "hits": { "hits": [ {"_id": "a", "_score": 9.5, "_source": {"title": "testa"}}, {"_id": "b", "_score": 8.9, "_source": {"title": "testb"}}, {"_id": "c", "_score": 8.3, "_source": {"title": "testc"}} ] } } } ] } } }
注意事项
- 如果用通配符匹配动态索引(比如
biz-*匹配所有业务前缀索引),需要把terms聚合的size参数设置为大于预期匹配到的索引总数,ES默认最多返回10个分组,超过的会被截断。 top_hits的size不建议设置过大,单分组返回条目数建议控制在100以内,避免单次查询数据量过高影响集群性能。- 全局的
from/size分页参数是对全量命中结果生效,无法实现每个索引分组内的分页,如果需要分组内分页,可以在top_hits内部配置from参数实现简单分页,深度分页场景建议结合search_after逻辑改造。
内容的提问来源于stack exchange,提问作者Paris
相关产品推荐
相关产品推荐

