Elasticsearch跨多索引查询如何实现结果全局统一排序
问题复现
当前使用的查询DSL:
{ "query": { "bool": { "must": [ { "match": { "content": "Netherlands" } } ] } }, "sort": [ { "file.created": { "order": "asc" } } ] }
查询请求地址:
http://localhost:9200/trial2,trial3/_doc/_search?pretty
实际返回结果仅在单个索引内部有序:先返回trial2索引内排好序的结果,再返回trial3索引内排好序的结果,没有实现跨索引全局排序。预期效果为按file.created日期字段跨所有索引全局排序,文档排序不受URL中索引的排列顺序影响。
问题原因
这个问题90%以上的诱因是两个索引中file.created字段的映射配置不一致:比如trial2中该字段是date类型,trial3中该字段是text/keyword/long等其他类型,或是两个索引中该日期字段的format定义不兼容。这种场景下Elasticsearch跨索引检索时,不会对类型不统一的排序字段做全局归并排序,只会按URL里的索引顺序,分别返回每个索引内部排好序的结果。
剩下小概率场景是查询时自定义了路由规则、或是配置了索引级别的index_boost改写了分片召回优先级,基本不会遇到。
解决方法
1. 统一跨索引排序字段映射(优先选择,长期稳定)
- 先分别查询两个索引的字段映射,确认类型差异:
# 查询trial2的file.created字段映射 curl http://localhost:9200/trial2/_mapping/field/file.created # 查询trial3的file.created字段映射 curl http://localhost:9200/trial3/_mapping/field/file.created
- 将两个索引的
file.created字段配置修改为完全一致的date类型,推荐通用格式配置:
"file.created": { "type": "date", "format": "strict_date_optional_time||epoch_millis" }
- 对存量历史数据执行
_update_by_query重算字段索引值,确保所有文档的该字段值符合统一日期格式,之后再执行多索引查询就能得到全局有序的结果。
2. 临时兼容方案(无需修改索引映射)
如果暂时无法调整索引映射,可以在排序规则中添加unmapped_type参数,强制Elasticsearch对没有对应映射、或映射类型不匹配的索引,统一按指定类型计算排序值,修改后的DSL如下:
{ "query": { "bool": { "must": [ { "match": { "content": "Netherlands" } } ] } }, "sort": [ { "file.created": { "order": "asc", "unmapped_type": "date" } } ] }
注意:这个方案只是临时兼容,如果两个索引中file.created的实际存储值格式差异过大(比如一个存标准日期字符串,一个存非日期的普通文本),还是会出现排序值计算错误,长期使用还是建议统一字段映射。
3. 结果校验
配置完成后可以通过返回结果中每个文档自带的sort字段值校验排序是否生效:全局排序生效时,所有文档sort数组里的排序值会严格按照你指定的升序/降序规则排列,不会出现两个索引的排序值整体断层的情况。
内容的提问来源于stack exchange,提问作者DennisKip

