# 多字段查询(Multi Match Query)

### 多字段查询

multi\_match查询基于匹配查询且允许多字段查询构建的：

```
GET /_search
{
  "query": {
    "multi_match" : {
      "query":    "this is a test", # 1
      "fields": [ "subject", "message" ] #2
    }
  }
}
```

| 1 | 查询字符串  |
| - | ------ |
| 2 | 要查询的字段 |

字段盒每个字段的重点都可以用通配符来指定，比如：

```
GET /_search
{
  "query": {
    "multi_match" : {
      "query":    "Will Smith",
      "fields": [ "title", "*_name" ]  # 1
    }
  }
}
```

| 1 | 查询title、first\_name 盒 last\_name字段 |
| - | ---------------------------------- |

可以使用插入符号（^）表示法来增强单个字段

```
GET /_search
{
  "query": {
    "multi_match" : {
      "query" : "this is a test",
      "fields" : [ "subject^3", "message" ] （1）
    }
  }
}
```

| 1 | 主题字段的重要性是消息字段的三倍 |
| - | ---------------- |

### 多字段查询的类型

内部执行multi\_match查询的方式取决于type参数，可以将其设置为：

best\_fields： (默认) 查找与任何字段匹配的文档，使用最佳字段中的权重。 详情参见：[`best_fields`](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html#type-best-fields)

most\_fields： 查找与任何字段匹配的文档，并组合每个字段的权重。详情参见：[`most_fields`](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html#type-most-fields).

cross\_fields： 使用相同的分析仪处理字段，就像它们是一个大字段。 在任何字段中查找每个字词，详情参见：[`cross_fields`](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html#type-cross-fields).

phrase： 对每个字段运行match\_phrase查询，并合并每个字段的权重，详情参见：[`phrase` and `phrase_prefix`](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html#type-phrase).

phrase\_prefix：对每个字段运行match\_phrase\_prefix查询，并合并每个字段的权重，详情参见：[`phrase` and `phrase_prefix`](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html#type-phrase)

### best\_fields

best\_fields类型是非常有用的，当您搜索在同一字段中要找多个字词时。 例如，单个字段中的“棕狐”比一个字段中的“棕色”和另一个字段中的“狐狸”更有意义。

```
GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "brown fox",
      "type":       "best_fields",
      "fields":     [ "subject", "message" ],
      "tie_breaker": 0.3
    }
  }
}
```

等价于执行：

```
GET /_search
{
  "query": {
    "dis_max": {
      "queries": [
        { "match": { "subject": "brown fox" }},
        { "match": { "message": "brown fox" }}
      ],
      "tie_breaker": 0.3
    }
  }
```

通常，best\_fields类型使用单个最佳匹配字段的权重，但如果指定了tie\_breaker，则计算分数如下：

* 从最佳匹配字段得权重
* 用于所有其他匹配字段加上`tie_breaker * _score`

此外，如match查询中所述，接受analyzer，boost，operator，minimum\_should\_match，fuzziness，lenient，prefix\_length，max\_expansions，rewrite，zero\_terms\_query和cutoff\_frequency。

> 运算符和minimum\_should\_match
>
> best\_fields和most\_fields类型是以字段为中心的 - 它们为每个字段生成一个匹配查询。 这意味着运算符和minimum\_should\_match参数分别应用于每个字段，这可能不是您想要的
>
> 以此查询为例：
>
> ```
> GET /_search
> {
>   "query": {
>     "multi_match" : {
>       "query":      "Will Smith",
>       "type":       "best_fields",
>       "fields":     [ "first_name", "last_name" ],
>       "operator":   "and" #1
>     }
>   }
> }
> ```

| 1 | 所有查询条件必须存在 |
| - | ---------- |

> 这个查询可以理解为：
>
> ```
>   (+first_name:will +first_name:smith)
> | (+last_name:will  +last_name:smith)
> ```
>
> 换句话说，所有术语必须存在于单个字段中以供文档匹配。
>
> 有关更好的解决方案，请参阅[cross\_fields](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/query-dsl-multi-match-query.html#type-cross-fields)

### most\_fields

当以不同方式查询包含相同文本的多个字段时，most\_fields类型最有用。 例如，主字段可以包含同义词，词干和没有变音符号的术语。 第二字段可以包含原始术语，并且第三字段可以包含带状疱疹。 通过组合所有三个字段的权重，我们可以将尽可能多的文档与主字段匹配，但使用第二和第三字段将最相似的结果推送到列表的顶部

查询如下：

```
GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "quick brown fox",
      "type":       "most_fields",
      "fields":     [ "title", "title.original", "title.shingles" ]
    }
  }
}
```

等价于执行：

```
GET /_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "title":          "quick brown fox" }},
        { "match": { "title.original": "quick brown fox" }},
        { "match": { "title.shingles": "quick brown fox" }}
      ]
    }
  }
}
```

每个匹配子句的权重分加在一起，然后除以匹配子句的数量。

此外，如match查询中所述，接受analyzer，boost，operator，minimum\_should\_match，fuzziness，lenient，prefix\_length，max\_expansions，rewrite，zero\_terms\_query和cutoff\_frequency，但请参阅operator和minimum\_should\_match。

### phrase和phrase\_prefix

phrase和phrase\_prefix类型的行为与best\_fields类似，但是它们使用match\_phrase或match\_phrase\_prefix查询，而不是匹配查询。

如下查询：

```
GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "quick brown f",
      "type":       "phrase_prefix",
      "fields":     [ "subject", "message" ]
    }
  }
}
```

等价于执行：

```
GET /_search
{
  "query": {
    "dis_max": {
      "queries": [
        { "match_phrase_prefix": { "subject": "quick brown f" }},
        { "match_phrase_prefix": { "message": "quick brown f" }}
      ]
    }
  }
}
```

此外，如match查询中所述，接受analyzer，boost，operator，minimum\_should\_match，fuzziness，lenient，prefix\_length，max\_expansions，rewrite，zero\_terms\_query和cutoff\_frequency，但请参阅operator和minimum\_should\_match。

> 重要：
>
> phrase、phrase\_prefix和fuzziness
>
> fuzziness参数不能与phrase或phrase\_prefix一起使用

### cross\_fields

cross\_fields类型对于多个字段应匹配的结构化文档特别有用。 例如，当查询“Will Smith”的first\_name和last\_name字段时，最佳匹配可能在一个字段中具有“Will”，而在另一个字段中具有“Smith”

> 这听起来像是most\_fields的工作，但这种方法有两个问题。 第一个问题是，对每个字段应用operator和minimum\_should\_match，而不是per-term（参见上面的解释）。
>
> 第二个问题是关于相关性：first\_name和last\_name字段中不同的术语频率可能会产生意外的结果。
>
> 例如，假设我们有两个人：“Will Smith”和“Smith Jones”。 “Smith”作为姓氏是非常普遍的（因此具有低重要性），但是“Smith”作为名字是非常罕见的（因此是非常重要的）。
>
> 如果我们搜索“Will Smith”，“Smith Jones”文档可能会出现在更匹配的“Will Smith”上面，因为first\_name：smith的得分胜过了first\_name：will加上last\_name：smith的组合分数

处理这些类型的查询的一种方法是简单地将first\_name和last\_name字段索引到单个full\_name字段中。 当然，这只能在索引时完成。

cross\_field类型试图通过采用以术语为中心的方法在查询时解决这些问题。 它首先将查询字符串分析为单个术语，然后在任何字段中查找每个术语，就好像它们是一个大字段。

例如如下查询：

```
GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "Will Smith",
      "type":       "cross_fields",
      "fields":     [ "first_name", "last_name" ],
      "operator":   "and"
    }
  }
}
```

执行等价与：

```
+(first_name:will  last_name:will)
+(first_name:smith last_name:smith)
```

换句话说，所有术语必须存在于至少一个字段中以供文档匹配。 （与best\_fields和most\_fields的逻辑进行比较。）

这解决了两个问题之一。 不同项频率的问题通过混合所有字段频率来解决，以便平衡差异。

在实践中，first\_name：smith将被视为具有与last\_name：smith相同的频率，加一。 这将使得first\_name和last\_name上的匹配具有可比的分数，对last\_name具有很小的优势，因为它是包含smith的最可能的字段。

注意，cross\_fields通常只对所有的boost字段都为1的短字符串字段有用。否则boosts，term freqs和length标准化以这样一种方式促成分数，使得术语统计的混合不再有意义了。

如果您通过[Validate API](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/search-validate.html)运行上述查询，则返回以下解释：

```
+blended("will",  fields: [first_name, last_name])
+blended("smith", fields: [first_name, last_name])
```

此外，如match查询中所述，接受analyzer，boost，operator，minimum\_should\_match，fuzziness，lenient，prefix\_length，max\_expansions，rewrite，zero\_terms\_query和cutoff\_frequency，但请参阅operator和minimum\_should\_match。

### cross\_field 和 analysis

cross\_field类型只能在具有相同分析器的字段上以term-centric模式工作。 具有相同分析器的字段在上面的示例中被分组在一起。 如果有多个组，它们将与bool查询结合使用。

例如，如果我们有具有相同分析器的第一和最后一个字段，加上first.edge和last.edge，它们都使用edge\_ngram分析器，

查询如下：

```
GET /_search
{
 "query": {
    "multi_match" : {
      "query":      "Jon",
      "type":       "cross_fields",
      "fields":     [
        "first", "first.edge",
        "last",  "last.edge"
      ]
    }
  }
}
```

等价与执行：

```
    blended("jon", fields: [first, last])
| (
    blended("j",   fields: [first.edge, last.edge])
    blended("jo",  fields: [first.edge, last.edge])
    blended("jon", fields: [first.edge, last.edge])
)
```

换句话说，第一个和最后一个将被分组在一起并被视为单个字段，first.edge和last.edge将被分组在一起并被视为单个字段。

拥有多个组是很好的，但是当与operator或minimum\_should\_match相结合时，它可能会遇到与most\_fields或best\_fields相同的问题。

您可以轻松地将此查询重新编写为两个单独的cross\_fields查询以及bool查询，并将minimum\_should\_match参数应用于其中一个：

```
GET /_search
{
  "query": {
    "bool": {
      "should": [
        {
          "multi_match" : {
            "query":      "Will Smith",
            "type":       "cross_fields",
            "fields":     [ "first", "last" ],
            "minimum_should_match": "50%"  # 1
          }
        },
        {
          "multi_match" : {
            "query":      "Will Smith",
            "type":       "cross_fields",
            "fields":     [ "*.edge" ]
          }
        }
      ]
    }
  }
}
```

| 1 | 在第一个或最后一个字段中必须存在一个will或smith |
| - | ---------------------------- |

您可以通过在查询中指定分析器参数将所有字段强制设置到同一组中：

```
GET /_search
{
  "query": {
   "multi_match" : {
      "query":      "Jon",
      "type":       "cross_fields",
      "analyzer":   "standard",  # 1
      "fields":     [ "first", "last", "*.edge" ]
    }
  }
}
```

| 1 | 对所有字段使用标准分析仪 |
| - | ------------ |

等价与执行：

```
blended("will",  fields: [first, first.edge, last.edge, last])
blended("smith", fields: [first, first.edge, last.edge, last])
```

## tie\_breaker <a href="#id-duo-zi-duan-cha-xun-tiebreaker" id="id-duo-zi-duan-cha-xun-tiebreaker"></a>

默认情况下，每个词汇混合查询将使用组中任何字段返回的最佳分数，然后将这些分数加在一起以给出最终分数。 tie\_breaker参数可以更改每个期间混合查询的默认行为。 它接受：

0.0 取出单个最佳分数（例如）first\_name：will和last\_name：will（default）

1.0 将（例如）first\_name：will和last\_name：will的分数加在一起

0.0 \<n <1.0 取单个最佳分数加上tie\_breaker乘以来自其他匹配字段的每个分数。

> ## cross\_field 和 fuzziness <a href="#id-duo-zi-duan-cha-xun-crossfield-he-fuzziness" id="id-duo-zi-duan-cha-xun-crossfield-he-fuzziness"></a>
>
> fuzziness字段不能和cross\_fields类型一起使用
