英国邮政编码的 Elasticsearch 映射,能够处理间距和大写

Elasticsearch mapping for the UK postcodes, able to deal with spacing and capatalization

我正在寻找 Elasticsearch 7 的 mapping/analyzer 安装程序,其中包含英国邮政编码。我们不需要任何模糊运算符,但应该能够处理大写字母和间距的变化。

一些示例:

查询字符串: "SN13 9ED" 应该return:

但不应该 return:

默认使用关键字分析器,这似乎对间距问题敏感,但对大写字母不敏感。它还将 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 的两个,从而将它们保存在倒排索引中,在本例中为 sn131ep。在将它们存储在倒排索引中之前,我也会将它们小写。

请注意,我为您的邮政编码添加的场景是它的大小是固定的,即有 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"
        }
      }
    ]
  }
}

请注意,即使使用查询 snp131psnp13 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 1eps n 1 3 1 e psn131ep 都将匹配 SN13 1EP

我认为此选项的主要缺点是,我们将不再获得 sn13 的任何结果,因为我们不生产代币。然而,sn13* 会给我们带来结果。

是否可以将这两种方法混合在一起,以便我们两全其美?