在 Elasticsearch 中正确排序精确匹配和"beginning with"(前缀)



我需要改进Elasticsearch的搜索结果列表。

假设我们有3个文档,其中只有一个字段,内容如下:

  • "苹果">
  • "青苹果">
  • "苹果树">

如果我搜索"苹果",可能会发生这种情况,我得到的结果排序如下:

  • "青苹果">
  • "苹果树">
  • "苹果">

但我想要的是获得最高分数的精确匹配,这里是带有"apple"的文档。

下一个最高分应该是以搜索词开头的条目,这里是"苹果树",其余按默认方式排序。

所以我想要这个:

  • "苹果">
  • "苹果树">
  • "青苹果">

我试图通过使用rescore:来实现它

curl -X GET "http://localhost:9200/my_index_name/_search?size=10&pretty" -H 'Content-Type: application/json' -d'
{
"query": {
"query_string": {
"query": "apple"
}
},
"rescore": {
"window_size": 500,
"query": {
"score_mode": "multiply",
"rescore_query": {
"bool": {
"should": [
{
"match": {
"my_field1": {
"query": "apple",
"boost": 4
}
}
},
{
"match": {
"my_field1": {
"query": "apple*",
"boost": 2
}
}
}
]
}
},
"query_weight": 0.7,
"rescore_query_weight": 1.2
}
}
}'

但这并不能真正起作用,因为Elasticsearch似乎用空格分隔所有单词。例如,搜索"apple*"也会提供"greenapple"。这似乎就是为什么rescore对我不起作用的原因。

可能还有其他字符,如点"."、"-"、";"等,Elasticsearch用来拆分和打乱我的排序。

我还在"rescore_query"中使用"match_phrase"而不是"bool",但没有成功。

我也尝试过只有一个匹配这个:

curl -X GET "http://localhost:9200/my_index_name/_search?size=10&pretty" -H 'Content-Type: application/json' -d'
{
"query": {
"query_string": {
"query": "apple"
}
},
"rescore": {
"window_size": 500,
"query": {
"score_mode": "multiply",
"rescore_query": {
"bool": {
"should": [
{
"match": {
"my_field1": {
"query": "apple*",
"boost": 2
}
}
}
]
}
},
"query_weight": 0.7,
"rescore_query_weight": 1.2
}
}
}'

它似乎有效,但我仍然不确定。这是正确的做法吗?

编辑1:对于其他查询,一个匹配的重新存储操作不正确。

唯一需要处理分数的地方是精确匹配,否则按术语位置排列的顺序会给出正确的顺序。让我们通过以下内容来理解这一点:

让我们首先创建如下映射:

PUT test
{
"mappings": {
"_doc": {
"properties": {
"my_field1": {
"type": "text",
"analyzer": "whitespace",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
}

我用whitespace分析器创建了字段my_field1,以确保通过仅使用空格作为分隔符来创建令牌。其次,我创建了一个子字段,名为keyword,类型为keywordkeyword将保存输入字符串的未分析值,我们将使用它进行精确匹配。

让我们在索引中添加一些文档:

PUT test/_doc/1
{
"my_field1": "apple"
}
PUT test/_doc/2
{
"my_field1": "apple tree"
}
PUT test/_doc/3
{
"my_field1": "green apple"
}

如果使用以下查询来搜索术语apple,则文档的顺序为2,1,3.

POST test/_doc/_search
{
"explain": true,
"query": {
"query_string": {
"query": "apple",
"fields": [
"my_field1"
]
}
}
}

上述查询中的"explain": true给出了输出中的分数计算步骤。阅读这篇文章会让你了解文档是如何评分的。

我们所需要做的就是提高比分,以确保比赛准确无误。我们将与my_field1.keyword进行精确的比赛。你可能会有一个问题,为什么不my_field1。这是因为分析了my_field1,当为3个文档的输入字符串生成令牌时,所有文档都将具有针对该字段存储的令牌(术语)apple(以及其他术语(如果存在的话),例如文档2的tree和文档3的green)。当我们在此字段上为术语apple运行精确匹配时,所有文档都将匹配,并对每个文档的分数产生类似的影响,因此分数不会发生变化。由于只有一个文档具有applemy_field1.keyword的精确值,因此该文档(文档1)将与精确查询相匹配,我们将对此进行改进。因此,查询将是:

{
"query": {
"bool": {
"should": [
{
"query_string": {
"query": "apple",
"fields": [
"my_field1"
]
}
},
{
"query_string": {
"query": ""apple"",
"fields": [
"my_field1.keyword^2"
]
}
}
]
}
}
}

以上查询的输出:

{
"took": 9,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1.7260925,
"hits": [
{
"_index": "test3",
"_type": "_doc",
"_id": "1",
"_score": 1.7260925,
"_source": {
"my_field1": "apple"
}
},
{
"_index": "test3",
"_type": "_doc",
"_id": "2",
"_score": 0.6931472,
"_source": {
"my_field1": "apple tree"
}
},
{
"_index": "test3",
"_type": "_doc",
"_id": "3",
"_score": 0.2876821,
"_source": {
"my_field1": "green apple"
}
}
]
}
}

最新更新