我正在使用Elasticsearch进行全文搜索,而我正试图找到一种更好的搜索长短语的方法。
例如,我有一个字段“卖家”,最多可以有250个字符,我想查找卖家='某个卖家名字带空格'的所有商品。
如果我理解正确,为了搜索包含空格的文本,我必须使用基本上创建令牌的NGramTokenizer:
's', 'so', 'som', 'some', 'some ', 'some s' etc.
我知道我可以定义最小和最大克数,但我需要能够搜索'a b',所以我的最小克必须至少为3和最大克作为我的字段最大长度。
所以我必须为每个项目创建很多令牌,而且它只是卖家,但是用4k字符描述呢?
此解决方案的性能非常低。
有人能建议一个更好的解决方案来处理带有空格的长短语吗?
我的索引设置:
analysis: {
analyzer: {
autoComplete: {
filter: [
"lowercase"
],
type: "custom",
tokenizer: "autoComplete"
},
caseInsensitive: {
type: "custom",
filter: [
"lowercase"
],
tokenizer: "keyword"
}
},
tokenizer: {
autoComplete: {
type: "nGram",
min_gram: "1",
max_gram: "40"
}
}
},
我使用“autoComplete”作为索引分析器,使用“caseInsensitive”作为搜索分析器
修改
我使用NGramTokenizer来搜索部分单词
真实的例子:
Title: 'Huge 48" Bowtie LED Opti neon wall sign. 100,000 hours Bar lamp light'
search query: 'Huge 48" Bowt'
使用空格标记器,如果搜索短语,则无法搜索部分单词。
答案 0 :(得分:4)
您需要回答的第一个问题是:您是否需要匹配单词中的子字符串。例如,在 miss 离子中匹配 miss 。如果你需要这个功能,那么没有比ngrams更好的方法来实现它。尝试在术语开头使用通配符,意味着要遍历索引中的每个术语,看它是否匹配,并且它不能很好地扩展。
请注意,您可以通过两种方式使用ngrams:标记器或标记过滤器。您可以使用令牌过滤器变体代替您使用的令牌化程序。首先使用standard
或whitespace
标记生成器对文本进行标记,然后应用ngram标记过滤器。使用令牌过滤器,您的索引中不会包含空格。您有多少次需要找到以ing
结尾的单词的文字,并且在其后面有一个以to
开头的单词?
如果你不需要查看单词内容,但有时想要省略后缀,还有其他几个选项。第一个是另一种克,edge grams,它们锚定在单词的开头。边缘图的最常见用例场景是搜索即用型功能。
下面你可以看到索引的示例比较(来自inquisitor插件的截图)huge bowtie
使用所有克方法(最小值:2最大值:3):
令牌的数字很重要,它们是位置编号。查找短语时使用位置编号。寻找短语"a b"
主要是寻找令牌"a"
,然后寻找令牌"b"
,并检查他们的位置差异是否等于1.正如您在上面所看到的,那些克产生的位置查找短语时可能会出现一些问题。
首先,让我们看看如何使用_validate API使用"huge bowtie"
以这种方式分析字段,以便对字段进行解释:
"(hu hug huge) (bo bow bowt bowti bowtie)"
"hu hug huge bo bow bowt bowti bowtie"
"(hu hug ug uge ge) (bo bow ow owt wt wti ti tie ie)"
"hu hug ug uge ge bo bow ow owt wt wti ti tie ie"
令牌化程序查询解释相当简单:您不必一个接一个地查看两个令牌,而是必须查看所有克并确保它们彼此跟随。过滤器版本更麻烦:查询"huge bowtie"
将与文本hu owt
匹配,因为它足以使单词中的至少一个克匹配。
如果您使用已分析的查询并且未指定需要短语搜索,则还必须小心。例如,对于边缘ngrams,使用"query_string": { "query": "bowtie" }
将转换为bo OR bow OR bowt OR bowt OR bowti OR bowtie
,因为默认query_string
运算符为OR
。这不是用户想要的,因为它会与bo
匹配任何内容。
另请注意,如果在同一位置上有多个令牌,则存在一些问题,即某些短语将匹配,即使它们不应该匹配。例如,短语"hu bowti"
将与edge_filter和ngram_filter标记匹配,即使源文本中没有此类短语。
似乎令牌过滤器克的变体是劣等的,并没有真正有用。但是当使用克令牌过滤器时,人commonly使用不同的分析器进行搜索而不是索引。例如,如果我们保留查询"huge bowtie"
而不进行分析,则会通过仅查找2个字词来查找匹配项(因为它们都在索引中,有huge:1
和{{1} })。但是,使用这种方法,你需要设置n相当高(100%确定一切都匹配它应该等于最长的单词)。否则,您可能会遇到使用最大克数5的情况,因为索引只包含bowtie:2
令牌,因此您不会与bowtie
匹配。
你可以看到克引入了相当复杂的问题。这就是为什么人们通常将克与正常索引文本结合起来(使用multi_field映射)。将来以后留下自己的选择。使用不同的分析器为相同的文本编制索引允许在一次搜索中使用两个字段时以多种方式进行搜索并提高精度。
如果您不想处理所有与克相关的问题。您可以简单地正常索引文本并使用通配符。你在搜索时间内支付了价格,但是根据你的数据和场景,它可以工作..我个人在我公司使用通配符来查询索引,这些索引一起使用数十亿个文档和弹性句柄就可以了。
如果您决定使用通配符查询,则可以选择几个选项。您可以使用wildcard查询或query_string查询。但是使用它们,您无法立即进行短语和通配符后缀查询。希望有匹配查询变体,它可以完全符合您的要求:搜索短语,将最后一个单词视为不完整:
bowti
摘自docs:
match_phrase_prefix与match_phrase相同,但它除外 允许在文本的最后一个术语上进行前缀匹配。
总结一下。
如果我理解你的情况,我会在带有原始文本的多字段中使用边缘标记器或我最喜欢的边缘标记过滤器(使用标准搜索分析器)。拥有原始文本允许在边缘克中使用较低的值。有了这样的映射,您可以使用以下query_string:{
"match_phrase_prefix" : {
"message" : {
"query" : "Huge 48" Bowt",
"max_expansions" : 100
}
}
}
。你不必担心你的边缘克数太低,因为你的原始文本有后备。我认为n等于10-15应该足够了吗?此外,原始文本通配符始终是一个选项。
Here也是关于ngrams的好文章。