zoukankan      html  css  js  c++  java
  • Elasticsearch之match_phrase小坑记录

    1、问题抛出

    某个词组在Elasitcsearch中的某个document中存在,就一定通过某种匹配方式把它搜出来。 
    举例:

    title=公路局正在治理解放大道路面积水问题。

    输入关键词:道路,能否搜索到这个document呢? 
    实际应用中可能需要: 
    1)检索关键词”理解”、”解放”、”道路”、“理解放大”,都能搜出这篇文档。 
    2)单个的字拆分“治”、“水”太多干扰,不要被检索出来。 
    3)待检索的词不在词典中,也必须要查到。 
    4)待检索词只要在原文title或content中出现,都要检索到。 
    5)检索要快,要摒弃wildcard模糊匹配性能问题。

    2、问题分析

    常用的stand标准分词,可以满足要求1)、3)、4)、5)。 
    标准分词器是什么鬼? 
    标准分析仪是默认分析仪,如果没有指定,则默认使用该分词器。 它提供了基于语法的标记,并且适用于大多数语言。 
    对于中文字符串,会逐个汉字分词。 
    标准分词器的结果如下:

    GET /ik_index/_analyze?analyzer=standard
    {
    "text":"公路局正在治理解放大道路面积水问题"
    }
    公,路,局,正,在,治,理,解,放,大,道,路,面,积,水,问,题

    但,会出现冗余数据非常多。 

    针对要求2),排除match检索,排除stand分词。 
    针对要求5),排除wildcard模糊检索。 
    针对要求3)、4),新词也要被检索到比如:“声临其境”、“孙大剩”等也要能被搜索到。 
    针对要求1),采用match_phrase貌似靠谱些。

    3、小试牛刀

    先使用IK-max-word细粒度分词器,结合match_phrase试一试?

    步骤1:定义索引和Mapping

    PUT ik_index
    {
      "mappings":{
      "ik_type":{
      "properties":{
      "title":{
      "type":"text",
      "fields":{
      "ik_my_max":{
      "type":"text",
      "analyzer":"ik_max_word"
      },
      "ik_my_smart":{
      "type":"text",
      "analyzer":"ik_smart"
      },
      "keyword":{
      "type":"keyword",
      "ignore_above":256
      }
      }
      }
      }
      }
      }
      }

    这里,为了验证分词,同时使用了ik_smart和ik_max两种分词。 

    实际开发中不需要,因为:两种分词共存,会导致导入数据创建索引的时候,索引会非常大,对磁盘和检索性能都会有影响。

    步骤2:插入文档

    POST ik_index/ik_type/3
    {
      "title":"公路局正在治理解放大道路面积水问题"
    }

    步骤3:实施检索

    POST ik_index/ik_type/_search
    {
      "profile":"true",
      "query":
      {
      "match_phrase":
      {
      "title.ik_my_max":"道路"
      }
      }
    }

    搜索结果如下: 
    无结果返回。

    {
      "took": 1,
      "timed_out": false,
      "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
      },
      "hits": {
      "total": 0,
      "max_score": null,
      "hits": []
      }
    }

    为什么使用了max_word细粒度分词,使用了match_pharse检索,为什么没有结果。 
    分析一下: 
    细粒度ik_max_word分词结果为:

    GET /ik_index/_analyze?analyzer=ik_max_word
    {
    "text":"公路局正在治理解放大道路面积水问题"
    }
    公路局 ,公路 ,路局 ,路 ,局正 ,正在 ,正 ,治理 ,治 ,理解 ,
    理 ,解放 ,解 ,放大 ,大道 ,大 ,道路 ,道 ,路面 ,路 ,
    面积 ,面 ,积水 ,积 ,水 ,问题

    以上方式,除了可以返回分词结果外,还能返回词所在的位置position。

    构建索引的时候,道路被拆分为:道路:16,道:17,路:19。(注意中间加了18:路面)

    {
      "token": "路面",
      "start_offset": 11,
      "end_offset": 13,
      "type": "CN_WORD",
      "position": 18
      }

    而检索的时候,而道路拆分为: 道路0 道1 路2

    match_phrase检索时候,文档必须同时满足以下两个条件,才能被检索到: 
    1)分词后所有词项都出现在该字段中; 
    2)字段中的词项顺序要一致。 
    位置信息可以被存储在倒排索引中,因此 match_phrase 查询这类对词语位置敏感的查询, 就可以利用位置信息去匹配包含所有查询词项,且各词项顺序也与我们搜索指定一致的文档,中间不夹杂其他词项。

    为了验证如上的解释,新增一篇“道路”相关的title,检验一下:

    POST ik_index/ik_type/4
    {
      "title":"党员干部坚持走马克思主义道路的重要性"
    }

    注意:这时,搜索道路是可以匹配到的。

    "hits": {
      "total": 1,
      "max_score": 1.9684901,
      "hits": [
      {
      "_index": "ik_index",
      "_type": "ik_type",
      "_id": "4",
      "_score": 1.9684901,
      "_source": {
      "title": "党员干部坚持走马克思主义道路的重要性"
      }
      }
      ]
      },

    细粒度ik_max_word分词结果为:

    党员干部, 党员, 干部, 坚持走, 坚持, 坚, 持, 走马, 马克思主义, 马克思,
    马克, 马, 克, 思, 主义, 道路, 道, 路, 重要性, 重要,
    要性, 性

    构建索引的时候,道路被拆分为:15,16,17位置。 
    与检索的词项顺序是一致的。 
    这里解析更详细:http://t.cn/R8pzw9e

    4、match_pharse都搜不出来,还有没有别的方案?

    有,和match_pharse类似,不过match_phrase_prefix支持最后一个term前缀匹配。 
    除了把查询文本的最后一个分词只做前缀匹配之外,match_phrase_prefix和match_phrase查询基本一样,参数 max_expansions 控制最后一个单词会被重写成多少个前缀,也就是,控制前缀扩展成分词的数量,默认值是50(官网文档建议50)。 
    扩展的前缀数量越多,找到的文档数量就越多; 
    如果前缀扩展的数量太少,可能查找不到相应的文档,遗漏数据。

    POST ik_index/ik_type/_search
    {
      "profile":"true",
      "query":
      {
      "match_phrase_prefix" : {
      "title.ik_my_max" : {
      "query": "道路",
      "max_expansions": 50
      }
      }
    }
    }

    经验证: 关键词”理解”、”解放”、”道路”、“理解放大”,都能搜出这篇文档。

    5、应用场景

    我们自己开发搜索引擎的时候,经常会出现基于title或者content字段进行检索。 
    如果用match检索,会出现噪音很多的情况; 
    如果用match_phrase,会出现某些字段检索不出来的情况,如上分析的“道路”; 
    如果用wildcard,能检索出来,但又有性能问题的存在。 
    这时候,可以考虑下: match_phrase_prefix。

    6、小结

    实际开发中,根据应用场景不同,采用不同的分词器。 
    如果选用ik,建议使用ik_max_word分词,因为:ik_max_word的分词结果包含ik_smart。 
    匹配的时候,如果想尽可能的多检索结果,考虑使用match; 
    如果想尽可能精确的匹配分词结果,考虑使用match_phrase; 
    如果短语匹配的时候,怕遗漏,考虑使用match_phrase_prefix。

  • 相关阅读:
    实验 7 综合练习一
    实验或作业模版: 实验 6-1 最大公约数 最小公倍数
    实验 6 数组1
    Pro
    作业 4 函数应用
    老大
    双端队列
    zxa and leaf
    Baby Ming and Matrix games
    The more, The Better
  • 原文地址:https://www.cnblogs.com/pyspark/p/8817706.html
Copyright © 2011-2022 走看看