如何按Elasticsearch中matched_queries的fooMatches值排序查询结果?
问题:如何按matched_queries中的fooMatches值排序Elasticsearch结果?
原查询语句
"query": { "bool": { "must": [ { "bool": { "_name": "fooMatches", "minimum_should_match":1, "should": [ { "terms": { "foo":["a"] } }, { "terms": { "foo":["b"] } }, { "terms": { "foo":["c"] } }, { "terms": { "foo":["d"] } } ] } }, // 其他多个子句 ] } }
查询结果示例
"hits": [ { "_score": 5, ... "matched_queries": { "fooMatches": 3 } } ]
需求说明
默认结果按_score排序,但需要改为优先按matched_queries.fooMatches的数值降序排序,当该数值相同时,再按原查询的_score降序排序。此前尝试的"sort": [{"fooMatches._score":"desc"}, "_score"]及变体均无效。
正确实现方式
由于matched_queries是查询执行后返回的元数据,无法直接在排序阶段引用,因此需要通过**脚本排序(script sort)**直接计算匹配的should子句数量,具体配置如下:
方案1:脚本排序(推荐)
这种方式不会修改原查询的_score,仅在排序阶段使用自定义逻辑,完全满足需求:
"sort": [ { "_script": { "type": "number", "script": { "source": """ int matchCount = 0; if (doc['foo'].contains('a')) matchCount++; if (doc['foo'].contains('b')) matchCount++; if (doc['foo'].contains('c')) matchCount++; if (doc['foo'].contains('d')) matchCount++; return matchCount; """ }, "order": "desc" } }, { "_score": "desc" } ]
脚本逻辑与原查询中fooMatches的should子句完全对应,直接统计foo字段匹配目标值的数量,以此作为首要排序依据,同分情况下按原得分排序。
方案2:通过function_score整合排序逻辑
如果需要将匹配数融入评分再排序,可以使用function_score,但会改变原_score的含义:
"query": { "function_score": { "query": { // 原查询的完整内容 }, "functions": [ { "script_score": { "script": { "source": """ int matchCount = 0; if (doc['foo'].contains('a')) matchCount++; if (doc['foo'].contains('b')) matchCount++; if (doc['foo'].contains('c')) matchCount++; if (doc['foo'].contains('d')) matchCount++; return matchCount; """ } } } ], "boost_mode": "sum" // 将匹配数与原得分叠加,也可使用"replace"完全替换 } }, "sort": [ { "_score": "desc" } ]
原写法无效的原因
fooMatches._score不是合法的排序字段路径:fooMatches是查询的自定义名称,并非文档的字段,Elasticsearch无法直接识别并获取对应数值,必须通过脚本直接计算匹配数量。
内容的提问来源于stack exchange,提问作者Matt Burland
相关产品推荐
相关产品推荐

