ElasticSearch-5-搜索相关性与自定义评分
1、简介
Elasticsearch 的核心功能是全文检索,而评分就是衡量查询条件与文档匹配相关度的机制。得分越高,代表文档与搜索词越相关,排名就越靠前。
核心要点:
-
基于 TF-IDF / BM25 算法:
-
TF(词频):一个查询词在某个文档中出现的次数越多,该文档的相关性得分可能越高。
-
IDF(逆文档频率):一个查询词在所有文档中出现的频率。如果某个词在所有文档中都很常见(如“的”、“是”),它的区分度就低,权重就小;反之,一个稀有词权重就高。
-
BM25:是 TF-IDF 的现代优化版本,ES 从 5.x 版本开始默认使用。它改进了长文档的权重处理,避免了长文档天然占优的问题,效果通常更好。
-
-
评分过程:
-
当你执行一个查询时,ES 会为每个匹配的文档计算一个
_score。 -
这个分数是一个正浮点数,是相对值,仅在同一查询的上下文中有意义。
-
你可以通过
explain=trueAPI 查看详细的评分计算过程,了解每个因子是如何贡献的。
-
- 1) 排序偏好:通过在搜索结果中给每个文档自定义评分,可以更好地满足搜索用户的排序偏好。
- 2) 特殊字段权重:通过给特定字段赋予更高的权重,可以让这些字段对搜索结果的影响更大。
- 3) 业务逻辑需求:根据业务需求,可以定义复杂的评分逻辑,使搜索结果更符合业务需求。
自定义评分广泛应用于需要复杂业务排序逻辑的场景,而不仅仅是文本相关性。例如:
-
电商搜索:
-
综合排序:相关性 + 销量权重 + 好评率 + 店铺等级 + 是否促销。
-
个性化:结合用户历史行为(如常购品牌、价格区间)提升相关商品排名。
-
运营需求:对赞助商品、新品、自营商品进行加权。
-
-
本地服务/OTA(在线旅游):
-
地理位置:酒店、餐厅的搜索,距离是极其重要的因素(使用衰减函数)。
-
多维排序:餐厅搜索 = 关键词匹配 + 距离衰减 + 评分 + 人均价格偏好 + 是否营业中。
-
2、以下是几种主要的自定义评分策略:
- Index Boost: 在索引层面修改相关性。
- boosting: 修改文档相关性。
- negative_boost: 降低相关性。
- function_score: 自定义评分。
- rescore_query:查询后二次打分。
POST my_index_*/_search { "query": { "term": { "field.keyword": { "value": "queryValue" } } }, "indices_boost": [ { "my_index_100a": 1.5 }, { "my_index_100b": 1.2 }, { "my_index_100c": 1 } ] }
给某些文档或查询条件一个权重因子,以提升或降低其重要性。
- 若boosting值为0~1,如0.2,代表降低评分。
- 若boosting值>1,如1.5,则代表提升评分。
实现方式:
- 在bool查询中使用boost参数。
- 在function_score中使用weight函数。
GET /blogs/_search { "query": { "bool": { "should": [ { "match": { "title": { "query": "apple,ipad", "boost": 4 } } }, { "match": { "content": { "query": "apple,ipad", "boost": 1 } } } ] } } }
3、negative_boost: 降低相关性
- negative_boost仅对查询中定义为negative的部分生效。
- 计算评分时,不修改boosting positive部分评分,而negative部分的评分则乘以negative_boost的值。
- negative_boost取值为0~1.0,如0.3。
{ "query": { "boosting": { "positive": { "match": { "title": "apple" } }, "negative": { "match": { "title": "pie" } }, "negative_boost": 0.5 } } }
-
匹配
positive查询的文档会正常计算得分。 -
同时匹配了
negative查询的文档,其得分会在原始得分的基础上乘以negative_boost(即0.5),从而降低得分。 -
只匹配
negative而不匹配positive的文档不会出现在结果中(因为boosting查询要求必须匹配positive部分)。
4、function_score: 自定义评分
function_score 的核心结构:{ "query": { "function_score": { "query": { "match_all": {} }, // 原始查询,得到基础文档集和原始 _score "functions": [ // 一个或多个“算分函数” { "filter": { ... }, // (可选)指定一个过滤器,仅对匹配的文档应用此函数 "weight": 2, // (常用函数)将原始分数乘以一个常数 "field_value_factor": { // (常用函数)用文档的某个数值字段值来影响分数 "field": "popularity", "factor": 1.2, "modifier": "log1p" }, "random_score": { ... }, // 随机函数,用于随机排序 "script_score": { // (最强大)自定义脚本完全控制分数计算 "source": "_score * doc['boost_field'].value" }, "exp": { ... }, // 衰减函数,如高斯衰减,用于距离、时间等 "gauss": { ... }, "linear": { ... } } ], "score_mode": "sum", // 多个函数分数如何合并?sum, multiply, avg, max, min... "boost_mode": "multiply" // 函数合并后的分数如何与原始 _score 结合?multiply, replace, sum... } } }
常用自定义评分方法示例:
1、加权(weight):简单粗暴,给某些文档直接乘个系数。
{ "function_score": { "query": { "match": { "title": "手机" } }, "functions": [ { "filter": { "term": { "brand": "苹果" } }, "weight": 1.5 } ], "score_mode": "sum", "boost_mode": "multiply" } }
2、字段值因子(field_value_factor):用文档的数值字段(如销量、热度)来提升分数。
{ "function_score": { "query": { "match": { "content": "编程" } }, "functions": [ { "field_value_factor": { "field": "view_count", // 使用 view_count 字段 "factor": 0.1, // 原始字段值先乘这个因子 "modifier": "log1p", // 然后进行 log(1 + value) 运算,防止高分值字段影响过大 "missing": 1 // 如果字段缺失,默认值 } } ], "boost_mode": "sum" // 原始分 + 处理后的 view_count 值 } }
3、脚本评分(script_score):终极武器,用 Painless 脚本编写任意复杂度的逻辑
{ "function_score": { "query": { "match": { "title": "酒店" } }, "functions": [ { "script_score": { "source": """ double score = _score; // 根据地理距离减分 if (doc['location'].size() > 0) { double distance = geoDistance(params.lat, params.lon, doc['location'].lat, doc['location'].lon); score *= 1 / (distance + 1); // 距离越远,分数越低 } // 根据价格加分(越便宜越好) score += (100 - doc['price'].value) * 0.01; // 根据用户偏好标签加分 for (String tag : params.user_preferred_tags) { if (doc['tags'].contains(tag)) { score += 10; } } return score; """, "params": { "lat": 39.909, "lon": 116.397, "user_preferred_tags": ["免费WiFi", "游泳池"] } } } ], "boost_mode": "replace" // 完全使用脚本计算的分数 } }

浙公网安备 33010602011771号