Elasticsearch 长短语搜索
Elasticsearch long phrases search
我正在使用 Elasticsearch 进行全文搜索,我正在尝试寻找一种更好的方法来搜索长短语。
例如,我有一个字段 "Seller",最多可以包含 250 个字符,我想查找 Seller = 'some seller name with spaces'.
的所有项目
如果我理解正确,为了搜索包含空格的文本,我必须使用 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'
使用空格分词器,如果您搜索短语,则无法搜索部分单词。
您需要回答的第一个问题是:是否需要匹配单词中的子字符串。例如在 transmission 中匹配 miss。如果您需要此功能,那么没有比 ngrams 更好的方法来实现它了。尝试在术语的开头使用通配符,意味着遍历索引中的每个术语以查看它是否匹配并且不能很好地扩展。
请注意,您可以通过两种方式使用 ngrams:作为分词器或作为分词过滤器。除了您使用的分词器,您还可以使用分词过滤器变体。首先使用 standard
或 whitespace
分词器对文本进行分词,然后应用 ngram 分词过滤器。使用令牌过滤器,您的索引中不会有带空格的克。您需要多久查找一次以 ing
结尾的单词和紧随其后的以 to
开头的单词的文本?
如果您不需要查看 word 的内部,但有时又想省去后缀,还有其他几个选项。第一个是另一种克,edge grams,它们锚定在单词的开头。 edge ngrams 最常见的用例场景是输入即搜索功能。
下面您可以看到索引比较示例(来自 inquisitor 插件的屏幕截图)huge bowtie
使用所有这些克方法(最小值:2 最大值:3):
令牌旁边的数字很重要,它们是位置编号。查找短语时使用位置编号。查找短语 "a b"
本质上是查找标记 "a"
然后查找标记 "b"
并检查它们的位置差是否等于 1。正如您在上面看到的,这些克结果位置查找短语时可能会出现一些问题。
首先,让我们看看如何使用查询 "huge bowtie"
using _validate API:
对以这种方式分析的字段解释短语查询
- edge_filter
"(hu hug huge) (bo bow bowt bowti bowtie)"
- edge_tokenizer
"hu hug huge bo bow bowt bowti bowtie"
- ngram_filter
"(hu hug ug uge ge) (bo bow ow owt wt wti ti tie ie)"
- ngram_tokenizer
"hu hug ug uge ge bo bow ow owt wt wti ti tie ie"
tokenizer 查询解释相当简单:不是一个接一个地查看两个 token,而是必须查看所有的 gram 并确保它们相互跟随。过滤器版本更麻烦:查询 "huge bowtie"
将匹配文本 hu owt
因为单词匹配中至少有一个克就足够了。
如果您使用分析查询并且没有指定您需要短语搜索,您也必须小心。例如,对于边缘 ngram,使用 "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 标记匹配,即使源文本中没有这样的短语。
gram 的标记过滤器变体似乎很差,而且不是很有用。但是当使用 gram 标记过滤器时,人们 commonly 使用不同的分析器进行搜索而不是索引。例如,如果我们让查询 "huge bowtie"
保持原样而不对其进行分析,它将仅通过查找 2 个术语来找到匹配项(因为它们都在索引中,所以有 huge:1
和 bowtie:2
)。但是使用这种方法,您需要将 n 设置得相当高(要 100% 确定所有内容都会匹配,它应该等于最长的单词)。否则,当使用 max gram 5 时,您可能会遇到不匹配 bowtie
搜索的情况,因为索引只包含 bowti
标记。
如您所见,gram 引入了相当复杂的问题。这就是为什么人们通常将克与正常索引的文本结合起来(使用 multi_field 映射)。以后给自己留下选择。使用不同的分析器索引相同的文本允许以多种方式进行搜索并在一次使用两个字段进行搜索时提高精度。
如果您不想处理所有与克相关的问题。您可以简单地正常索引文本并使用通配符。你在搜索时间上付出了代价,但根据你的数据和场景,它可能会起作用。就我个人而言,在我的公司,我们使用通配符来查询索引,这些索引总共有数十亿个文档,弹性处理得很好。
如果您决定使用通配符查询,您有几个选择。您可以使用 wildcard query or query_string 查询。但是使用它们您将无法同时进行短语和通配符后缀查询。希望有匹配查询变体可以完全满足您的需求:搜索最后一个单词被视为不完整的短语:
{
"match_phrase_prefix" : {
"message" : {
"query" : "Huge 48" Bowt",
"max_expansions" : 100
}
}
}
摘自docs:
The match_phrase_prefix is the same as match_phrase, except that it
allows for prefix matches on the last term in the text.
总结一下
如果我正确理解你的情况,我会在带有原始文本的多字段中使用边缘标记器或我最喜欢的边缘标记过滤器(带有标准搜索分析器)。拥有原始文本允许在边缘克中使用较低的值。有了这样的映射,您可以使用以下 query_string:"originalText: \"Huge 48" Bowt\" OR edgeGrammed: \"Huge 38" Bowt\""
。你不必担心你的 n in edge gram 太低,因为你有原始文本的后备。我认为 n 等于 10-15 应该足够了吧?此外,对于原始文本,通配符始终是一个选项。
Here 也是一篇关于 ngram 的好文章。
我正在使用 Elasticsearch 进行全文搜索,我正在尝试寻找一种更好的方法来搜索长短语。
例如,我有一个字段 "Seller",最多可以包含 250 个字符,我想查找 Seller = 'some seller name with spaces'.
的所有项目如果我理解正确,为了搜索包含空格的文本,我必须使用 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'
使用空格分词器,如果您搜索短语,则无法搜索部分单词。
您需要回答的第一个问题是:是否需要匹配单词中的子字符串。例如在 transmission 中匹配 miss。如果您需要此功能,那么没有比 ngrams 更好的方法来实现它了。尝试在术语的开头使用通配符,意味着遍历索引中的每个术语以查看它是否匹配并且不能很好地扩展。
请注意,您可以通过两种方式使用 ngrams:作为分词器或作为分词过滤器。除了您使用的分词器,您还可以使用分词过滤器变体。首先使用 standard
或 whitespace
分词器对文本进行分词,然后应用 ngram 分词过滤器。使用令牌过滤器,您的索引中不会有带空格的克。您需要多久查找一次以 ing
结尾的单词和紧随其后的以 to
开头的单词的文本?
如果您不需要查看 word 的内部,但有时又想省去后缀,还有其他几个选项。第一个是另一种克,edge grams,它们锚定在单词的开头。 edge ngrams 最常见的用例场景是输入即搜索功能。
下面您可以看到索引比较示例(来自 inquisitor 插件的屏幕截图)huge bowtie
使用所有这些克方法(最小值:2 最大值:3):
令牌旁边的数字很重要,它们是位置编号。查找短语时使用位置编号。查找短语 "a b"
本质上是查找标记 "a"
然后查找标记 "b"
并检查它们的位置差是否等于 1。正如您在上面看到的,这些克结果位置查找短语时可能会出现一些问题。
首先,让我们看看如何使用查询 "huge bowtie"
using _validate API:
- edge_filter
"(hu hug huge) (bo bow bowt bowti bowtie)"
- edge_tokenizer
"hu hug huge bo bow bowt bowti bowtie"
- ngram_filter
"(hu hug ug uge ge) (bo bow ow owt wt wti ti tie ie)"
- ngram_tokenizer
"hu hug ug uge ge bo bow ow owt wt wti ti tie ie"
tokenizer 查询解释相当简单:不是一个接一个地查看两个 token,而是必须查看所有的 gram 并确保它们相互跟随。过滤器版本更麻烦:查询 "huge bowtie"
将匹配文本 hu owt
因为单词匹配中至少有一个克就足够了。
如果您使用分析查询并且没有指定您需要短语搜索,您也必须小心。例如,对于边缘 ngram,使用 "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 标记匹配,即使源文本中没有这样的短语。
gram 的标记过滤器变体似乎很差,而且不是很有用。但是当使用 gram 标记过滤器时,人们 commonly 使用不同的分析器进行搜索而不是索引。例如,如果我们让查询 "huge bowtie"
保持原样而不对其进行分析,它将仅通过查找 2 个术语来找到匹配项(因为它们都在索引中,所以有 huge:1
和 bowtie:2
)。但是使用这种方法,您需要将 n 设置得相当高(要 100% 确定所有内容都会匹配,它应该等于最长的单词)。否则,当使用 max gram 5 时,您可能会遇到不匹配 bowtie
搜索的情况,因为索引只包含 bowti
标记。
如您所见,gram 引入了相当复杂的问题。这就是为什么人们通常将克与正常索引的文本结合起来(使用 multi_field 映射)。以后给自己留下选择。使用不同的分析器索引相同的文本允许以多种方式进行搜索并在一次使用两个字段进行搜索时提高精度。
如果您不想处理所有与克相关的问题。您可以简单地正常索引文本并使用通配符。你在搜索时间上付出了代价,但根据你的数据和场景,它可能会起作用。就我个人而言,在我的公司,我们使用通配符来查询索引,这些索引总共有数十亿个文档,弹性处理得很好。
如果您决定使用通配符查询,您有几个选择。您可以使用 wildcard query or query_string 查询。但是使用它们您将无法同时进行短语和通配符后缀查询。希望有匹配查询变体可以完全满足您的需求:搜索最后一个单词被视为不完整的短语:
{
"match_phrase_prefix" : {
"message" : {
"query" : "Huge 48" Bowt",
"max_expansions" : 100
}
}
}
摘自docs:
The match_phrase_prefix is the same as match_phrase, except that it allows for prefix matches on the last term in the text.
总结一下
如果我正确理解你的情况,我会在带有原始文本的多字段中使用边缘标记器或我最喜欢的边缘标记过滤器(带有标准搜索分析器)。拥有原始文本允许在边缘克中使用较低的值。有了这样的映射,您可以使用以下 query_string:"originalText: \"Huge 48" Bowt\" OR edgeGrammed: \"Huge 38" Bowt\""
。你不必担心你的 n in edge gram 太低,因为你有原始文本的后备。我认为 n 等于 10-15 应该足够了吧?此外,对于原始文本,通配符始终是一个选项。
Here 也是一篇关于 ngram 的好文章。