Elasticsearch 如何匹配字段标记是查询标记子集的文档

Elasticsearch how to match documents for which the field tokens are a sub-set of the query tokens

我有一个 keyword/key-phrase 字段,我使用标准分析器对其进行了标记。如果搜索短语中包含该字段的所有标记,我希望该字段匹配。

例如,如果字段值为 "veni, vidi, vici" 且搜索词组为 "Ceaser veni,vidi,vici" 我希望此搜索词组匹配但搜索词组 "veni, vidi" 不匹配。

我还需要 "vidi, veni, vici"(奇怪!)来匹配。所以术语的位置和顺序并不重要。我认为词组匹配不太适合我。

对于这个特定示例,我可以使用 "bool query" 和 "minimum_should_match" 参数,但这并不是我真正想要的,因为至少应该匹配搜索短语中大约 ratio/number 个标记。

没有内置解决方案,但这个有效:

  1. 为每个文档添加一个额外的字段,其中包含字段中的术语数。因此,在您的 "veni, vidi, vici" 示例中,您将拥有一个类似于 "field_term_count" 的字段:3.

  2. 对搜索查询中的每个标记执行单独的匹配搜索。

  3. 对至少有一个匹配项的每个文档匹配的搜索次数求和(例如,具有文档 ID 键和计数值的哈希表)。

  4. 将 3 中的匹配项数与每个具有匹配项的文档的 "field_term_count" 字段进行比较。如果它们相等,则文档匹配。

然后 "Ceaser veni,vidi,vici" 将匹配,但搜索词组 "veni, vidi" 将不会根据需要匹配。对于合理数量的匹配,它应该相当快。

纯 ES 解决方案应该是这样的。您将需要两个请求。

1) 首先你需要通过 analyze api 传递用户查询以获得所有搜索令牌。

curl -XGET 'localhost:9200/_analyze' -d '
{
  "analyzer" : "standard",
  "text" : "Ceaser veni,vidi,vici"
}'

您将获得 4 个代币 ceaser, veni, vidi, 维奇。您需要将这些标记作为数组传递给下一个 search 请求。

2) 我们需要搜索其标记为搜索标记 子集 的文档。

{
  "query": {
    "filtered": {
      "filter": {
        "bool": {
          "must": [
            {
              "query": {
                "match": {
                  "title": "Ceaser veni,vidi,vici"
                }
              }
            },
            {
              "script": {
                "script": "if(search_tokens.containsAll(doc['title'].values)){return true;}",
                "params": {
                  "search_tokens": [
                    "ceaser",
                    "veni",
                    "vidi",
                    "vici"
                  ]
                }
              }
            }
          ]
        }
      }
    }
  }
}

过滤器中第一个 match query 的工作是缩小脚本应该 运行 的文档范围。 containsAll 方法将检查文档标记是否为搜索标记的 sublist。这会很慢,但可以完成您当前设置的工作。您可以做的一项重大改进是将令牌存储为数组,以便 doc['title'].values 可以替换为该字段,从而改进脚本。

希望对您有所帮助!