elasticsearch 使用分词之后match_phrase 字符串精确查找的坑

  • 在使用ES对字符串进行精确查找时,通常将使用match_phrase查询,但当我们对查询的字段进行分词之后,直接使用match_phrase查询可能达不到预期的效果。如:
    某个Index中存有如下内容
 {
      "id": "1",
      "title": "努力改善农业生态环境"
  }

当我们需要在title字段中查询“农业生态”字段时,将查询不到任何记录

{
  "query": {
    "match_phrase": {
      "title": {
        "query": "农业生态"
      }
    }
  }
}

为什么title字段中包含有这个子字符串我们却查询不到呢,首先将title中的字段进行分词,看看结果:

{
    "tokens":[
        {
            "token":"努力",
            "start_offset":0,
            "end_offset":2,
            "type":"ad",
            "position":0
        },
        {
            "token":"改善",
            "start_offset":2,
            "end_offset":4,
            "type":"n",
            "position":1
        },
        {
            "token":"农业",
            "start_offset":4,
            "end_offset":6,
            "type":"n",
            "position":2
        },
        {
            "token":"生态环境",
            "start_offset":6,
            "end_offset":10,
            "type":"nz",
            "position":3
        },
        {
            "token":"生态",
            "start_offset":6,
            "end_offset":8,
            "type":"n",
            "position":4
        },
        {
            "token":"环境",
            "start_offset":8,
            "end_offset":10,
            "type":"n",
            "position":5
        }
    ]
}

在上面的分词中,“农业生态环境”为分成了:农业、生态环境、生态、环境 这几个词
注意每个词条position的值,在农业和生态之间有一个生态环境,使得农业和生态的position位置相差了一位

下面我们在看一下我们需要查询的字符串的分词情况:

{
    "tokens":[
        {
            "token":"农业",
            "start_offset":0,
            "end_offset":2,
            "type":"n",
            "position":0
        },
        {
            "token":"生态",
            "start_offset":2,
            "end_offset":4,
            "type":"n",
            "position":1
        }
    ]
}

可以看见我们需要查询的短语被分成了两个词条,并且他们的position位置是相邻的。我们再来看一下官方对match_phrase语法的解释:

Like the match query, the match_phrase query first analyzes the query string to produce a list of terms. It then searches for all the terms, but keeps only documents that contain all of the search terms, in the same positions relative to each other.

大致意思是说:使用match_phrase查询时,先对需要查询的字符串进行分词,然后返回包含所有词条的文档。最后一句in the same positions relative to each other.意思就是说用match_phrase查找时, 查找分词器分出的词的位置和要建索引时分出的词的位置一样。
在看我们上面的例子:

存储的原文:农业生态环境
分词及position:农业 0 生态环境 1 生态 2 环境 3
{
  "analyzer": "hanlp-index",
  "text": "农业生态生态"
}
{
    "tokens":[
        {
            "token":"农业",
            "start_offset":0,
            "end_offset":2,
            "type":"n",
            "position":0
        },
        {
            "token":"生态环境",
            "start_offset":2,
            "end_offset":6,
            "type":"nz",
            "position":1
        },
        {
            "token":"生态",
            "start_offset":2,
            "end_offset":4,
            "type":"n",
            "position":2
        },
        {
            "token":"环境",
            "start_offset":4,
            "end_offset":6,
            "type":"n",
            "position":3
        }
    ]
}

查询的字符串:

需要查询的字符串:农业生态
分词及position:农业 0  生态 1
{
  "analyzer": "hanlp-index",
  "text": "农业生态"
}
{
    "tokens":[
        {
            "token":"农业",
            "start_offset":0,
            "end_offset":2,
            "type":"n",
            "position":0
        },
        {
            "token":"生态",
            "start_offset":2,
            "end_offset":4,
            "type":"n",
            "position":1
        }
    ]
}

从上面的例子可以看出,数据index时,农业生态的position位置分别为 0和2
而我们检索的时候农业生态的position位置分别为0和1
在match_phrase看来,这种是不匹配的,所以使用农业生态检索不出农业生态环境的记录。这就是官方对match_phrase语法最后一句的解释。

解决方法

方法一

对需要查询字符串的字段使用默认分词。默认分词将会把每一个汉字分开,一个汉字作为一个词条

设置mapping的时候对字段使用默认分词(直接指定字段类型为text即可)
{
    "title":{
        "type":"text"
    }
}

针对于汉字就是一个一个分,这种肯定是可以查全的。但一个一个字分的话,每个字对应的文档集合非常多,如果数据量达到了百亿,在求交集,计算距离时,效果非常差。

方法二

在使用match_phrase查询的时候指定slop参数

{
  "query": {
    "match_phrase": {
      "title": {
        "query": "农业生态",
        "slop": 1
      }
    }
  }
}

slop参数的值即表示允许词条position的差值。使用该方法虽然可以解决上面的问题,但是在查询的返回列表中将会返回一些不满足情况的数据,如下面的查询:

{
  "query": {
    "match_phrase": {
      "title": {
        "query": "改善环境",
        "slop": 1
      }
    }
  }
}

返回的数据中将包含:
“改善投资环境“ 的数据。因为在改善投资环境中,改善和投资的position也是相差1的。

方法三

使用match_phrase_prefix 语法查询。该语法在短语查询的基础上还加上了词条的前缀匹配。

{
  "query": {
    "match_phrase_prefix": {
      "title": {
        "query": "农业生态"
      }
    }
  }
}

基本可以达到精确查询的需求

你可能感兴趣的:(elasticsearch 使用分词之后match_phrase 字符串精确查找的坑)