英国邮政编码的 Elasticsearch 映射,能够处理间距和大写
Elasticsearch mapping for the UK postcodes, able to deal with spacing and capatalization
我正在寻找 Elasticsearch 7 的 mapping/analyzer 安装程序,其中包含英国邮政编码。我们不需要任何模糊运算符,但应该能够处理大写字母和间距的变化。
一些示例:
查询字符串: "SN13 9ED" 应该return:
- sn139ed
- SN13 9ED
- Sn13 9ed
但不应该 return:
- SN13 1EP
- SN131EP
默认使用关键字分析器,这似乎对间距问题敏感,但对大写字母不敏感。它还将 return 匹配 SN13 1EP
除非我们将查询指定为 SN13 AND 9ED
,这是我们不想要的。
此外,使用关键字分析器,SN13 9ED
return 的查询是 SN13 1EP
的结果,其相关性高于 SN13 9ED
,即使这应该是准确的匹配。为什么同一字符串中的 2 个匹配项的相关性低于仅 1 个匹配项的相关性?
邮编映射
"post_code": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
查询
"query" => array:1 [▼
"query_string" => array:1 [▼
"query" => "KT2 7AJ"
]
]
我相信根据我的评论,当您的搜索字符串为 SN13 9ED
.
时,您可能已经能够过滤掉 SN13 1EP
希望您知道 Analysis
is, how Analyzers
work on text
field and how by default Standard Analyzer
在标记最终存储在倒排索引中之前应用于标记的内容。请注意,这仅适用于 text
个字段。
查看您的映射,如果您在 post_code
而不是 post_code.keyword
上使用搜索,我相信大写问题会得到解决,因为 text
字段的 ES 默认使用 Standard Analyzer
这意味着你的标记最终会以小写格式保存在索引中,即使在查询时,ES 在查询期间,也会在搜索倒排索引之前应用分析器。
请注意,默认情况下,在索引时间和搜索时间该字段
期间应用映射中配置的相同分析器
对于你有 sn131ep
的场景,我所做的是利用 Pattern Capture Token Filter ,我在其中指定了一个会破坏的正则表达式将令牌分成长度分别为 4 和 3 的两个,从而将它们保存在倒排索引中,在本例中为 sn13
和 1ep
。在将它们存储在倒排索引中之前,我也会将它们小写。
请注意,我为您的邮政编码添加的场景是它的大小是固定的,即有 7 个字符。如果不是这样,您可以添加更多模式
详情请见下方:
映射:
PUT my_postcode_index
{
"settings" : {
"analysis" : {
"filter" : {
"mypattern" : {
"type" : "pattern_capture",
"preserve_original" : true,
"patterns" : [
"(\w{4}+)|(\w{3}+)", <--- Note this and feel free to add more patterns
"\s" <--- Filter based on whitespace
]
}
},
"analyzer" : {
"my_analyzer" : {
"tokenizer" : "pattern",
"filter" : [ "mypattern", "lowercase" ] <--- Note the lowercase here
}
}
}
},
"mappings": {
"properties": {
"postcode":{
"type": "text",
"analyzer": "my_analyzer", <--- Note this
"fields":{
"keyword":{
"type": "keyword"
}
}
}
}
}
}
示例文档:
POST my_postcode_index/_doc/1
{
"postcode": "SN131EP"
}
POST my_postcode_index/_doc/2
{
"postcode": "sn13 1EP"
}
POST my_postcode_index/_doc/3
{
"postcode": "sn131ep"
}
请注意,这些文档在语义上是相同的。
请求查询:
POST my_postcode_index/_search
{
"query": {
"query_string": {
"default_field": "postcode",
"query": "SN13 1EP",
"default_operator": "AND"
}
}
}
回复:
{
"took" : 24,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 0.6246513,
"hits" : [
{
"_index" : "my_postcode_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.6246513,
"_source" : {
"postcode" : "SN131EP"
}
},
{
"_index" : "my_postcode_index",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.6246513,
"_source" : {
"postcode" : "sn131ep"
}
},
{
"_index" : "my_postcode_index",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.5200585,
"_source" : {
"postcode" : "sn13 1EP"
}
}
]
}
}
请注意,即使使用查询 snp131p
和 snp13 1ep
,也会返回所有三个文档。
补充说明:
您可以使用 Analyze API 来确定为特定文本创建了哪些标记
POST my_postcode_index/_analyze
{
"analyzer": "my_analyzer",
"text": "sn139ed"
}
你可以在下面看到倒排索引中存储了哪些标记。
{
"tokens" : [
{
"token" : "sn139ed",
"start_offset" : 0,
"end_offset" : 7,
"type" : "word",
"position" : 0
},
{
"token" : "sn13",
"start_offset" : 0,
"end_offset" : 7,
"type" : "word",
"position" : 0
},
{
"token" : "9ed",
"start_offset" : 0,
"end_offset" : 7,
"type" : "word",
"position" : 0
}
]
}
还有:
您可能还想阅读有关 Ngram Tokenizer 的内容。我建议您试用这两种解决方案,看看哪种最适合您的输入。
请测试一下,如果您有任何疑问,请告诉我。
除了Opsters的回答,下面的也可以从相反的角度来解决问题。对于 Opster 的回答,他们建议通过已知的邮政编码模式拆分值,这很好。
如果我们不知道模式,可以使用以下方法:
{
"analysis": {
"filter": {
"whitespace_remove": {
"pattern": " ",
"type": "pattern_replace",
"replacement": ""
}
},
"analyzer": {
"no_space_analyzer": {
"filter": [
"lowercase",
"whitespace_remove"
],
"tokenizer": "keyword"
}
}
}
}
{
"post_code": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
},
"analyzer": "no_space_analyzer"
}
}
由于小写过滤器,这允许我们使用任何间距和任何大小写进行搜索。
sn13 1ep
、s n 1 3 1 e p
、sn131ep
都将匹配 SN13 1EP
我认为此选项的主要缺点是,我们将不再获得 sn13
的任何结果,因为我们不生产代币。然而,sn13*
会给我们带来结果。
是否可以将这两种方法混合在一起,以便我们两全其美?
我正在寻找 Elasticsearch 7 的 mapping/analyzer 安装程序,其中包含英国邮政编码。我们不需要任何模糊运算符,但应该能够处理大写字母和间距的变化。
一些示例:
查询字符串: "SN13 9ED" 应该return:
- sn139ed
- SN13 9ED
- Sn13 9ed
但不应该 return:
- SN13 1EP
- SN131EP
默认使用关键字分析器,这似乎对间距问题敏感,但对大写字母不敏感。它还将 return 匹配 SN13 1EP
除非我们将查询指定为 SN13 AND 9ED
,这是我们不想要的。
此外,使用关键字分析器,SN13 9ED
return 的查询是 SN13 1EP
的结果,其相关性高于 SN13 9ED
,即使这应该是准确的匹配。为什么同一字符串中的 2 个匹配项的相关性低于仅 1 个匹配项的相关性?
邮编映射
"post_code": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
查询
"query" => array:1 [▼
"query_string" => array:1 [▼
"query" => "KT2 7AJ"
]
]
我相信根据我的评论,当您的搜索字符串为 SN13 9ED
.
SN13 1EP
希望您知道 Analysis
is, how Analyzers
work on text
field and how by default Standard Analyzer
在标记最终存储在倒排索引中之前应用于标记的内容。请注意,这仅适用于 text
个字段。
查看您的映射,如果您在 post_code
而不是 post_code.keyword
上使用搜索,我相信大写问题会得到解决,因为 text
字段的 ES 默认使用 Standard Analyzer
这意味着你的标记最终会以小写格式保存在索引中,即使在查询时,ES 在查询期间,也会在搜索倒排索引之前应用分析器。
请注意,默认情况下,在索引时间和搜索时间该字段
期间应用映射中配置的相同分析器对于你有 sn131ep
的场景,我所做的是利用 Pattern Capture Token Filter ,我在其中指定了一个会破坏的正则表达式将令牌分成长度分别为 4 和 3 的两个,从而将它们保存在倒排索引中,在本例中为 sn13
和 1ep
。在将它们存储在倒排索引中之前,我也会将它们小写。
请注意,我为您的邮政编码添加的场景是它的大小是固定的,即有 7 个字符。如果不是这样,您可以添加更多模式
详情请见下方:
映射:
PUT my_postcode_index
{
"settings" : {
"analysis" : {
"filter" : {
"mypattern" : {
"type" : "pattern_capture",
"preserve_original" : true,
"patterns" : [
"(\w{4}+)|(\w{3}+)", <--- Note this and feel free to add more patterns
"\s" <--- Filter based on whitespace
]
}
},
"analyzer" : {
"my_analyzer" : {
"tokenizer" : "pattern",
"filter" : [ "mypattern", "lowercase" ] <--- Note the lowercase here
}
}
}
},
"mappings": {
"properties": {
"postcode":{
"type": "text",
"analyzer": "my_analyzer", <--- Note this
"fields":{
"keyword":{
"type": "keyword"
}
}
}
}
}
}
示例文档:
POST my_postcode_index/_doc/1
{
"postcode": "SN131EP"
}
POST my_postcode_index/_doc/2
{
"postcode": "sn13 1EP"
}
POST my_postcode_index/_doc/3
{
"postcode": "sn131ep"
}
请注意,这些文档在语义上是相同的。
请求查询:
POST my_postcode_index/_search
{
"query": {
"query_string": {
"default_field": "postcode",
"query": "SN13 1EP",
"default_operator": "AND"
}
}
}
回复:
{
"took" : 24,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 0.6246513,
"hits" : [
{
"_index" : "my_postcode_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.6246513,
"_source" : {
"postcode" : "SN131EP"
}
},
{
"_index" : "my_postcode_index",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.6246513,
"_source" : {
"postcode" : "sn131ep"
}
},
{
"_index" : "my_postcode_index",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.5200585,
"_source" : {
"postcode" : "sn13 1EP"
}
}
]
}
}
请注意,即使使用查询 snp131p
和 snp13 1ep
,也会返回所有三个文档。
补充说明:
您可以使用 Analyze API 来确定为特定文本创建了哪些标记
POST my_postcode_index/_analyze
{
"analyzer": "my_analyzer",
"text": "sn139ed"
}
你可以在下面看到倒排索引中存储了哪些标记。
{
"tokens" : [
{
"token" : "sn139ed",
"start_offset" : 0,
"end_offset" : 7,
"type" : "word",
"position" : 0
},
{
"token" : "sn13",
"start_offset" : 0,
"end_offset" : 7,
"type" : "word",
"position" : 0
},
{
"token" : "9ed",
"start_offset" : 0,
"end_offset" : 7,
"type" : "word",
"position" : 0
}
]
}
还有:
您可能还想阅读有关 Ngram Tokenizer 的内容。我建议您试用这两种解决方案,看看哪种最适合您的输入。
请测试一下,如果您有任何疑问,请告诉我。
除了Opsters的回答,下面的也可以从相反的角度来解决问题。对于 Opster 的回答,他们建议通过已知的邮政编码模式拆分值,这很好。
如果我们不知道模式,可以使用以下方法:
{
"analysis": {
"filter": {
"whitespace_remove": {
"pattern": " ",
"type": "pattern_replace",
"replacement": ""
}
},
"analyzer": {
"no_space_analyzer": {
"filter": [
"lowercase",
"whitespace_remove"
],
"tokenizer": "keyword"
}
}
}
}
{
"post_code": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
},
"analyzer": "no_space_analyzer"
}
}
由于小写过滤器,这允许我们使用任何间距和任何大小写进行搜索。
sn13 1ep
、s n 1 3 1 e p
、sn131ep
都将匹配 SN13 1EP
我认为此选项的主要缺点是,我们将不再获得 sn13
的任何结果,因为我们不生产代币。然而,sn13*
会给我们带来结果。
是否可以将这两种方法混合在一起,以便我们两全其美?