一、概要
Elasticsearch 的评分机制(Relevance Scoring)是其全文搜索能力的核心,它决定了文档与查询的匹配程度并按相关性排序返回结果。
二、评分机制基础
基本概念
- 目的:
- 相关性排序(Relevance Ranking)
- 将匹配文档按照与查询的相关性从高到低排序。
- 确保最相关的文档出现在结果列表顶部。
- 示例:搜索"智能手机"时,标题完全匹配的产品排在描述部分匹配的产品之前。
- 搜索质量优化
- 区分"勉强匹配"和"精确匹配"的文档。
- 识别文档中真正重要的匹配内容。
- 示例:匹配稀有词项的文档比匹配常见词项的文档得分更高。
- 相关性排序(Relevance Ranking)
- 特点:
- 无固定上限,分数越高相关性越强。
- 不同查询类型的评分计算方式不同。
- 任何搜索请求都会默认返回每个文档的评分,记录在响应的 _score 字段。
- 可通过 explain API 查看详细评分过程。
- 目的:
详细评分分析
- 使用 explain API 查看单个文档的详细评分计算过程:
响应包含详细解释:GET /your_index/_explain/1 { "query": { "match": { "title": "Elasticsearch" } } }
{ "_index": "your_index", "_id": "1", "matched": true, "explanation": { "value": 1.3862942, "description": "weight(title:elasticsearch in 0) [PerFieldSimilarity], result of:", "details": [ { "value": 1.3862942, "description": "score(freq=1.0), computed as boost * idf * tf from:", "details": [ { "value": 2.2, "description": "boost" }, { "value": 0.6931471, "description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:", "details": [...] }, { "value": 0.90909094, "description": "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:", "details": [...] } ] } ] } }
- 搜索时添加 explain 参数,为搜索结果中的所有文档返回评分解释:
GET /your_index/_search { "explain": true, "query": { "match": { "title": "Elasticsearch" } } }
- 使用 Profile API 分析查询执行的详细时间信息和评分计算:
响应包含详细的性能分析:GET /your_index/_search { "profile": true, "query": { "match": { "title": "Elasticsearch" } } }
{ "profile": { "shards": [ { "searches": [ { "query": [ { "type": "MatchQuery", "description": "title:elasticsearch", "time_in_nanos": 125000, "breakdown": {...}, "children": [...] } ], "rewrite_time": 42000, "collector": [...] } ] } ] } }
- 使用 explain API 查看单个文档的详细评分计算过程:
三、基础评分模型
TF-IDF 模型 (早期版本默认)
- 计算公式:score(q,d) = queryNorm(q) × coord(q,d) × ∑ ( tf(t in d) × idf(t)² × t.getBoost() × norm(t,d) )
- 核心组件:
- TF (Term Frequency):词项在文档中出现的频率
- 出现次数越多,得分越高。
- 计算公式:tf(t in d) = √frequency
- IDF (Inverse Document Frequency):词项在所有文档中的稀有程度
- 越稀有的词项权重越高。
- 计算公式:idf(t) = 1 + log(numDocs / (docFreq + 1))
- Field-length Norm:字段长度归一化
- 短字段匹配的权重更高。
- 计算公式:norm(d) = 1 / √numTerms
- TF (Term Frequency):词项在文档中出现的频率
BM25 评分模型(Elasticsearch 5.x 及以后)
- 成为默认算法
- 从 Elasticsearch 5.x 开始,BM25 取代 TF-IDF 成为默认评分算法。
- BM25 公式:score(D,Q) = ∑ IDF(qi) × (f(qi,D) × (k1 + 1)) / (f(qi,D) + k1 × (1 - b + b × |D| / avgdl))
- 核心参数:
- k1:控制词频饱和度的参数(默认1.2)。
- 值越小饱和度越快。
- b:控制字段长度影响的参数(默认0.75)
- 0=禁用长度归一化,1=完全归一化。
- k1:控制词频饱和度的参数(默认1.2)。
- 成为默认算法
BM25 优势
- 对短字段匹配更友好。
- 词频饱和更快(避免高频词主导结果)。
- 更符合现代信息检索需求。
四、评分组件深度解析
查询时评分因素
因素 描述 影响方式 词项权重 查询词项的重要性 可通过boost调整 协调因子 匹配查询条件的比例 匹配越多分数越高 查询归一化 复杂查询的平衡处理 长查询的分数调节 索引时评分因素
因素 存储位置 作用 文档频率 倒排索引 计算IDF 字段长度 norms 长度归一化 索引选项 index_options 控制存储信息量
五、影响评分的核心因素
TF-IDF(词频-逆文档频率)
- 词频(Term Frequency, TF):查询词在文档中出现的次数越多,得分越高。
- 逆文档频率(Inverse Document Frequency, IDF):查询词在所有文档中出现的频率越低(越稀有),得分越高。
字段长度归一化(Field-length Norm)
- 较短的字段中匹配到的词比长字段中匹配到的词权重更高。
- 例如:标题匹配比正文匹配得分更高。
查询提升(Boosting)
- 可以手动提升某些字段或查询条件的权重。
- 例如:
{ "query": { "multi_match": { "query": "搜索词", "fields": ["title^3", "content"] // title字段权重是content的3倍 } } }
协调因子(Coordination Factor)
- 考虑文档中匹配到的查询条件数量。
- 匹配到的条件越多,得分越高
文档权重(Document Boosting)
- 单个文档可以设置权重提升值。
- 在索引时通过_boost参数设置。
其他因素
- 索引时的norms设置(是否考虑字段长度)。
- index_options设置(存储哪些信息用于评分)。
- 查询时的tie_breaker参数(处理多条件查询)。
- 查询时使用的分析器(影响分词结果)。
六、自定义评分
Elasticsearch 提供了多种方式来实现自定义评分,使您能够超越默认的相关性算法,根据业务需求定制搜索结果的排序。以下是主要的自定义评分方法:
- Function Score Query
最强大的自定义评分工具,允许修改原始_score或完全替换它:{ "query": { "function_score": { "query": { "match": { "title": "elasticsearch" } }, "functions": [ { "filter": { "term": { "tags": "popular" } }, "weight": 2 }, { "script_score": { "script": { "source": "Math.log(doc['views'].value + 1)" } } } ], "score_mode": "sum", // 函数结果如何组合:sum, multiply, avg, max, min, first "boost_mode": "multiply" // 如何与原始分数组合:multiply, replace, sum, avg, max, min } } }
- 脚本评分 (Script Score)
使用Painless脚本实现完全自定义的评分逻辑:{ "query": { "script_score": { "query": { "match_all": {} }, "script": { "source": """ double score = 0; // 基于点赞数和收藏数计算评分 score += doc['likes'].value * 0.3; score += doc['favorites'].value * 0.7; // 新内容加分 long age = params.now - doc['publish_date'].value.toInstant().toEpochMilli(); score *= Math.max(0.1, 2 - age / (1000*60*60*24*30.0)); return score; """, "params": { "now": 1700000000000 } } } } }
- 字段值因子 (Field Value Factor)
使用文档中的字段值来调整评分:{ "query": { "function_score": { "query": { "match": { "content": "elasticsearch" } }, "field_value_factor": { "field": "popularity", "factor": 1.2, "modifier": "log1p", // none, log, log1p, log2p, ln, ln1p, ln2p, square, sqrt, reciprocal "missing": 1 }, "boost_mode": "multiply" } } }
- 衰减函数 (Decay Functions)
基于距离或时间的衰减评分:{ "query": { "function_score": { "query": { "match": { "name": "hotel" } }, "functions": [ { "gauss": { "location": { "origin": "40.715, -74.011", // 中心点 "scale": "2km", // 衰减距离 "offset": "500m", // 不衰减的范围 "decay": 0.5 // 衰减率 } } }, { "exp": { "publish_date": { "origin": "now", "scale": "30d", "offset": "7d", "decay": 0.5 } } } ], "score_mode": "multiply" } } }
- 自定义相似度算法
在索引映射中定义自定义相似度:{ "settings": { "index": { "similarity": { "custom_similarity": { "type": "BM25", "b": 0.75, "k1": 1.2 } } } }, "mappings": { "properties": { "title": { "type": "text", "similarity": "custom_similarity" } } } }
- 查询时提升 (Query-Time Boosting)
{ "query": { "bool": { "should": [ { "match": { "title": { "query": "elasticsearch", "boost": 3 } } }, { "match": { "content": { "query": "elasticsearch", "boost": 1 } } } ] } } }
七、固定评分
constant_score 是 Elasticsearch 中一种特殊的查询类型,它允许你为过滤条件分配一个固定的评分,而不是由 Elasticsearch 计算相关性评分。这在某些场景下非常有用,特别是当你只关心文档是否匹配而不需要相关性评分时。
- 基本语法
{ "query": { "constant_score": { "filter": { // 你的过滤条件 }, "boost": 1.0 // 可选,默认值为1.0 } } }
- 核心特点
- 固定评分:所有匹配的文档都会获得相同的评分。
- 性能优势:跳过了评分计算阶段,提高查询效率。
- 只包含 filter 上下文:不支持 query 上下文的条件。
- 典型使用场景
- 精确匹配过滤(不关心评分)
{ "query": { "constant_score": { "filter": { "term": { "status": "published" } } } } }
- 范围过滤
{ "query": { "constant_score": { "filter": { "range": { "price": { "gte": 100, "lte": 500 } } }, "boost": 1.5 } } }
- 组合多个过滤条件
{ "query": { "constant_score": { "filter": { "bool": { "must": [ { "term": { "category": "electronics" } }, { "range": { "stock": { "gt": 0 } } } ] } } } } }
- 调整 boost 值控制结果排序
{ "query": { "bool": { "should": [ { "constant_score": { "filter": { "term": { "priority": "high" } }, "boost": 3.0 } }, { "constant_score": { "filter": { "term": { "priority": "medium" } }, "boost": 2.0 } } ] } } }
- 精确匹配过滤(不关心评分)
八、优化评分策略
优化 Elasticsearch 评分策略是提高搜索结果相关性的关键。以下是系统化的优化方法和实践建议:
8.1 基础评分模型优化
- 选择合适的相似度算法
PUT /my_index { "settings": { "similarity": { "custom_bm25": { "type": "BM25", // 默认且推荐算法 "k1": 1.2, // 控制词频饱和度(默认1.2) "b": 0.75 // 控制字段长度归一化程度(0-1) } } }, "mappings": { "properties": { "content": { "type": "text", "similarity": "custom_bm25" } } } }
- 字段级别优化
- 重要字段提升:
{ "query": { "multi_match": { "query": "搜索词", "fields": ["title^3", "description^2", "content"] } } }
- 禁用不必要字段的评分:
{ "mappings": { "properties": { "metadata": { "type": "text", "norms": false // 禁用长度归一化 } } } }
- 重要字段提升:
8.2 业务导向的评分优化
- 业务权重集成
{ "query": { "function_score": { "query": { "match": { "product": "手机" } }, "functions": [ { "field_value_factor": { "field": "sales_volume", "modifier": "log1p", "factor": 0.1 } }, { "field_value_factor": { "field": "user_rating", "modifier": "sqrt", "factor": 1.2 } } ], "boost_mode": "sum" } } }
- 时间衰减策略
{ "query": { "function_score": { "query": { "match_all": {} }, "functions": [ { "exp": { "publish_date": { "origin": "now", "scale": "30d", "offset": "7d", "decay": 0.5 } } } ], "score_mode": "multiply" } } }
8.3 高级优化技术
- 个性化评分
{ "query": { "function_score": { "query": { "match": { "category": "电子产品" } }, "functions": [ { "script_score": { "script": { "source": """ // 基于用户偏好调整评分 double boost = 1.0; if(params.user_preferences.contains(doc['brand'].value)) { boost = 2.0; } return _score * boost; """, "params": { "user_preferences": ["苹果", "华为"] } } } } ] } } }