multi_match 查询
multi_match
查询为能在多个字段上反复执行相同查询提供了一种便捷方式。
multi_match
多匹配查询的类型有多种,其中的三种恰巧与 了解我们的数据 中介绍的三个场景对应,即: best_fields
、 most_fields
和 cross_fields
(最佳字段、多数字段、跨字段)。
默认情况下,查询的类型是 best_fields
, 这表示它会为每个字段生成一个 match
查询,然后将它们组合到 dis_max
查询的内部,如下:
{
"dis_max": {
"queries": [
{
"match": {
"title": {
"query": "Quick brown fox",
"minimum_should_match": "30%"
}
}
},
{
"match": {
"body": {
"query": "Quick brown fox",
"minimum_should_match": "30%"
}
}
},
],
"tie_breaker": 0.3
}
}
上面这个查询用 multi_match
重写成更简洁的形式:
{
"multi_match": {
"query": "Quick brown fox",
"type": "best_fields", 1
"fields": [ "title", "body" ],
"tie_breaker": 0.3,
"minimum_should_match": "30%" 2
}
}
1 |
|
2 | 如 |
查询字段名称的模糊匹配
字段名称可以用模糊匹配的方式给出:任何与模糊模式正则匹配的字段都会被包括在搜索条件中, 例如可以使用以下方式同时匹配 book_title
、 chapter_title
和 section_title
(书名、章名、节名)这三个字段:
{
"multi_match": {
"query": "Quick brown fox",
"fields": "*_title"
}
}
提升单个字段的权重
可以使用 ^
字符语法为单个字段提升权重,在字段名称的末尾添加 ^boost
, 其中 boost
是一个浮点数:
{
"multi_match": {
"query": "Quick brown fox",
"fields": [ "*_title", "chapter_title^2" ] 1
}
}
1 |
|
案例:
{
"multi_match": {
"query": "` + keyword + `",
"fields": [
"goodsName.goodsName^3",
"categoryName^2",
"goodsName.goodsNameMax^1",
"jingle^1",
"specString^1"
],
"type": "cross_fields",
"minimum_should_match": "5<90%"
}
}
best_fields、most_fields、cross_fields 3种策略介绍:
1.最佳字段(Best fields):
指的就是搜索结果中应该返回某一个字段匹配到了最多的关键词的文档。
PUT /my_index/my_type/1
{
"title": "Quick brown rabbits",
"body": "Brown rabbits are commonly seen."
}
PUT /my_index/my_type/2
{
"title": "Keeping pets healthy",
"body": "My quick brown fox eats rabbits on a regular basis."
}
# 用户输入了"Brown fox",文档2匹配的更好一些,因为它包含了用户寻找的两个单词。
2.多数字段(Most fields):
一个用来调优相关度的常用技术是将相同的数据索引到多个字段中。它用来尽可能多地匹配文档。
ES会为每个字段生成一个match查询,然后将它们包含在一个bool查询中。
指的就是搜索结果应该返回匹配了更多的字段的document优先返回回来。
most_fields
会告诉 Elasticsearch 合并所有匹配字段的评分:
most_fields 方式的问题
用 most_fields
这种方式搜索也存在某些问题,这些问题并不会马上显现:
- 它是为多数字段匹配 任意 词设计的,而不是在 所有字段 中找到最匹配的。
- 它不能使用
operator
或minimum_should_match
参数来降低次相关结果造成的长尾效应。 - 词频对于每个字段是不一样的,而且它们之间的相互影响会导致不好的排序结果。
# 我们可以将查询传入到validate-query API中进行查看:
GET /_validate/query?explain
{
"query": {
"multi_match": {
"query": "Poland Street W1V",
"type": "most_fields",
"fields": [ "street", "city", "country", "postcode" ]
}
}
}
它会产生下面的解释(explaination):
(street:poland street:street street:w1v)
(city:poland city:street city:w1v)
(country:poland country:street country:w1v)
(postcode:poland postcode:street postcode:w1v)
# 你可以发现能够在两个字段中匹配poland的文档会比在一个字段中匹配了poland和street的文档的分值要高
3.跨字段(Cross fields):
对于一些实体,标识信息会在多个字段中出现,每个字段中只含有一部分信息:
- Person:
first_name
和last_name
- Book:
title
,author
, 和description
- Address:
street
,city
,country
, 和postcode
此时,我们希望在任意字段中找到尽可能多的单词。我们需要在多个字段中进行查询,就好像这些字段是一个字段那样。
指的是一个唯一标识,跨域了多个字段,它将所有的字段视为一个大的字段,然后在任一字段中搜索每个词条
# 通过 minimum_should_match 去长尾,提高搜索精确度
{
"multi_match": {
"query": "` + keyword + `",
"fields": [
"goodsName.goodsName^3",
"categoryName^2",
"goodsName.goodsNameMax^1",
"jingle^1",
"specString^1"
],
"type": "cross_fields",
"minimum_should_match": "5<90%"
}
}
# 通过 operator “and” 连接 让所有词都是必须的。
GET /_validate/query?explain
{
"query": {
"multi_match": {
"query": "peter smith",
"type": "most_fields",
"operator": "and",
"fields": [ "first_name", "last_name" ]
}
}
}