就像 match
查询对于标准全文检索是一种最常用的查询一样,当你想找到彼此邻近搜索词的查询方法时,就会想到 match_phrase
查询 。
GET /my_index/my_type/_search
{
"query": {
"match_phrase": {
"title": "quick brown fox"
}
}
}
类似
match
查询,
match_phrase
查询首先将查询字符串解析成一个词项列表,然后对这些词项进行搜索,但只保留那些包含
全部
搜索词项,且
位置
与搜索词项相同的文档。 比如对于
quick fox
的短语搜索可能不会匹配到任何文档,因为没有文档包含的
quick
词之后紧跟着
fox
。
词项的位置编辑
当一个字符串被分词后,这个分析器不但会 返回一个词项列表,而且还会返回各词项在原始字符串中的 位置 或者顺序关系:
GET /_analyze?analyzer=standard
Quick brown fox
{
"tokens": [
{
"token": "quick",
"start_offset": 0,
"end_offset": 5,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "brown",
"start_offset": 6,
"end_offset": 11,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "fox",
"start_offset": 12,
"end_offset": 15,
"type": "<ALPHANUM>",
"position": 3
}
]
}
位置信息可以被存储在倒排索引中,因此 match_phrase
查询这类对词语位置敏感的查询, 就可以利用位置信息去匹配包含所有查询词项,且各词项顺序也与我们搜索指定一致的文档,中间不夹杂其他词项。
什么是短语编辑
一个被认定为和短语 quick brown fox
匹配的文档,必须满足以下这些要求:
-
quick
、brown
和fox
需要全部出现在域中。 -
brown
的位置应该比quick
的位置大1
。 -
fox
的位置应该比quick
的位置大2
。
如果以上任何一个选项不成立,则该文档不能认定为匹配。
混合起来编辑
精确短语匹配 或许是过于严格了。也许我们想要包含 “quick brown fox” 的文档也能够匹配 “quick fox,” , 尽管情形不完全相同。
GET /my_index/my_type/_search
{
"query": {
"match_phrase": {
"title": {
"query": "quick fox",
"slop": 1
}
}
}
}
slop
参数告诉 match_phrase
查询词条相隔多远时仍然能将文档视为匹配 。 相隔多远的意思是为了让查询和文档匹配你需要移动词条多少次?
我们以一个简单的例子开始吧。 为了让查询 quick fox
能匹配一个包含 quick brown fox
的文档, 我们需要 slop
的值为 1
:
Pos 1 Pos 2 Pos 3 ----------------------------------------------- Doc: quick brown fox ----------------------------------------------- Query: quick fox Slop 1: quick ↳ fox
尽管在使用了 slop
短语匹配中所有的单词都需要出现, 但是这些单词也不必为了匹配而按相同的序列排列。 有了足够大的 slop
值, 单词就能按照任意顺序排列了。
为了使查询 fox quick
匹配我们的文档, 我们需要 slop
的值为 3
:
Pos 1 Pos 2 Pos 3 ----------------------------------------------- Doc: quick brown fox ----------------------------------------------- Query: fox quick Slop 1: fox|quick ↵Slop 2: quick ↳ fox Slop 3: quick ↳ fox
多值字段编辑
对多值字段使用短语匹配时会发生奇怪的事。 想象一下你索引这个文档:
PUT /my_index/groups/1 { "names": [ "John Abraham", "Lincoln Smith"] }
然后运行一个对 Abraham Lincoln
的短语查询:
GET /my_index/groups/_search { "query": { "match_phrase": { "names": "Abraham Lincoln" } } }
令人惊讶的是, 即使 Abraham
和 Lincoln
在 names
数组里属于两个不同的人名, 我们的文档也匹配了查询。 这一切的原因在Elasticsearch数组的索引方式。
在分析 John Abraham
的时候, 产生了如下信息:
- Position 1:
john
- Position 2:
abraham
然后在分析 Lincoln Smith
的时候, 产生了:
- Position 3:
lincoln
- Position 4:
smith
换句话说, Elasticsearch对以上数组分析生成了与分析单个字符串 John Abraham Lincoln Smith
一样几乎完全相同的语汇单元。 我们的查询示例寻找相邻的 lincoln
和 abraham
, 而且这两个词条确实存在,并且它们俩正好相邻, 所以这个查询匹配了。
幸运的是, 在这样的情况下有一种叫做 position_increment_gap
的简单的解决方案, 它在字段映射中配置 。
DELETE /my_index/groups/PUT /my_index/_mapping/groups
{ "properties": { "names": { "type": "string", "position_increment_gap": 100 } } }
position_increment_gap
设置告诉 Elasticsearch 应该为数组中每个新元素增加当前词条 position
的指定值。 所以现在当我们再索引 names 数组时,会产生如下的结果:
- Position 1:
john
- Position 2:
abraham
- Position 103:
lincoln
- Position 104:
smith
现在我们的短语查询可能无法匹配该文档因为 abraham
和 lincoln
之间的距离为 100 。 为了匹配这个文档你必须添加值为 100 的 slop
。
https://www.elastic.co/guide/cn/elasticsearch/guide/current/phrase-matching.html