Elasticsearch:了解和解决文档更新后 Elasticsearch 分数的变化

news/2024/11/16 13:32:03/

问题

问卷中有如下这样的文档,开发者想通过 match query 搜索这些文档来使用分数。

POST sample-index-test/_doc/1
{"first_name": "James","last_name" : "Osaka"
}

以下是对上述文档的示例查询:

GET sample-index-test/_explain/1
{"query": {"match": {"first_name": "James"}}
}

上述命令给出来的结果是:

{"_index": "sample-index-test","_id": "1","matched": true,"explanation": {"value": 0.2876821,"description": "weight(first_name:james in 0) [PerFieldSimilarity], result of:","details": [{"value": 0.2876821,"description": "score(freq=1.0), computed as boost * idf * tf from:","details": [{"value": 2.2,"description": "boost","details": []},{"value": 0.2876821,"description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:","details": [{"value": 1,"description": "n, number of documents containing term","details": []},{"value": 1,"description": "N, total number of documents with field","details": []}]},{"value": 0.45454544,"description": "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:","details": [{"value": 1,"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": 1,"description": "dl, length of field","details": []},{"value": 1,"description": "avgdl, average length of field","details": []}]}]}]}
}

如你所知,Elasticsearch 根据相关性对文档进行评分。 在为该文档建立索引后,让我们现在搜索索引。我们目前只有一份关于该索引的文档。

GET sample-index-test/_search
{"query": {"match": {"first_name": "James"}}
}

搜索后,你将看到以下结果:

{"took": 0,"timed_out": false,"_shards": {"total": 1,"successful": 1,"skipped": 0,"failed": 0},"hits": {"total": {"value": 1,"relation": "eq"},"max_score": 0.2876821,"hits": [{"_index": "sample-index-test","_id": "1","_score": 0.2876821,"_source": {"first_name": "James","last_name": "Osaka"}}]}
}

我想提请你注意结果的 _score 字段。 如你所见,我们文档的 _score 值为 0.2876821 。 例如,当你多次更新文档时,假设我们使用以下请求更新了记录 10 次:

POST sample-index-test/_update/1
{"script" : "ctx._source.first_name = 'James'; ctx._source.last_name = 'Cena';"
}或者 POST sample-index-test/_doc/1
{"first_name": "James","last_name" : "Cena"
}

不会有任何添加到索引中。 我们又有了一份文件,没有了。 我们刚刚更新了文档的 last_name 字段。 让我们再次进行精确搜索并尝试查看结果:

GET sample-index-test/_search
{"query": {"match": {"first_name": "James"}}
}

上面的命令显示的结果是:

{"took": 0,"timed_out": false,"_shards": {"total": 1,"successful": 1,"skipped": 0,"failed": 0},"hits": {"total": {"value": 1,"relation": "eq"},"max_score": 0.046520013,"hits": [{"_index": "sample-index-test","_id": "1","_score": 0.046520013,"_source": {"first_name": "James","last_name": "Cena"}}]}
}

正如你在此处看到的,分数发生了变化。 该文档的分数现在为 0.046520013 。 但根据 TF/IDF 计算,我们需要看到与我们的第一个搜索响应相同的分数。 因为当我们将它与文档的第一个状态进行比较时,没有任何变化。 即使我没有更改 first name 字段,我也只是更改了 last_name 字段并继续搜索 first_name 。 让我们对 _explain 端点进行更多挖掘。

GET sample-index-test/_explain/1
{"query": {"match": {"first_name": "James"}}
}

Explain API 端点将为查询和特定文档计算得分解释。 上述请求的结果如下所示:

{"_index": "sample-index-test","_id": "1","matched": true,"explanation": {"value": 0.046520013,"description": "weight(first_name:james in 0) [PerFieldSimilarity], result of:","details": [{"value": 0.046520013,"description": "score(freq=1.0), computed as boost * idf * tf from:","details": [{"value": 2.2,"description": "boost","details": []},{"value": 0.046520017,"description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:","details": [{"value": 10,"description": "n, number of documents containing term","details": []},{"value": 10,"description": "N, total number of documents with field","details": []}]},{"value": 0.45454544,"description": "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:","details": [{"value": 1,"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": 1,"description": "dl, length of field","details": []},{"value": 1,"description": "avgdl, average length of field","details": []}]}]}]}
}

去掉一些对我们来说是可选的部分。 现在让我们关注 IDF 计算。 如你所知,反向文档频率(Inverse Document Frequency)查看一个词在语料库中的常见(或不常见)程度。 这意味着我们将使用索引中的文档数来计算 IDF。有关 IDF 的更多知识,请阅读文章 “Elasticsearch:分布式计分”。

idf, computed as log(1 + (N - n + 0.5) / (n + 0.5))

正如你在上面看到的,我们使用的是文档总数,但问题是我们在索引中有一个文档,但它显示的是 10。

              {"value": 10,"description": "n, number of documents containing term","details": []},{"value": 10,"description": "N, total number of documents with field","details": []}

因此,如果你使用此分数来计算其他服务的内容,这就是问题所在。

为什么会这样?

Elasticsearch 使用 Lucene 并将所有文档存储在段中。 段(segment)是不可变的,文档更新操作有两步过程。 更新文档时,将创建一个新文档,并将旧文档标记为已删除。 所以,当你在 Elasticsearch 索引中创建第一个文档时,Elasticsearch 会将它保存在一个段中,并且只有一个文档。 然后你更新同一个文档 10 次; 在任何更新操作中,Elasticsearch 都会在一个段中创建另一个文档,并将最旧的文档标记为已删除。 但是当你搜索索引时,你会从段中找到最新的文档状态。 暂时删除的文档数量为10。你会再次搜索到文档的最新状态,但 Elasticsearch 会继续在内部统计它们以进行IDF 计算。 因此,每次更新后,“the number of documents with field” 和 “number of documents containing term” 都会发生变化。

解决方案

如你所知,如果你知道什么是段,这个问题会在一段时间后自行解决。 所以,如果你想自己做这件事而不等待,你需要使用 _forcemerge。 我需要在这里放一个来自 Elasticsearch 文档的解释。在我们稍微等一段时间后,我们再去搜索,我们将会看到最终的分数和我们刚开始搜索的结果是一样的。

合并通过将其中的一些合并在一起来减少每个分片中的段数,并且还释放已删除文档所使用的空间。 合并通常会自动发生,但有时手动触发合并很有用。
我们建议只强制合并只读索引(意味着索引不再接收写入)。

为了对我们的索引执行 _forcemerge,我们使用了以下请求:

POST sample-index-test/_forcemerge

根据你的索引大小,此请求可能需要一些时间,你可以通过在 Kibana 上执行以下请求来完成任务:

GET _tasks?actions=*forcemerge*&detailed

另一种方法就是等待。 Elasticsearch 还有一个调度程序和合并策略来自动合并段。 在使用强制合并之前,我建议仔细阅读相关的官方文档。

最后,还有一个索引生命周期操作,用于使用策略执行强制合并操作。 根据你的逻辑,你可以使用不同的解决方案来获得更好的搜索评分结果。


http://www.ppmy.cn/news/84074.html

相关文章

​LeetCode解法汇总2451. 差值数组不同的字符串

目录链接: 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目: https://github.com/September26/java-algorithms 原题链接:力扣 描述: 给你一个字符串数组 words ,每一个字符串长度都相同,令所…

深度学习神经网络学习笔记-多模态方向-11-Deep Voice: Real-time Neural Text-to-Speech

摘要 本文提出Deep Voice,一种完全由深度神经网络构建的生产质量文本到语音系统。Deep Voice为真正的端到端神经语音合成奠定了基础。该系统由五个主要的构建模块组成:用于定位音素边界的分割模型、字素到音素的转换模型、音素时长预测模型、基频预测模型和音频合成…

SpringBoot Controller层传入的参数进行解密

一、 应用场景 当和第三方应用对接系统的时候, 可能别人的参数加密方式和我们的不相同,那就需要和对方沟通好他们的接口参数是如何加密的,达成一致后才方便后续的工作开展。 二、示例说明 采用Springboot 项目开发,先在compone…

10 工具Bootchart的使用(windows)

Bootchart的使用方法(windows) 下载bootchart.jar并拷贝到windows, 然后保证windows也安装了open jdk 1.8; 下载地址:https://download.csdn.net/download/Johnny2004/87807973 打开设备开机启动bootchart的开关: adb shell touch /data/boo…

微信小程序---使用云数据库实现登录功能

实现效果 在数据库找不到登录信息时弹出提示框 一、开通云开发平台并创建数据表 进入微信官方文档按步骤操作即可 二、登录界面及样式 login.wxml如下&#xff1a; <view id"total"> <image src"../../images/user_cog_green.png"></im…

18-02 数据库设计核心要点

概念结构设计 通过对用户需求进行综合、归纳和抽象&#xff0c;形成独立于具体数据库管理系统的概念模型把需求分析阶段得到的应用需求&#xff0c;抽象成概念模型连接现实世界和信息世界的桥梁好的概念结构设计 能真实、充分的反映现实世界要易于理解要易于修改 基本概念 …

【Linux高级 I/O(4)】异步 IO实例及其优化(全文代码)

异步 I/O 在 I/O 多路复用中&#xff0c;进程通过系统调用 select()或 poll()来主动查询文件描述符上是否可以执行 I/O 操作。 而在异步 I/O 中&#xff0c;当文件描述符上可以执行 I/O 操作时&#xff0c;进程可以请求内核为自己发送一个信号。之后进程就可以执行任何其它…

AIGC和ChatGPT推进百度、阿里、腾讯、华为大模型技术创新

AIGC | PC集群 | PC Farm | GPU服务器 生成式AI | Stable Diffusion | ChatGPT 2022 年 12 月&#xff0c;OpenAI 推出了 ChatGPT&#xff0c;这是一种高性能计算的大型语言生成模型。它的出现推动了人机对话技术的发展&#xff0c;并在网络上引起了极大的关注。目前&#xff…