目录
介绍
目前,我们一直在讨论改进 Elasticsearch自动完成功能。在大多数情况下,这就足够了。然而,在我们的案例中,事情并没有那么乐观,我们将看看为什么。
设置
在我们的系统中,我们为每个索引提供单个文档类型。由于我们的系统中有很多类型,并且最终用户可以更改类型的数量及其映射,因此我们有不可预测的索引数量。目前,这个数字有17个不同的指数。当然,文档并非在所有类型中分布均匀,这意味着某些索引比其他索引包含更多的记录。
项目的主要功能是能够跨索引的逻辑组执行搜索。这一点以及跨索引的数据倾斜会导致一些相关性影响,当我们详细讨论我们的问题时,这些影响对您来说将是显而易见的。
另一件值得一提的事情是,用户搜索查询相当具体。我们项目的目的是剖析大量文档以获得一两个相关文档。因此,我们不希望像“Ukraine”这样的模糊查询可能会产生数千个文档。当我们继续提出解决方案时,这将很重要。
衡量相似性的标准方法
默认情况下,Elasticsearch使用BM25相似性对搜索结果进行排名。有三个主要因素决定了文档的分数:
- 术语频率(TF) — 搜索词在文档中搜索的字段中出现的次数越多,该文档的相关性就越高。
- 反向文档频率(IDF) — 我们正在搜索的字段中包含搜索词的文档越多,该词的重要性就越低。
- 字段长度 — 如果文档在非常短(即包含几个单词)的字段中包含搜索词,则它比在很长的字段(即包含许多单词)中包含搜索词的文档更可能相关。
让我们更深入地了解IDF。反向文档频率是考虑稀有全局单词的计算函数。这个词在整个语料库中越罕见,它就越重要。
让我们看一下解释部分的摘录,以更好地了解它的计算方式。
{
"value": 0.5389965,
"description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:",
"details": [
{
"value": 3,
"description": "n, number of documents containing term",
"details": []
},
{
"value": 5,
"description": "N, total number of documents with field",
"details": []
}
]
},
这意味着IDF与索引中的文档数成反比。在我们的例子中,它会导致危险。回想一下,我们对多个索引执行搜索。假设有两个不同的索引,一个包含2K文档,另一个包含20K文档,它们都只包含给定查询的一个匹配项。这意味着第一个索引的IDF会更高,因此第一个索引的结果将更相关!
但是,对于最终用户来说,索引的数量和其中的项目数量是一个无关紧要的实现细节。用户关心的是输出结果与查询字符串输入的匹配程度。
丢弃IDF
考虑到用户对跨索引的数据倾斜不感兴趣,并且放置足够具体的查询,我们决定IDF在我们的案例中无关紧要。我们如何从搜索计算公式中丢弃它?
答案是脚本相似性。
让我们重新实现BM25,但使用恒定的IDF。
PUT /index
"settings": {
"index": {
"similarity": {
"discarded_idf": {
"type": "scripted",
"script": {
"source": "double tf = Math.sqrt(doc.freq);
double idf = 1.0; double norm = 1 / Math.sqrt(doc.length);
return query.boost * tf * idf * norm;"
}
}
}
}
}
差不多就是这样:原始的BM25公式,但IDF在所有索引的每次搜索中都是恒定的。完成此操作后,我们可以计算出更适合我们用例的相似性。
结论
Elasticsearch为您的搜索查询提供了广泛的工具,以返回最相关的结果。用尽专用数据类型和丰富的查询DSL后,还可以通过使用一组预定义算法或推出自己的算法来调整相似性计算。
https://www.codeproject.com/Tips/5349236/Applying-Custom-Similarity-Calculation-in-Elastics