【踩坑分析】Elasticsearch 中文 PhraseQuery 无法精确匹配问题分析

栏目: 后端 · 发布时间: 5年前

  • 首先,看下下面的例子,猜下两条查询能不能召回 doc1
PUT test_phrase
{
  "mappings" : {
      "_doc" : {
        "properties" : {
          "body" : {
            "type" : "text",
            "analyzer" : "ik_max_word",
            "search_analyzer" : "ik_smart"
          }
        }
      }
    }
}

PUT test_phrase/_doc/1
{
  "body":"南京市长"
}

GET test_phrase/_search
{
  "query": {
    "match_phrase": {
      "body": "南京市长"
    }
  }
}

GET test_phrase/_search
{
  "query": {
    "match_phrase": {
      "body": "南京"
    }
  }
}
复制代码

原因分析

  • 那么为什么呢?首先看一下,两个分词器结果不一,所以直接怀疑是由于分词不一造成的查不到的问题
# GET test_phrase/_analyze
# {
#   "text": ["南京市长"],
#   "analyzer": "ik_max_word"
# }
{
  "tokens" : [
    {
      "token" : "南京",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "南京市",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "市长",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 2
    }
  ]
}

# GET test_phrase/_analyze
# {
#   "text": ["南京市长"],
#   "analyzer": "ik_smart"
# }
{
  "tokens" : [
    {
      "token" : "南京",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "市长",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 1
    }
  ]
}
复制代码
  • 那么为什么会由于分词不一而造成问题呢?
  • 整理源码,我们可以看到 Phrase 的整体流程如下
    【踩坑分析】Elasticsearch 中文 PhraseQuery 无法精确匹配问题分析
  • 具体如下
    org.elasticsearch.index.query.MatchPhraseQueryBuilder#doToQuery
    org.elasticsearch.index.search.MatchQuery#parse
    
    • 其中 org.elasticsearch.index.search.MatchQuery#getAnalyzer 确定分词器,如果查询没指定 analyzer 的话,那么 phrase 就用 searchQuoteAnalyzer,如果没有 searchQuoteAnalyzer 则用 searchAnalyzer
      【踩坑分析】Elasticsearch 中文 PhraseQuery 无法精确匹配问题分析
    1. org.apache.lucene.util.QueryBuilder#createFieldQuery 进行分词并判断有无 graph 和同义词,如果没有就用简单的 phrase 查询
      【踩坑分析】Elasticsearch 中文 PhraseQuery 无法精确匹配问题分析
    2. org.apache.lucene.util.QueryBuilder#analyzePhrase 构建真实查询,确定每个词的 postion
      【踩坑分析】Elasticsearch 中文 PhraseQuery 无法精确匹配问题分析
    3. org.apache.lucene.search.PhraseWeight#getPhraseMatcher 召回倒排链,然后判断 slop,如果为0,则转为ExactPhraseMatcher
      【踩坑分析】Elasticsearch 中文 PhraseQuery 无法精确匹配问题分析
    4. org.apache.lucene.search.ExactPhraseMatcher#nextMatch 比较 position 是否相等
      【踩坑分析】Elasticsearch 中文 PhraseQuery 无法精确匹配问题分析

临时解法

  • 由上面第二步,可以看到,其实 ES 本身也提供了利用 searchQuoteAnalyzer 的解决方案。因此临时解法可以是让用户给 text 字段增加 search_quote_analyzer 参数, search_quote_analyzer 官方文档
  • 另外如果是单场景可以在查询时指定 analyzer 或者 querystring 里的 quote_analyzer

解决思路&难点

  • 但是让用户修改并不优雅,因此我们还是希望可以寻求在引擎层解决的方案。但是目前尚未找到一个较好方案,暂时记录下思路,以后再做补充
  • 由上面分析可知,这个问题的本质是 PhraseQuery 以 postion 位置连续性确定是否为短句,而由于写入时和查询时不同分词器,position不一致。
  • 因此,有两个思路,但都有些难点,也希望抛砖引玉,大家能指导一下

多重 Position

  • 思路: 修改 position 生成逻辑, 使得查询和写入时一致。 比如 ik_max_word 模式下有三种切分方式,就分别标记 position,而不是原来的混标,这样就可以保证 smart 也是 max 的一个子集
  • 以『南京市长江大桥维修』为例
    • 目前混标方案:

      南京 南京市 市长 长江 长江大桥 大桥 维修
      0 1 2 3 4 5 6
    • 独立标注方案:

      南京 南京市 市长 长江 长江大桥 大桥 维修
      0 - 1 2 - 3 4
      0 - 1 - 2 - 3
      - 0 - 1 - 2 3
      - 0 - - 1 - 2
  • 难点:由上例子可以看出,这种方案虽然可以保证 position 的一致性,但是一旦有歧义词,则会造成后继词位置全不一样,会造成大量数据膨胀。如使用此方法,则需要找到一种快速记录查找多重 position 的方法

以上所述就是小编给大家介绍的《【踩坑分析】Elasticsearch 中文 PhraseQuery 无法精确匹配问题分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

人类2.0

人类2.0

皮埃罗∙斯加鲁菲(Piero Scaruffi) / 闫景立、牛金霞 / 中信出版集团股份有限公司 / 2017-2-1 / CNY 68.00

《人类2.0:在硅谷探索科技未来》从在众多新技术中选择了他认为最有潜力塑造科技乃至人类未来的新技术进行详述,其中涉及大数据、物联网、人工智能、纳米科技、虚拟现实、生物技术、社交媒体、区块链、太空探索和3D打印。皮埃罗用一名硅谷工程师的严谨和一名历史文化学者的哲学视角,不仅在书中勾勒出这些新技术的未来演变方向和面貌,还对它们对社会和人性的影响进行了深入思考。 为了补充和佐证其观点,《人类2.0......一起来看看 《人类2.0》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具