完成提示符的标记字符串

时间:2018-11-01 08:05:20

标签: elasticsearch nest

想要使用完成建议程序来构建电子商务网站的自动完成功能。

这是我的索引:

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”将不会返回任何结果...

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

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中做到这一点吗?

2 个答案:

答案 0 :(得分:1)

Completion suggester设计用于使用simple分析器而不是默认的standard分析器,根据您的类型快速搜索前缀查询text数据类型。

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

  1. Analyze API与分析器一起使用,该分析器会将标题标记化为要部分前缀匹配的标记/术语,然后将此集合作为input索引到{{1 }}字段。标准分析仪可能是一个很好的起点。

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

  1. 请不要在此处使用“完成建议程序”,而是将completion字段设置为具有multi-fieldstitle数据类型,其中包括text的不同方式应该进行分析(或不进行分析,例如,使用title子字段)。

    花一些时间使用Analyze API来构建一个分析器,该分析器将允许在标题中任何位置使用部分术语前缀。首先,诸如标准令牌生成器,小写令牌过滤器,Edgengram令牌过滤器以及可能的Stop令牌过滤器之类的工具会让您运行起来。还要注意,您可能希望Search analyzer的功能类似于索引分析器 Edgengram令牌过滤器,因为搜索输入中的令牌不需要进行ngramming。

    < / li>

答案 1 :(得分:0)

基于上述Russ Cam的答案(选项2),this Elasticsearch guide以及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 标记但没有语言的原因……请注意,我们有山脉,但没有高山运动
  4. 我已经定义了mapped_words_char_filter,它将C#转换为csharp,没有这个c#将不是有效的令牌...(此设置不会标记化F#)
  5. 我正在使用内置的html_stripchar_filter&amp;转换为&,并且由于我们的min_gram = 2
  6. 而被忽略
  7. 我们正在使用内置的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())));