我正在使用Elasticsearch进行全文搜索,并且试图找到一种更好的方式来搜索长短语。
例如,我有一个字段“Seller”,最多可以包含250个字符,我想查找所有带有Seller ='一些带有空格的卖家名称'的商品。
如果我正确理解,为了搜索包含空格的文本,我必须使用基本上创建 token 的NGramTokenizer,例如:
's', 'so', 'som', 'some', 'some ', 'some s' etc.
我知道我可以定义最小和最大克,但是我必须能够搜索“a b”,因此我的最小克必须至少为3,最大克为字段的最大长度。
因此,我必须为每个项目创建很多 token ,并且这只是卖方,但是关于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'
使用空白标记器,如果您搜索短语,则无法搜索部分单词。
最佳答案
您需要回答的第一个问题是:是否需要匹配单词中的子字符串。例如,在反式中匹配错过 ionic 。如果您需要此功能,那么没有比ngrams更好的方法了。试图在术语开始时使用通配符,将意味着遍历索引中的每个术语以查看其是否匹配并且缩放效果不佳。 The match_phrase_prefix is the same as match_phrase, except that it
allows for prefix matches on the last term in the text.
请注意,可以通过两种方式使用ngram:作为标记生成器或作为标记过滤器。除了使用的分词器之外,还可以使用 token 过滤器变体。首先使用standard
或whitespace
标记生成器标记文本,然后应用ngram标记过滤器。使用 token 过滤器,索引中不会有克。您需要多久查找一次以ing
结尾的单词,紧随其后以to
开头的单词的文本?
如果您不需要查看单词的内在内容,但有时想省略后缀,则还有其他几种选择。第一个是另一种克,即edge grams,它们固定在单词的开头。边缘ngram的最常见用例场景是按需搜索功能。
在下面,您可以看到使用所有这些克方法(最小:2个最大:3个)的索引(来自inquisitor插件的屏幕截图)huge bowtie
的示例比较:
token 中的数字很重要,它们是位置号。查找短语时使用位置编号。查找短语"a b"
本质上是查找 token "a"
,然后查找 token "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
,因为单词内至少有1克匹配就足够了。
如果使用分析查询并且不指定需要短语搜索,则还必须小心。例如,使用"query_string": { "query": "bowtie" }
可以将边缘ngram转换为bo OR bow OR bowt OR bowt OR bowti OR bowtie
,因为默认的query_string
运算符是OR
。那不是用户想要的,因为它将与bo
匹配。
还要注意,如果在同一位置上有多个标记,则存在一些短语即使不匹配也将匹配的问题。例如,短语"hu bowti"
将与edge_filter和ngram_filter token 匹配,即使源文本中没有这样的短语。
克的 token 过滤器变体似乎较差,并且没有真正的用处。但是,当使用gram token 过滤器时,人们commonly使用不同的分析器进行搜索而不是建立索引。例如,如果我们不对查询"huge bowtie"
进行任何分析而不进行分析,则仅查找2个字词即可找到匹配项(因为它们都在索引中,因此存在huge:1
和bowtie:2
)。但是,使用这种方法时,您需要将n设置得较高(要确保100%匹配的所有内容都应等于最长的单词)。否则,在使用最大语法5时,您可能会遇到与bowtie
搜索不匹配的情况,因为索引仅包含bowti
token 。
如您所见,克引入了相当复杂的问题。这就是为什么人们通常将克与正常索引的文本结合在一起(使用multi_field映射)。以后再给自己留下选择。使用不同的分析器为同一文本建立索引可以在多种方式下进行搜索,并在一次使用两个字段进行搜索时提高了准确性。
如果您不想处理所有与克相关的问题。您可以简单地正常索引文本并使用通配符。您需要付出搜索时间的代价,但是根据您的数据和方案,它可能会起作用。在我公司中,我们个人使用通配符查询具有数十亿个文档的索引,并且可以很好地处理它。
如果您决定使用通配符查询,则有几种选择。您可以使用wildcard查询或query_string查询。但是,使用它们将无法立即进行短语和通配符后缀查询。希望有一个完全符合您要求的匹配查询变体:搜索词尾不完整的词组:{
"match_phrase_prefix" : {
"message" : {
"query" : "Huge 48" Bowt",
"max_expansions" : 100
}
}
}
摘自docs:
总结一下。
如果我正确理解了您的情况,则可以在带有原始文本的多字段中使用边缘标记器或我最喜欢的边缘标记过滤器(带有标准搜索分析器)。拥有原始文本可以使用较低的边克值。有了这样的映射,您可以使用以下query_string:"originalText: \"Huge 48" Bowt\" OR edgeGrammed: \"Huge 38" Bowt\""
。您不必担心边缘克的n太低,因为原始文本会有一个后备。我认为n等于10-15就足够了吗?同样,对于原始文本,通配符始终是一个选项。
Here也是一篇关于ngram的不错的文章。
关于elasticsearch - Elasticsearch长短语搜索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32379190/