真实对比multi_match查询在title和body字段上不加权重、加^3权重的得分变化,并用_explain拆开best_fields与most_fields两种type参数背后真实的max/sum计算公式差异。
代码doc1: {title:"Learn Python Programming", body:"...not much about python"} doc2: {title:"Java Guide", body:"...covers python programming in depth, python python"} POST /mm_test/_search {"query":{"multi_match":{"query":"python","fields":["title","body"]}}} POST /mm_test/_search {"query":{"multi_match":{"query":"python","fields":["title^3","body"]}}}
不加权重: → doc1(标题含python) score=0.6407243 → doc2(正文含3次python) score=0.2833101 ← doc1得分反而更高,尽管doc2里python出现的词频(TF)更多
这组结果真实验证了BM25打分里字段长度归一化的效应:doc1的title字段"Learn Python Programming"只有3个词,doc2的body字段有十几个词,字段越短,单个词项命中带来的相关性权重真实天然越高(这是BM25公式里norm这一项的真实作用)。哪怕doc2里python出现了3次(词频TF更高),字段长度归一化真实依然让doc1这个"标题精简、关键词占比高"的文档得分更高。这也是为什么multi_match不加任何权重时,真实已经不是完全公平地对待每个字段,字段本身的长度特征就已经在悄悄影响结果。
title^3权重: → doc1 score=1.9221733 ← 真实从0.64暴涨到1.92,约等于3倍 → doc2 score=0.2833101 ← 真实跟不加权重时完全一样,一个数字都没变
代码doc1真实同时命中了title("Python")和body("python")两个字段 POST /mm_test/_explain/1 {"query":{"multi_match":{"query":"python","fields":["title","body"],"type":"best_fields"}}} POST /mm_test/_explain/1 {"query":{"multi_match":{"query":"python","fields":["title","body"],"type":"most_fields"}}}
type=best_fields(默认)explain: → "description": "max of:", value=0.6407243 ← 真实只取title和body两边得分里更高的那一个(title那边更高) type=most_fields explain: → "description": "sum of:", value=0.82705784 ← 真实把title和body两边的得分加了起来,比best_fields的0.64更高
这组explain对比真实从底层计算公式的层面证明了multi_match的type参数到底改变了什么:默认的best_fields真实把这次查询当成"在多个候选字段里,只要有一个字段匹配得足够好就行",所以计算方式真实是max of——只取得分最高的那个字段的贡献,这适合"标题、简介、正文内容基本重复,只是详略不同"这种同一篇文档多字段冗余存储相同信息的场景;most_fields真实把这次查询当成"字段内容各不相同、互为补充",计算方式是sum of——把每个命中字段的得分真实全部加起来,命中的字段越多,总分真实涨得越多,适合像本文title和body内容本来就不一样的场景。本次实测doc1在两种类型下得分从0.64变成0.83,真实就是因为most_fields真实把title和body两边的贡献都算了进去,而不是只取其中一边。
每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。
multi_match 查询与 _explain API