【问题标题】:How to fuzzy match email or telephone by Elasticsearch?如何通过 Elasticsearch 模糊匹配电子邮件或电话?
【发布时间】:2016-07-12 00:06:56
【问题描述】:

我想通过 Elasticsearch 对电子邮件或电话进行模糊匹配。例如:

匹配所有以@gmail.com结尾的电子邮件

匹配所有以136开头的电话。

我知道我可以使用通配符,

{
 "query": {
    "wildcard" : {
      "email": "*gmail.com"
    }
  }
}

但是性能很差。我尝试使用正则表达式:

{"query": {"regexp": {"email": {"value": "*163\.com*"} } } }

但不起作用。

有没有更好的方法?

curl -XGET localhost:9200/user_data

{
    "user_data": {
        "aliases": {},
        "mappings": {
            "user_data": {
                "properties": {
                    "address": {
                        "type": "string"
                    },
                    "age": {
                        "type": "long"
                    },
                    "comment": {
                        "type": "string"
                    },
                    "created_on": {
                        "type": "date",
                        "format": "dateOptionalTime"
                    },
                    "custom": {
                        "properties": {
                            "key": {
                                "type": "string"
                            },
                            "value": {
                                "type": "string"
                            }
                        }
                    },
                    "gender": {
                        "type": "string"
                    },
                    "name": {
                        "type": "string"
                    },
                    "qq": {
                        "type": "string"
                    },
                    "tel": {
                        "type": "string"
                    },
                    "updated_on": {
                        "type": "date",
                        "format": "dateOptionalTime"
                    },
                }
            }
        },
        "settings": {
            "index": {
                "creation_date": "1458832279465",
                "uuid": "Fbmthc3lR0ya51zCnWidYg",
                "number_of_replicas": "1",
                "number_of_shards": "5",
                "version": {
                    "created": "1070299"
                }
            }
        },
        "warmers": {}
    }
}

映射:

{
  "settings": {
    "analysis": {
      "analyzer": {
        "index_phone_analyzer": {
          "type": "custom",
          "char_filter": [ "digit_only" ],
          "tokenizer": "digit_edge_ngram_tokenizer",
          "filter": [ "trim" ]
        },
        "search_phone_analyzer": {
          "type": "custom",
          "char_filter": [ "digit_only" ],
          "tokenizer": "keyword",
          "filter": [ "trim" ]
        },
        "index_email_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [ "lowercase", "name_ngram_filter", "trim" ]
        },
        "search_email_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [ "lowercase", "trim" ]
        }
      },
      "char_filter": {
        "digit_only": {
          "type": "pattern_replace",
          "pattern": "\\D+",
          "replacement": ""
        }
      },
      "tokenizer": {
        "digit_edge_ngram_tokenizer": {
          "type": "edgeNGram",
          "min_gram": "3",
          "max_gram": "15",
          "token_chars": [ "digit" ]
        }
      },
      "filter": {
        "name_ngram_filter": {
          "type": "ngram",
          "min_gram": "3",
          "max_gram": "20"
        }
      }
    }
  },
  "mappings" : {
    "user_data" : {
      "properties" : {
        "name" : {
          "type" : "string",
          "analyzer" : "ik"
        },
        "age" : {
          "type" : "integer"
        },
        "gender": {
          "type" : "string"
        },
        "qq" : {
          "type" : "string"
        },
        "email" : {
          "type" : "string",
          "analyzer": "index_email_analyzer",
          "search_analyzer": "search_email_analyzer"
        },
        "tel" : {
          "type" : "string",
          "analyzer": "index_phone_analyzer",
          "search_analyzer": "search_phone_analyzer"
        },
        "address" : {
          "type": "string",
          "analyzer" : "ik"
        },
        "comment" : {
          "type" : "string",
          "analyzer" : "ik"
        },
        "created_on" : {
          "type" : "date",
          "format" : "dateOptionalTime"
        },
        "updated_on" : {
          "type" : "date",
          "format" : "dateOptionalTime"
        },
        "custom": {
          "type" : "nested",
          "properties" : {
            "key" : {
              "type" : "string"
            },
            "value" : {
              "type" : "string"
            }
          }
        }
      }
    }
  }
}

【问题讨论】:

    标签: mysql elasticsearch


    【解决方案1】:

    一个简单的方法是创建一个自定义分析器,它使用n-gram token filter 来处理电子邮件(=> 见下文index_email_analyzersearch_email_analyzer + email_url_analyzer 用于精确匹配电子邮件)和edge-ngram token filter对于手机(=> 见下文 index_phone_analyzersearch_phone_analyzer)。

    完整的索引定义如下。

    PUT myindex
    {
      "settings": {
        "analysis": {
          "analyzer": {
            "email_url_analyzer": {
              "type": "custom",
              "tokenizer": "uax_url_email",
              "filter": [ "trim" ]
            },
            "index_phone_analyzer": {
              "type": "custom",
              "char_filter": [ "digit_only" ],
              "tokenizer": "digit_edge_ngram_tokenizer",
              "filter": [ "trim" ]
            },
            "search_phone_analyzer": {
              "type": "custom",
              "char_filter": [ "digit_only" ],
              "tokenizer": "keyword",
              "filter": [ "trim" ]
            },
            "index_email_analyzer": {
              "type": "custom",
              "tokenizer": "standard",
              "filter": [ "lowercase", "name_ngram_filter", "trim" ]
            },
            "search_email_analyzer": {
              "type": "custom",
              "tokenizer": "standard",
              "filter": [ "lowercase", "trim" ]
            }
          },
          "char_filter": {
            "digit_only": {
              "type": "pattern_replace",
              "pattern": "\\D+",
              "replacement": ""
            }
          },
          "tokenizer": {
            "digit_edge_ngram_tokenizer": {
              "type": "edgeNGram",
              "min_gram": "1",
              "max_gram": "15",
              "token_chars": [ "digit" ]
            }
          },
          "filter": {
            "name_ngram_filter": {
              "type": "ngram",
              "min_gram": "1",
              "max_gram": "20"
            }
          }
        }
      },
      "mappings": {
        "your_type": {
          "properties": {
            "email": {
              "type": "string",
              "analyzer": "index_email_analyzer",
              "search_analyzer": "search_email_analyzer"
            },
            "phone": {
              "type": "string",
              "analyzer": "index_phone_analyzer",
              "search_analyzer": "search_phone_analyzer"
            }
          }
        }
      }
    }
    

    现在,让我们一点一点地剖析它。

    对于phone 字段,想法是用index_phone_analyzer 索引电话值,它使用边缘ngram 标记器来索引电话号码的所有前缀。因此,如果您的电话号码是1362435647,则会产生以下令牌:113136136213624136243136243513624356、@ 987654340@,1362435641362435647

    然后在搜索时,我们使用另一个分析器search_phone_analyzer,它将简单地获取输入数字(例如136)并使用简单的matchterm 查询将其与phone 字段匹配:

    POST myindex
    { 
        "query": {
            "term": 
                { "phone": "136" }
        }
    }
    

    对于email 字段,我们以类似的方式进行,因为我们使用index_email_analyzer 索引电子邮件值,它使用一个ngram 标记过滤器,它将产生所有可能的不同长度的标记(介于1 和20 个字符),可以从电子邮件值中获取。例如:john@gmail.com 将被标记为 jjojoh、...、gmail.com、...、john@gmail.com

    然后在搜索时,我们将使用另一个名为 search_email_analyzer 的分析器,它将获取输入并尝试将其与索引标记进行匹配。

    POST myindex
    { 
        "query": {
            "term": 
                { "email": "@gmail.com" }
        }
    }
    

    email_url_analyzer 分析器未在此示例中使用,但我已将其包括在内,以防您需要匹配确切的电子邮件值。

    【讨论】:

    • 哇!太棒了!
    • 您可以调整 n-gram 最小/最大参数,但此设置支持很多搜索案例。
    • 取决于许多因素(硬件、数据量等),这就是为什么您必须尝试并根据您的需要进行调整的原因。我只是向您展示了一种可行的方法,现在您需要根据自己的喜好对其进行铺平:)
    • 您可能需要擦除索引和数据并从头开始重建。然后说明您要发送的查询。
    • 如果我搜索“136”,所有文档都将匹配。我应该更改 min_gram 吗?
    猜你喜欢
    • 1970-01-01
    • 2017-06-15
    • 2015-04-13
    • 1970-01-01
    • 1970-01-01
    • 2016-03-10
    • 1970-01-01
    • 2022-11-10
    • 2016-03-20
    相关资源
    最近更新 更多