Elasticsearch 荧光笔误报

Elasticsearch highlighter false positives

我在 ES 6.1.1 中使用 nGram 分词器并得到一些奇怪的亮点:

查询 auftrag 按预期匹配文档 7 和 9,但在文档 9 中 betrag 未正确突出显示。这是荧光笔的问题 - 如果问题出在查询文档 8 上,也会返回。

示例代码

#!/usr/bin/env bash

# Example based on  
# https://www.elastic.co/guide/en/elasticsearch/guide/current/ngrams-compound-words.html
# with suggestions from from 
# https://github.com/elastic/elasticsearch/issues/21000

如果存在则删除索引

curl -sS -XDELETE 'localhost:9200/my_index'
printf '\n-------------\n'

创建新索引

curl -sS -XPUT 'localhost:9200/my_index?pretty' -H 'Content-Type: application/json' -d'
{
    "settings": {
    "analysis": {
      "analyzer": {
        "trigrams": {
          "tokenizer": "my_ngram_tokenizer",
          "filter": ["lowercase"]
        }
      },
      "tokenizer": {
        "my_ngram_tokenizer": {
          "type": "nGram",
          "min_gram": "3",
          "max_gram": "3",
          "token_chars": [
            "letter",
            "digit",
            "symbol",
            "punctuation"
          ]
        }
      }
    }
},
    "mappings": {
        "my_type": {
            "properties": {
                "text": {
                    "type":     "text",
                    "analyzer": "trigrams",
                    "term_vector": "with_positions_offsets"
                }
            }
        }
    }
}
'
printf '\n-------------\n'

填充索引

curl -sS -XPOST 'localhost:9200/my_index/my_type/_bulk?pretty' -H 'Content-Type: application/json' -d'
{ "index": { "_id": 7 }}
{ "text": "auftragen" }
{ "index": { "_id": 8 }}
{ "text": "betrag" }
{ "index": { "_id": 9 }}
{ "text": "betrag auftragen" }
'
printf '\n-------------\n'
sleep 1  # Give ES time to index

查询

curl -sS -XGET 'localhost:9200/my_index/my_type/_search?pretty' -H 'Content-Type: application/json' -d'
{
    "query": {
        "match": {
            "text": {
                "query": "auftrag",
                "minimum_should_match": "100%"
            }
        }
    },
      "highlight": {
        "fields": {
          "text": {
            "fragment_size": 120,
            "type": "fvh"
          }
        }
      }
}
'

我得到的点击数是(缩写):

"hits" : [
      {
        "_id" : "9",
        "_source" : {
          "text" : "betrag auftragen"
        },
        "highlight" : {
          "text" : [
            "be<em>tra</em>g <em>auf</em><em>tra</em>gen"
          ]
        }
      },
      {
        "_id" : "7",
        "_source" : {
          "text" : "auftragen"
        },
        "highlight" : {
          "text" : [
            "<em>auf</em><em>tra</em>gen"
          ]
        }
      }
    ]

我尝试了各种解决方法,例如使用 unified/fvh 荧光笔并设置所有似乎相关的选项,但没有成功。非常感谢任何提示。

这里的问题不在于突出显示,而在于你如何使用 nGram 分析器。

首先,当您以这种方式配置映射时:

"mappings": {
  "my_type": {
    "properties": {
      "text": {
        "type"       : "text",
        "analyzer"   : "trigrams",
        "term_vector": "with_positions_offsets"
      }
    }
  }
}

你是在对 Elasticsearch 说你想将它用于索引文本并提供搜索词。在您的情况下,这仅意味着:

  1. 您的文档 9 = "betrag auftragen" 中的文本被分成三元组,因此在索引中您有类似的内容:[bet, etr, tra, rag, auf, uft, ftr, tra, rag, age , 创]
  2. 文档 7 = "auftragen" 中的文本被拆分为三元组,因此在索引中您有类似的内容:[auf, uft, ftr, tra, rag, age, gen]
  3. 您的搜索词 = "auftrag" 也针对三元组进行了拆分,Elasticsearch 将其视为:[auf、uft、ftr、tra、rag]
  4. 最后,Elasticsearch 将搜索中的所有三元组与您的索引中的所有三元组进行匹配,因此您分别突出显示了 'auf' 和 'tra'。 'ufa'、'ftr' 和 'rag' 也匹配,但它们与 'auf' 和 'tra' 重叠并且未突出显示。

首先你需要做的是告诉 Elasticsearch 你不想将搜索词拆分为克。您需要做的就是将 search_analyzer 属性 添加到您的映射中:

"mappings": {
  "my_type": {
    "properties": {
      "text": {
        "type"           : "text",
        "analyzer"       : "trigrams",
        "search_analyzer": "standard",
        "term_vector"    : "with_positions_offsets"
      }
    }
  }
}

现在 standard analyzer 将搜索词中的单词视为单独的单词,因此在您的情况下,它只是 "auftrag"。

但是这个单一的改变对你没有帮助。它甚至会中断搜索,因为 "auftrag" 与您索引中的任何三字词都不匹配。

现在您需要通过增加 max_gram:

来改进您的 nGram 分词器
"tokenizer": {
  "my_ngram_tokenizer": {
    "type": "nGram",
    "min_gram": "3",
    "max_gram": "10",
    "token_chars": [
      "letter",
      "digit",
      "symbol",
      "punctuation"
    ]
  }
}

这样,您索引中的文本将分为 3-gram、4-gram、5-gram、6-gram、7-gram、8-gram、9-gram 和 10-gram。在这 7 克中,您会发现 "auftrag" 这是您的搜索词。

经过这两项改进后,搜索结果中的突出显示应如下所示:

"betrag <em>auftrag</em>en"

对于文档 9 和:

"<em>auftrag</em>en"

文档 7。

这就是 ngram 和高亮显示的协同工作方式。我知道 ES documentation is saying:

It usually makes sense to set min_gram and max_gram to the same value. The smaller the length, the more documents will match but the lower the quality of the matches. The longer the length, the more specific the matches. A tri-gram (length 3) is a good place to start.

这是真的。出于性能原因,您需要尝试使用此配置,但我希望我向您解释了它是如何工作的。

我在这里遇到了同样的问题,使用 ngram(trigram) 分词器,突出显示不完整,如:

query with `match`: samp
field data: sample
result highlight: <em>sam</em>ple
expected highlight: <em>samp</em>le

使用match_phrase,当设置字段的term_vectorwith_positions_offsets时,使用fvh高亮类型,这样可能会得到正确的高亮。

<em>samp</em>le

我希望这可以帮助您,因为您不需要更改分词器或增加 max_gram

但我的问题是我想使用simple_query_string,它不支持使用phrase进行默认字段查询,唯一的方法是使用引号将字符串包裹起来,如"samp" , 但由于查询字符串中有一些逻辑,所以我不能为用户做,并且要求用户也不要做。

@piotr-pradzynski 的解决方案可能对我没有帮助,因为我有很多数据,增加 max_gram 会导致大量存储使用。