掌握ElasticSearch(七):相关性评分
文章目录
- 一、Elasticsearch的打分机制
- 1.TF-IDF
- TF-IDF 概述
- 基本公式
- 示例
- 2.BM25
- BM25 概述
- 配置参数
- 二、boosting调整打分
- 1. `match` 查询
- 2. `multi_match` 查询
- 3. `bool` 查询
- 4. `boosting` 查询
- 5.动态 Boosting
- 三、Elasticsearch的查询再打分策略
- 查询阶段(Query Phase)
- 取回和打分阶段(Fetch and Score Phase)
- 四、function_score定制得分
- 内置评分函数
- 基本语法
- 参数说明
- 五、explain API
- 基本用法
- 解释结果结构
- 示例
- 解释结果示例
- 解释结果的详细说明
- 常见用途
一、Elasticsearch的打分机制
Elasticsearch提供了一个强大的全文搜索能力,并且能够实时处理大量的数据。在 Elasticsearch 中,文档的打分(Scoring)机制是基于 TF/IDF 模型(Term Frequency/Inverse Document Frequency),以及 BM25(Best Matching 25)算法来计算文档的相关性。
1.TF-IDF
TF-IDF 概述
TF-IDF(Term Frequency-Inverse Document Frequency,词频-逆文档频率)是一种常用的统计方法,用于评估一个词对文档或语料库的重要程度。这种方法结合了两个指标:
- TF (Term Frequency, 词频):一个词在文档中出现的频率。
- IDF (Inverse Document Frequency, 逆文档频率):一个词在整个文档集合中出现的频率的倒数,用来衡量这个词的独特性。
基本公式
示例
假设有一个简单的索引,包含以下三个文档:
- 文档 A: “猫 狗 鱼”
- 文档 B: “猫 猫 猫 狗”
- 文档 C: “狗 鱼”
如果用户搜索关键词 “猫”,Elasticsearch 会计算每个文档中 “猫” 的 TF-IDF 分数,并根据这些分数对文档进行排序。
- 文档 A 中 “猫” 的 TF-IDF 分数:
- 文档 B 中 “猫” 的 TF-IDF 分数:
- 文档 C 中 “猫” 的 TF-IDF 分数:
根据这些分数,文档 B 会被排在文档 A 之前,因为 “猫” 在文档 B 中的 TF-IDF 分数更高。
2.BM25
BM25(Best Matching 25)是一种广泛使用的排名函数,用于信息检索领域,特别是在搜索引擎中。相比传统的 TF-IDF 方法,BM25 更加准确地反映了文档与查询之间的相关性,尤其是在处理短查询和长文档时表现更佳。
BM25 概述
BM25 的评分公式如下:
其中:
配置参数
- 可以通过设置索引的
index.similarity.default
参数来调整 BM25 的参数:PUT /my-index { "settings": { "index": { "similarity": { "default": { "type": "BM25", "k1": 1.2, "b": 0.75 } } } } }
二、boosting调整打分
在 Elasticsearch 中,boosting
是一种用于调整查询中各个部分权重的技术。通过 boosting
,可以提高或降低某些查询条件的重要性,从而影响最终的搜索结果排序。boosting
可以应用于单个查询子句,也可以应用于整个查询。
- Boost 值:一个正数,表示查询子句的重要性。默认值为 1.0。值越大,查询子句在最终评分中的权重越高。
- Positive Query:主查询,用于匹配文档并计算基础评分。
- Negative Query:可选的负查询,用于减少某些文档的评分。
1. match
查询
match
查询是最常见的全文搜索查询,可以使用 boost
参数来调整匹配项的权重。
GET /my-index/_search
{
"query": {
"match": {
"title": {
"query": "Elasticsearch",
"boost": 2.0
}
}
}
}
在这个例子中,标题中包含 “Elasticsearch” 的文档的评分将乘以 2.0。
2. multi_match
查询
multi_match
查询允许在多个字段中进行搜索,并可以为每个字段设置不同的 boost
值。
GET /my-index/_search
{
"query": {
"multi_match": {
"query": "Elasticsearch",
"fields": [
"title^2.0",
"content^1.5",
"tags^1.0"
]
}
}
}
在这个例子中,标题字段的匹配项权重是内容字段的两倍,标签字段的匹配项权重最低。
3. bool
查询
bool
查询允许组合多个查询子句,并可以为每个子句设置 boost
值。
GET /my-index/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"title": {
"query": "Elasticsearch",
"boost": 2.0
}
}
},
{
"match": {
"content": {
"query": "Elasticsearch",
"boost": 1.5
}
}
}
]
}
}
}
在这个例子中,标题字段的匹配项权重是内容字段的 1.33 倍。
4. boosting
查询
boosting
查询允许你指定一个正查询和一个负查询,并通过 negative_boost
参数调整负查询的影响。
GET /my-index/_search
{
"query": {
"boosting": {
"positive": {
"match": {
"title": "Elasticsearch"
}
},
"negative": {
"match": {
"content": "outdated"
}
},
"negative_boost": 0.2
}
}
}
在这个例子中,标题中包含 “Elasticsearch” 的文档会被优先返回,但如果内容中包含 “outdated”,这些文档的评分会被显著降低。
5.动态 Boosting
除了静态设置 boost
值,Elasticsearch 还支持动态调整 boost
值。这可以通过使用脚本评分 (script_score
) 来实现。
GET /my-index/_search
{
"query": {
"script_score": {
"query": {
"match": {
"title": "Elasticsearch"
}
},
"script": {
"source": "_score * doc['popularity'].value"
}
}
}
}
在这个例子中,文档的最终评分是原始评分乘以 popularity
字段的值。
三、Elasticsearch的查询再打分策略
在 Elasticsearch 中,“查询后再打分”(Query Then Fetch and Score)是一种优化查询性能的策略。这种策略分为两个阶段:首先是查询阶段(Query Phase),然后是取回和打分阶段(Fetch and Score Phase)。下面详细介绍这两个阶段的工作原理和优化效果。
查询阶段(Query Phase)
-
分布式查询:
- 当用户提交一个查询请求时,Elasticsearch 会将查询请求广播到所有相关的分片(shards)。
- 每个分片独立执行查询,并生成一个初步的匹配文档列表(通常是前 K 个文档)。
-
局部评分:
- 每个分片会对匹配的文档进行初步评分,计算出每个文档的相关性得分。
- 这些评分是基于分片内的数据进行的,因此可能会有一些偏差。
-
汇总结果:
- 每个分片将初步评分的前 K 个文档的结果发送回协调节点(coordinating node)。
- 协调节点收集所有分片的结果,并进行全局排序,选出最终的前 N 个文档。
取回和打分阶段(Fetch and Score Phase)
-
文档取回:
- 协调节点从每个分片中取回初步筛选出的文档的完整内容。
- 这一步骤是为了获取文档的详细信息,以便进行进一步的评分和处理。
-
全局评分:
- 协调节点对取回的文档进行全局评分。这一步骤可能包括重新计算评分、应用额外的评分因子(如动态 boosting)等。
- 全局评分确保了最终结果的准确性,因为它考虑了所有分片的数据。
-
最终排序:
- 协调节点根据全局评分对文档进行最终排序,生成最终的搜索结果。
- 最终结果按评分高低返回给用户。
- 优化效果:
-
减少网络传输:
- 通过在每个分片上进行初步筛选和评分,减少了需要传输的文档数量,从而降低了网络带宽的消耗。
-
提高响应速度:
- 分布式查询和并行处理提高了查询的响应速度,尤其是在处理大规模数据集时。
-
减少资源消耗:
- 只有初步筛选出的文档才会被取回和重新评分,减少了计算资源的消耗。
“查询后再打分”策略通过分阶段处理查询请求,有效地提高了查询性能和响应速度。通过在分片上进行初步筛选和评分,减少了网络传输和资源消耗,确保了最终结果的准确性和可靠性。这种策略特别适合处理大规模数据集和复杂查询场景。
四、function_score定制得分
function_score
查询是 Elasticsearch 中一种强大的工具,用于在查询过程中动态地调整文档的评分。通过 function_score
,我们可以根据自定义的评分函数来修改文档的默认评分,从而更精细地控制搜索结果的排序。
- 评分函数:用于计算文档的新评分的函数。Elasticsearch 提供了多种内置的评分函数,也可以使用自定义脚本。
- 过滤器:用于限制哪些文档应该应用评分函数。
- Boost 模式:定义如何将评分函数的结果与默认评分相结合。
内置评分函数
Elasticsearch 提供了多种内置的评分函数,包括但不限于:
weight
:简单地为文档添加一个固定的权重。random_score
:为文档分配一个随机评分。field_value_factor
:根据文档中的某个字段值来调整评分。linear
:线性插值函数。exp
:指数衰减函数。gauss
:高斯衰减函数。script_score
:使用自定义脚本计算评分。
基本语法
GET /my-index/_search
{
"query": {
"function_score": {
"query": {
"match": { "title": "Elasticsearch" }
},
"functions": [
{
"filter": { "term": { "status": "active" } },
"weight": 2
},
{
"field_value_factor": {
"field": "popularity",
"factor": 1.2,
"modifier": "sqrt",
"missing": 1
}
}
],
"boost_mode": "multiply",
"score_mode": "sum",
"max_boost": 10,
"min_score": 1
}
}
}
参数说明
query
:基础查询,用于匹配文档并计算初始评分。functions
:一个数组,包含一个或多个评分函数及其过滤器。boost_mode
:定义如何将评分函数的结果与默认评分相结合。可选值包括:multiply
:乘法模式,将评分函数的结果乘以默认评分。replace
:替换模式,直接使用评分函数的结果作为最终评分。sum
:求和模式,将评分函数的结果加到默认评分上。avg
:平均模式,取评分函数的结果和默认评分的平均值。max
:最大值模式,取评分函数的结果和默认评分的最大值。min
:最小值模式,取评分函数的结果和默认评分的最小值。
score_mode
:定义如何将多个评分函数的结果组合在一起。可选值包括:multiply
:乘法模式。sum
:求和模式。avg
:平均模式。first
:使用第一个评分函数的结果。max
:最大值模式。min
:最小值模式。
max_boost
:设置评分的最大值。min_score
:设置评分的最小值,低于此值的文档将被过滤掉。
五、explain API
Elasticsearch 的 explain
API 用于获取关于特定文档为何被查询命中的详细信息,包括评分计算的细节。这对于调试和优化查询非常有用,可以帮助我们理解查询是如何工作的,以及为什么某些文档的评分高于其他文档。
- 解释结果:
explain
返回一个详细的 JSON 结构,描述了查询的每个部分如何影响文档的评分。 - 评分细节:包括每个评分组件的值、匹配条件、以及是否满足这些条件。
- 调试工具:帮助开发者和管理员理解查询的内部工作原理,从而优化查询性能和结果质量。
基本用法
要使用 explain
API,需要指定索引名称、文档 ID 和查询条件。以下是一个基本示例:
GET /my-index/_explain/1
{
"query": {
"match": {
"title": "Elasticsearch"
}
}
}
在这个例子中,my-index
是索引名称,1
是文档 ID,查询条件是匹配 title
字段中包含 “Elasticsearch” 的文档。
解释结果结构
explain
API 返回的 JSON 结构包含多个字段,以下是一些主要字段的解释:
_index
:文档所在的索引名称。_type
:文档的类型(在 Elasticsearch 7.x 及以上版本中,类型已被移除)。_id
:文档的 ID。matched
:布尔值,表示文档是否匹配查询。explanation
:包含评分计算的详细信息。value
:文档的最终评分。description
:评分计算的描述。details
:包含子评分组件的详细信息。
示例
假设我们有一个索引 my-index
,包含以下文档:
PUT /my-index/_doc/1
{
"title": "Elasticsearch in Action",
"content": "Learn how to use Elasticsearch effectively.",
"popularity": 50
}
PUT /my-index/_doc/2
{
"title": "Introduction to Search Engines",
"content": "An overview of search engines and their components.",
"popularity": 30
}
我们执行一个 explain
请求来查看文档 1
为何被查询命中:
GET /my-index/_explain/1
{
"query": {
"match": {
"title": "Elasticsearch"
}
}
}
解释结果示例
{
"_index": "my-index",
"_type": "_doc",
"_id": "1",
"matched": true,
"explanation": {
"value": 1.2038395,
"description": "weight(title:Elasticsearch in 0) [PerFieldSimilarity], result of:",
"details": [
{
"value": 1.2038395,
"description": "score(doc=0,freq=1.0 = termFreq=1.0\n), product of:",
"details": [
{
"value": 0.6931472,
"description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
"details": [
{
"value": 1.0,
"description": "docFreq",
"details": []
},
{
"value": 2.0,
"description": "docCount",
"details": []
}
]
},
{
"value": 1.7364818,
"description": "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:",
"details": [
{
"value": 1.0,
"description": "freq, occurrences of term within document",
"details": []
},
{
"value": 1.2,
"description": "k1, term saturation parameter",
"details": []
},
{
"value": 0.75,
"description": "b, length normalization parameter",
"details": []
},
{
"value": 16.0,
"description": "dl, length of field",
"details": []
},
{
"value": 16.0,
"description": "avgdl, average length of field",
"details": []
}
]
}
]
}
]
}
}
解释结果的详细说明
_index
:文档所在的索引名称,这里是my-index
。_type
:文档的类型,这里是_doc
。_id
:文档的 ID,这里是1
。matched
:布尔值,表示文档是否匹配查询,这里是true
。explanation
:value
:文档的最终评分,这里是1.2038395
。description
:评分计算的描述,这里是weight(title:Elasticsearch in 0) [PerFieldSimilarity], result of:
。details
:包含子评分组件的详细信息,例如idf
和tf
的计算过程。
常见用途
- 调试查询:帮助我们理解为什么某些文档被匹配,而其他文档没有被匹配。
- 优化评分:通过查看评分计算的细节,可以调整查询参数或评分函数,以优化搜索结果的质量。
- 性能分析:了解查询的性能瓶颈,优化查询性能。