完成建议者的标记字符串

Tokenizing string for completion suggester

想要使用 Completion Suggester 构建 e-commerce 网站的自动完成功能。

这是我的索引:

PUT myIndex
{
    "mappings": {
        "_doc" : {
            "properties" : {
                "suggest" : {
                    "type" : "completion"
                },
                "title" : {
                    "type": "keyword"
                }, 
                "category" : { 
                    "type": "keyword"
                },
                "description" : { 
                    "type": "keyword"
                }
            }
        }
    }
}

现在,在上传广告时,我希望标题字段用于自动完成,所以这就是我上传文档的方式:

POST dummy/_doc
{
  "title": "Blue asics running shoes",
  "category": "sports",
  "description": "Nice blue running shoes, size 44 eu",
  "suggest": {
    "input": "Blue Asics running shoes" // <-- use title
  }
}

问题是,这样,弹性搜索只匹配开头的字符串...即 "Blu" 会找到结果,但 "Asic" 或 "Run" 或 "Sho" 赢了return 什么都没有...

所以我需要做的是像这样标记我的输入:

POST dummy/_doc
{
  "title": "Blue asics running shoes",
  "category": "sports",
  "description": "Nice blue running shoes, size 44 eu",
  "suggest": {
    "input": ["Blue", "Asics", "running", "shoes"] // <-- tokenized title
  }
}

这会工作得很好......但是我应该如何标记我的领域?我知道我可以在 C# 中拆分字符串,但无论如何我可以在 Elasticsearch/Nest?

中执行此操作

Completion suggester 专为快速 search-as-you-type prefix 查询而设计,使用 simple 分析器,而不是 standard 分析器这是 text 数据类型的默认值。

如果您需要对标题中的 任何 标记进行部分前缀匹配,而不仅仅是从标题的开头开始,您可能需要考虑采用以下方法之一:

  1. Analyze API 与分析器一起使用,该分析器会将标题标记为 tokens/terms,您希望从中进行部分前缀匹配,并将此 collection 索引为inputcompletion 字段。标准分析器可能是一个很好的开始。

    请记住,完成建议的数据结构在使用时保存在内存中,因此跨文档的高术语基数将增加此数据结构的内存需求。还要考虑匹配项的“评分”很简单,因为它由应用于每个输入的权重控制。

  1. 不要在此处使用 Completion Suggester,而是将 title 字段设置为具有 multi-fieldstext 数据类型,其中包括 multi-fields 的不同方式 title 应该分析(或不分析,例如 keyword 子字段)。

    花一些时间使用 Analyze API 来构建一个分析器,该分析器将允许标题中任何位置的术语的部分前缀。作为开始,诸如 Standard tokenizer、Lowercase token filter、Edgengram token filter 和可能的 Stop token filter 之类的东西会让你 运行。另请注意,您需要一个 Search analyzer 来执行类似于索引分析器 except Edgengram 标记过滤器的操作,因为搜索输入中的标记不需要进行 ngrammed。

根据上面 Russ Cam 的回答(选项 2),this Elasticsearch guide and also this document,我最终得到以下解决方案:

PUT my_index
{
  "settings": {
    "analysis": {
      "filter": {
        "edge_ngram_token_filter": {
          "type": "edge_ngram",
          "min_gram": 2,
          "max_gram": 10
        },
        "additional_stop_words": {
          "type":       "stop",
          "stopwords":  ["your"]
        },
        "english_stemmer": {
          "type":       "stemmer",
          "language":   "english"
        },
        "english_possessive_stemmer": {
          "type":       "stemmer",
          "language":   "possessive_english"
        }
      },
      "char_filter": {
        "my_char_filter": {
          "type": "mapping",
          "mappings": [
            "C# => csharp",
            "c# => csharp"
          ]
        }
       },
       "analyzer": {
        "result_suggester_analyzer": { 
          "type": "custom",
          "tokenizer": "standard",
          "char_filter":  [ "html_strip", "my_char_filter" ],
          "filter": [
            "english_possessive_stemmer",
            "lowercase",
            "asciifolding",
            "stop",
            "additional_stop_words",
            "english_stemmer",
            "edge_ngram_token_filter",
            "unique"
          ]
        }
      }
    }
  }
}

测试此解决方案的查询:

POST my_index/_analyze
{
  "analyzer": "result_suggester_analyzer",
  "text": "C# &amp; SQL are great languages. K2 is the mountaineer's mountain. Your house-décor is à la Mode"
}

我会得到这些标记 (NGrams):

cs, csh, csha, cshar, csharp, sq, sql, gr, gre, grea, great, la, lan, lang,
langu, langua, languag, k2, mo, mou, moun, mount, mounta, mountai, mountain, 
ho, hou, hous, hous, de, dec, deco, decor, mod, mode

这里需要注意的事项:

  1. 我正在使用 stop 过滤器,这是默认的英语 过滤并阻止 are, is, the - 但不是 your
  2. 我定义了 additional_stop_words,它停止 your
  3. 我正在使用内置的 englishpossessive_english 词干分析器,它会标记词干:这就是为什么我们有 languag 标记但没有语言或语言...还要注意我们有 mountain 但没有 mountaineering.
  4. 我已经定义了 mapped_words_char_filter 将 C# 转换为 csharp,没有这个 c# 将不是一个有效的标记...(这个设置不会标记 F#)
  5. 我正在使用内置的 html_stripchar_filter&amp; 转换为 &,它被忽略了,因为我们的 min_gram = 2
  6. 我们正在使用内置的 asciifolding 标记过滤器,这就是为什么 décor 被标记为 decor.

这是上面的 NEST 代码:

var createIndexResponse = ElasticClient.CreateIndex(IndexName, c => c
    .Settings(st => st
        .Analysis(an => an
            .Analyzers(anz => anz
                .Custom("result_suggester_analyzer", cc => cc
                    .Tokenizer("standard")
                    .CharFilters("html_strip", "mapped_words_char_filter")
                    .Filters(new string[] { "english_possessive_stemmer", "lowercase", "asciifolding", "stop", "english_stemmer", "edge_ngram_token_filter", "unique" })
                )
            )
            .CharFilters(cf => cf
                .Mapping("mapped_words_char_filter", md => md
                    .Mappings(
                        "C# => csharp",
                        "c# => csharp"
                    )
                )
            )
            .TokenFilters(tfd => tfd
                .EdgeNGram("edge_ngram_token_filter", engd => engd
                    .MinGram(2)
                    .MaxGram(10)
                )
                .Stop("additional_stop_word", sfd => sfd.StopWords(new string[] { "your" }))
                .Stemmer("english_stemmer", esd => esd.Language("english"))
                .Stemmer("english_possessive_stemmer", epsd => epsd.Language("possessive_english"))
            )
        )
    )
    .Mappings(m => m.Map<AdDocument>(d => d.AutoMap())));