在Elasticsearch中正确排序以进行完全匹配并使用“开头为”(前缀)

时间:2019-02-05 10:54:24

标签: elasticsearch

我需要使用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 *”也将投放“ green apple”。这似乎就是recore无法为我工作的原因。

可能还有其他字符,例如点“。”,“-”,“;”等等。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
      }
   }
}'

这似乎可行,但是我仍然不确定。这是正确的方法吗?

EDIT1:对于其他查询,一个匹配结果无法正常工作。

1 个答案:

答案 0 :(得分:2)

您唯一需要在分数上进行操作的地方是完全匹配,否则按词条位置的顺序将为您提供正确的顺序。让我们通过以下内容了解这一点:

让我们首先创建一个映射,如下所示:

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

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

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

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(如果存在其他术语,例如{{ 1}}用于文档2,tree用于文档3)。当我们在字段green上执行完全匹配时,所有文档都会匹配,并且对每个文档的得分都会产生相似的影响,因此得分不会发生变化。由于只有一个文档具有与apple相对的确切值apple,因此该文档(文档1)将是精确查询的匹配项,我们将对其进行增强。因此查询将是:

my_field1.keyword

上述查询的输出:

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