ElasticSearch multi_match查询如何忽略词项重复对评分的影响
当前使用的ElasticSearch查询语句如下:
"body": { "query": { "bool": { "minimum_should_match": 1, "should": [ { "multi_match": { "query": "iphone", "fields": [ "primary.titles^2", "primary.descriptions^1" ], "fuzziness": 1, "prefix_length": 5, "max_expansions": 25, "operator": "and" } } ], "must": [] } } }
异常现象:primary.titles字段值为iphone/iphone的文档评分远高于字段值为iphone的文档,需要在评分计算时忽略词项重复带来的评分加成。
ElasticSearch默认使用BM25评分算法,*词项频次(TF)*是核心评分因子之一:同一个词在单字段内出现次数越多,TF值越高,对应评分越高。
文本iphone/iphone经分词后会产出2个iphone词元,TF值为2;而文本iphone分词后仅产出1个iphone词元,TF值为1,因此前者评分更高。
根据是否允许修改索引配置、是否需要保留细粒度相关性评分能力,可选以下方案:
方案1:索引层配置
unique分词过滤器(长期最优方案)
在自定义分词器中加入unique类型的token filter,配置only_on_same_position: false,可以在分词阶段直接过滤掉同字段内重复的词元,从根源上避免重复词被计入TF统计,不会改变正常的BM25评分逻辑。
配置示例:PUT /你的业务索引名 { "settings": { "analysis": { "filter": { "dedup_term_filter": { "type": "unique", "only_on_same_position": false } }, "analyzer": { "title_dedup_analyzer": { "tokenizer": "standard", "filter": ["lowercase", "dedup_term_filter"] } } } }, "mappings": { "properties": { "primary": { "properties": { "titles": { "type": "text", "analyzer": "title_dedup_analyzer" } } } } } }注意:该配置需要重建索引后生效。
方案2:调整BM25相似度参数,压低词频权重(适合不想重建索引的场景)
BM25的k1参数直接控制词频对评分的影响程度,默认值为1.2:值越小,词频增长带来的评分涨幅越低;当k1设为0时,词频将完全不影响评分,只要字段包含查询词,TF部分得分固定。
配置示例:PUT /你的业务索引名/_mapping { "properties": { "primary": { "properties": { "titles": { "type": "text", "similarity": { "type": "BM25", "k1": 0, "b": 0.75 } } } } } }注意:如果业务场景需要保留合理的词频权重(比如长文本中核心关键词多次出现可适当提权),不要把k1直接设为0,可调整到0.2~0.5的区间,既不会让重复1-2次的词评分虚高,也能保留基础的词频评分能力。该配置对新增写入数据即时生效,存量数据需要段合并后才会完全生效。
方案3:查询层包装
constant_score,取消TF评分(仅改查询语句,无需调整索引)
如果不需要细粒度的文本相关性评分,只要命中查询词就给固定得分,可以用constant_score包装原multi_match查询,命中后直接返回固定boost值,完全不统计TF、IDF等评分因子。
查询改写示例:"body": { "query": { "bool": { "minimum_should_match": 1, "should": [ { "constant_score": { "filter": { "multi_match": { "query": "iphone", "fields": [ "primary.titles", "primary.descriptions" ], "fuzziness": 1, "prefix_length": 5, "max_expansions": 25, "operator": "and" } }, "boost": 2 } } ], "must": [] } } }注意:该方案会完全抛弃全文检索的细粒度评分逻辑,所有命中该子句的文档得分统一,仅适合不需要根据文本相关度做精细排序的场景。
内容的提问来源于stack exchange,提问作者LEVE

