设置Elasticsearch N-Gram字分析器的过程

说n-gram是一个大话题,这是轻描淡写的。 进行快速搜索,您会发现自己盯着语言学和语言模型,数据挖掘或特定蛋白质的分解对首次亮相文化的衰落所隐含的信息量。

好吧,我在跟那最后一个开玩笑。 但是,如果您是开发人员,希望使用Elasticsearch在应用程序中进行搜索,那么您很有可能需要以实用的方式使用n-gram分析器来进行某些搜索,并且可能需要一些针对性的信息才能获得您的搜索以期望的方式表现。 在Elasticsearch中使用n-gram搜索可以做的事情有很多可能性。 该博客将使您开始思考如何在搜索中使用它们。

一个例子

首先,让我们在这里缩小一下范围。 在很多情况下,使用n-gram可能是指搜索句子,而您的gram则是指句子中的单词。 但是对于今天,我想集中讨论单个单词的细分。 n元语法世界中的单个单词称为带状疱疹。

让我们进一步缩小自己的范围,假设我们想使用此搜索进行近似匹配。 在应用程序中,要搜索单词(名称,用户名)或类似于单词的数据(电话号码),然后以与搜索单词紧密匹配的形式向搜索者提供更多信息,这并不少见。 在这里,我们还希望部分匹配该单词,而不是总是在前面,也不总是在结尾。

为了便于参考,请假装我们有一个可以按名称查找动物的站点。 也许这是兽医办公室的前线,而该办公室希望首先以宠物的名字进行所有查找。 当然,您可能会发现自己Swift扩大了搜索范围,以包括其他条件,但举个例子,假设该办公室的所有爱狗人士都疯了,必须使用狗的名字。

分析仪

现在让我们考虑一下分析仪的需求。 首先,我们已经知道我们想要某种n-gram。 我们想要部分匹配。 其次,上面我们已经决定要搜索单词内的部分匹配项。 在这种情况下,这将仅在一定程度上,正如我们稍后将看到的那样,但是现在我们可以确定我们需要NGram令牌生成器,而不是仅保留从令牌开头开始保留n-gram的Edge NGram令牌生成器。

ElasticSearch Ngrams允许最小和最大克数。 从最小值开始,我们要匹配多少个名字? 好的,默认值为1,但是由于我们已经在处理单个单词的数据,所以如果我们使用一个字母(一个字母组合),我们肯定会得到太多的结果。 实际上,同样的事情也将适用于二元组。 但是,足够多的人养着三个字母的宠物,我们最好不要走,否则我们可能永远不会在搜索结果中返回名为“ Ace”和“ Rex”的幼犬。 现在我们知道我们的最小克数将是3。 最大克呢? 默认值为2,我们已经超过了最小值。 我们的目标是尽可能多地包含潜在的准确匹配项,但在索引大小存储方面仍然不会发疯。

考虑选择一个过大的数字(例如52),并为3个字符至52个字符之间的所有可能可能性分解名称,您会发现随着数据的增长,这很快就会加起来。 这里有些妥协,因为在某些情况下,您最终可能会排除超出最大语法的数据。

解决此排除问题的方法有两种,一种是包含您的字段的第二个映射并使用其他分析器(例如标准分析器),或者使用第二种映射并受益于精确匹配的速度和准确性字词查询。

在我们的案例中,我们将利用将单独的分析器用于搜索和索引的功能。 我们假设最大值之后的数据与我们的搜索无关,在这种情况下,它极有可能与搜索无关。

因此,这里我们创建索引,然后设置自定义分析器。 关于整体内容,这里的示例将有些简单,但是我希望它们有助于理解。

注意:稍微偏离主题,但是在现实生活中,您将需要以一种更可重用的方式进行操作,例如模板,以便您可以轻松使用别名和版本并更新索引,但是为此例如,我只是展示了卷曲索引创建的最简单设置。

这是我们的第一个分析器,创建一个自定义分析器,并使用ngram_tokenizer和我们的设置。 如果您在这里,则可能知道这一点,但是令牌生成器用于将字符串分解为术语或令牌流。 您可以根据需要在此处添加空格和许多其他选项:

curl -XPUT 'localhost:9200/searchpets' -d '
    {
        "settings" : {
            "analysis" : {
                "analyzer" : {
                    "ngram_analyzer" : {
                        "tokenizer" : "ngram_tokenizer"
                    }
                },
                "tokenizer" : {
                    "ngram_tokenizer" : {
                        "type" : "nGram",
                        "min_gram" : "3",
                        "max_gram" : "8"
                    }
                }
            }
        }
    }'

我们对此索引创建的响应为{“ acknowledged”:true}。 优秀的。

好了,既然有了索引,使用新的分析仪时数据将是什么样?

curl -XGET'localhost:9200/searchpets/_analyze?analyzer=ngram_analyzer' -d 'Raven'

响应是:

{"tokens":[{"token":"Rav","start_offset":0,"end_offset":3,"type":"word","position":1},{"token":"Rave","start_offset":0,"end_offset":4,"type":"word","position":2},{"token":"Raven","start_offset":0,"end_offset":5,"type":"word","position":3},{"token":"ave","start_offset":1,"end_offset":4,"type":"word","position":4},{"token":"aven","start_offset":1,"end_offset":5,"type":"word","position":5},{"token":"ven","start_offset":2,"end_offset":5,"type":"word","position":6}]}

这是合理的。 所有令牌都在3到5个字符之间生成(因为单词少于8个,所以很明显)。

好的,太好了,现在将其应用于字段。 而且,是的,您绝对可以一步一步完成所有操作,我只是将其分解。

$ curl -XPUT 'http://localhost:9200/searchpets/_mapping/pet' -d '
{
    "pet": {
        "properties": {
            "name": {
                "type": "string",
                "analyzer": "ngram_analyzer"
            }
        }
    }
}
'

我们在现场测试分析:

curl -XGET 'http://localhost:9200/searchpets/_analyze?field=pet.name' -d 'Raven';

再一次,我们得到了我们期望的结果:

{"tokens":[{"token":"Rav","start_offset":0,"end_offset":3,"type":"word","position":1},{"token":"Rave","start_offset":0,"end_offset":4,"type":"word","position":2},{"token":"Raven","start_offset":0,"end_offset":5,"type":"word","position":3},{"token":"ave","start_offset":1,"end_offset":4,"type":"word","position":4},{"token":"aven","start_offset":1,"end_offset":5,"type":"word","position":5},{"token":"ven","start_offset":2,"end_offset":5,"type":"word","position":6}]}

现在,假设我已经继续并在此处添加了一些记录,并针对以下内容运行简单的匹配查询:{“ query”:{“ match”:{“ name”:“ Pegasus”}}}。

利用我的数据,我们可以得到以下信息:

"hits": {
	"total": 2,
	"max_score": 0.29710895,
	"hits": [
		{
			"_index": "searchpets",
			"_type": "pet",
			"_id": "3",
			"_score": 0.29710895,
			"_source": {
				"name": "Pegasus"
			}
		}
		,{
			"_index": "searchpets",
			"_type": "pet",
			"_id": "2",
			"_score": 0.0060450486,
			"_source": {
				"name": "Degas"
			}
		}
	]
}
}

我们获得最接近的匹配以及可能实际上是用户正在寻找的关闭选项。

定制分析仪

好的,但是现在我们正在使用一个非常基本的分析器案例。 如果我们需要一个自定义分析器,以便能够处理在搜索和索引上需要不同的分词器的情况,该怎么办? 如果我们想用关键字标记器来限制搜索怎么办?

让我们对其进行更改,以使用针对n元语法的过滤器来设置自定义分析器。 由于我们在下一次搜索中使用了tokenizer关键字和match查询,因此在显示的这些测试用例中,此处的结果实际上与之前的结果相同,但是您会注意到它们的评分方式有所不同。

$ curl -XPUT 'localhost:9200/searchpets' -d '
 {
    "settings": {
        "analysis": {
            "analyzer": {
                "namegrams": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": [
                        "ngrams_filter"
                    ]
                }
            },
            "filter": {
                "ngrams_filter": {
                    "type": "ngram",
                    "min_gram": 3,
                    "max_gram": 8
                }
            }
        }
    }
}'

现在,我们像以前一样添加映射和数据:

curl -XPUT 'http://localhost:9200/searchpets/_mapping/pet' -d '
{
    "pet": {
        "properties": {
            "name": {
                "type": "string",
                "analyzer": "namegrams"
            }
        }
    }
}
'

我运行另一个匹配查询:{“ query”:{“ match”:{“ name”:“ Pegasus”}}},响应为:

hits": {
"total": 2,
"max_score": 1.1884358,
"hits": [
	{
		"_index": "searchpets",
		"_type": "pet",
		"_id": "2",
		"_score": 1.1884358,
		"_source": {
			"name": "Pegasus"
		}
	}
	,{
		"_index": "searchpets",
		"_type": "pet",
		"_id": "3",
		"_score": 0.08060065,
		"_source": {
			"name": "Degas"
		}
	}
]
}

因此,我们进行了设置,并基于关键字标记器和n-grams过滤器获得了期望的结果和评分。 假设我们正在做一些更复杂的查询。 我们可能还添加了其他一些过滤器或标记器。 情况看起来不错,对吗? 好吧,差不多。

我前面提到的所有这些要记住的一个小因素。 我们有一个最大的8克。 那么,当我们拥有一个超过该大小的名称作为搜索条件时,会发生什么呢? 好吧,根据您的搜索,您可能不会获得任何数据。

您可能没有想到这里会发生什么! 您如何避免这种情况? 一种方法是使用不同的index_analyzer和search_analyzer。 拆分这些内容可以使您更好地控制搜索。

因此,假设我们对原始搜索所说的一切都是真实的,那么您的最终设置将是这样。 我不会深入探讨查询本身的详细信息,但是我们将假定它将使用指定的search_analyzer(我建议阅读ES文档中有关如何选择分析器进行搜索的层次结构)。

注意:这里的search_ngram分析器上的小写标记器会标准化标记文本,因此所有数字都将被剥离。 此示例适用于此示例,但是使用不同的数据可能会导致意外的结果。

$ curl -XPUT 'localhost/searchpets' -d '
 {
    "settings": {
        "analysis": {
            "analyzer": {
                "namegrams": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": [
                        "ngrams_filter"
                    ]
                },
                "search_ngram": {
                    "type": "custom",
                    "tokenizer": "lowercase",
                    "filter": [
                        "truncate_filter"
                    ]
                }
            },
            "filter": {
                "ngrams_filter": {
                    "type": "ngram",
                    "min_gram": 3,
                    "max_gram": 8
                },
                "truncate_filter": {
                    "type": "truncate",
                    "length": 8
                }
            }
        }
    }
}
’

然后,最后,我们再次设置映射:

curl -XPUT 'http://localhost:9200/searchpets/_mapping/pet' -d '
{
    "pet": {
        "properties": {
            "name": {
                "type": "string",
                "index_analyzer": "namegrams",
                "search_analyzer": "search_trigram"
            }
        }
    }
}'

最后的想法

那里有。 但是,这使得假设超过8个字符的数据不太重要。 如果您要拥有比最大克数更大的数据,并且类似的数据,您可能会发现自己需要进一步调整。

在Elastisearch中使用n-gram搜索可以做的事情有很多可能性。 我希望这可以让您开始思考如何在搜索中使用它们。

翻译自: https://www.javacodegeeks.com/2015/11/anatomy-of-setting-up-an-elasticsearch-n-gram-word-analyzer.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值