elasticsearch 基础 —— Multi Match Query

在Elasticsearch全文检索中,我们用的比较多的就是Multi Match Query,其支持对多个字段进行匹配。Elasticsearch支持5种类型的Multi Match,我们一起来深入学习下它们的区别。

5种类型的Multi Match Query

直接从官网的文档上摘抄一段来:

  • best_fields: (default) Finds documents which match any field, but uses the _score from the best field.
  • most_fields: Finds documents which match any field and combines the _score from each field.
  • cross_fields: Treats fields with the same analyzer as though they were one big field. Looks for each word in any field.
  • phrase: Runs a match_phrase query on each field and combines the _score from each field.
  • phrase_prefix: Runs a match_phrase_prefix query on each field and combines the _score from each field.

这里我们只考虑前面三种,后两种可以另外单独研究,就先忽略了。

创建测试索引,预置测试数据

创建gino_product索引

PUT /gino_product
{
  "mappings": {
    "product": {
      "properties": {
        "productName": {
          "type": "string",
          "analyzer": "fulltext_analyzer",
          "copy_to": [
            "bigSearchField"
          ]
        },
        "brandName": {
          "type": "string",
          "analyzer": "fulltext_analyzer",
          "copy_to": [
            "bigSearchField"
          ],
          "fields": {
            "brandName_pinyin": {
              "type": "string",
              "analyzer": "pinyin_analyzer",
              "search_analyzer": "standard"
            },
            "brandName_keyword": {
              "type": "string",
              "analyzer": "keyword",
              "search_analyzer": "standard"
            }
          }
        },
        "sortName": {
          "type": "string",
          "analyzer": "fulltext_analyzer",
          "copy_to": [
            "bigSearchField"
          ],
          "fields": {
            "sortName_pinyin": {
              "type": "string",
              "analyzer": "pinyin_analyzer",
              "search_analyzer": "standard"
            }
          }
        },
        "productKeyword": {
          "type": "string",
          "analyzer": "fulltext_analyzer",
          "copy_to": [
            "bigSearchField"
          ]
        },
        "bigSearchField": {
          "type": "string",
          "analyzer": "fulltext_analyzer"
        }
      }
    }
  },
  "settings": {
    "index": {
      "number_of_shards": 1,
      "number_of_replicas": 0
    },
    "analysis": {
      "tokenizer": {
        "simple_pinyin": {
          "type": "pinyin",
          "first_letter": "none"
        }
      },
      "analyzer": {
        "fulltext_analyzer": {
          "type": "ik",
          "use_smart": true
        },
        "pinyin_analyzer": {
          "type": "custom",
          "tokenizer": "simple_pinyin",
          "filter": [
            "word_delimiter",
            "lowercase"
          ]
        }
      }
    }
  }
}

插入一些测试数据

POST /gino_product/product/1
{
  "productName": "耐克女生运动轻跑鞋",
  "brandName": "耐克",
  "sortName": "鞋子",
  "productKeyword": "耐克,潮流,运动,轻跑鞋"
}

POST /gino_product/product/2
{
  "productName": "耐克女生休闲运动服",
  "brandName": "耐克",
  "sortName": "上衣",
  "productKeyword": "耐克,休闲,运动"
}

POST /gino_product/product/3
{
  "productName": "阿迪达斯女生冬季运动板鞋",
  "brandName": "阿迪达斯",
  "sortName": "鞋子",
  "productKeyword": "阿迪达斯,冬季,运动,板鞋"
}

POST /gino_product/product/4
{
  "productName": "阿迪达斯女生冬季运动夹克外套",
  "brandName": "阿迪达斯",
  "sortName": "上衣",
  "productKeyword": "阿迪达斯,冬季,运动,夹克,外套"
}

测试数据总览

测试数据

分别搜索【运动】

POST /gino_product/_search
{
  "query": {
    "multi_match": {
      "query": "运动",
      "fields": [
        "brandName^100",
        "brandName.brandName_pinyin^100",
        "brandName.brandName_keyword^100",
        "sortName^80",
        "sortName.sortName_pinyin^80",
        "productName^60",
        "productKeyword^20"
      ],
      "type": <multi-match-type>,
      "operator": "AND"
    }
  }
}

发现使用3种type都可以搜索出4条商品数据,而且排序也是一致的。

分别搜索【运动 上衣】

POST /gino_product/_search
{
  "query": {
    "multi_match": {
      "query": "运动 上衣",
      "fields": [
        "brandName^100",
        "brandName.brandName_pinyin^100",
        "brandName.brandName_keyword^100",
        "sortName^80",
        "sortName.sortName_pinyin^80",
        "productName^60",
        "productKeyword^20"
      ],
      "type": <multi-match-type>,
      "operator": "AND"
    }
  }
}

这次搜索只有cross_field才能搜索出数据,而使用best_fields和most_fields不行,为什么?

使用validate API来比较区别

POST /gino_product/_validate/query?rewrite=true
{
  "query": {
    "multi_match": {
      "query": "运动 上衣",
      "fields": [
        "brandName^100",
        "brandName.brandName_pinyin^100",
        "brandName.brandName_keyword^100",
        "sortName^80",
        "sortName.sortName_pinyin^80",
        "productName^60",
        "productKeyword^20"
      ],
      "type": <multi-match-type>,
      "operator": "AND"
    }
  }
}

best_fields:所有输入的Token必须在一个字段上全部匹配。

每个字段匹配时分别使用mapping上定义的analyzersearch_analyzer

(+brandName:运动 +brandName:上衣)^100.0 
| (+brandName.brandName_pinyin:运 +brandName.brandName_pinyin:动 +brandName.brandName_pinyin:上 +brandName.brandName_pinyin:衣)^100.0 
| (+brandName.brandName_keyword:运 +brandName.brandName_keyword:动 +brandName.brandName_keyword:上 +brandName.brandName_keyword:衣)^100.0
| (+sortName:运动 +sortName:上衣)^80.0 
| (+sortName.sortName_pinyin:运 +sortName.sortName_pinyin:动 +sortName.sortName_pinyin:上 +sortName.sortName_pinyin:衣)^80.0 
| (+productName:运动 +productName:上衣)^60.0 
| (+productKeyword:运动 +productKeyword:上衣)^20.0 

most_fields:所有输入的Token必须在一个字段上全部匹配。

best_fields不同之处在于相关性评分,best_fields取最大匹配得分(max计算),而most_fields取所有匹配之和(sum计算)。

(
    (+brandName:运动 +brandName:上衣)^100.0 
    (+brandName.brandName_pinyin:运 +brandName.brandName_pinyin:动 +brandName.brandName_pinyin:上 +brandName.brandName_pinyin:衣)^100.0 
    (+brandName.brandName_keyword:运 +brandName.brandName_keyword:动 +brandName.brandName_keyword:上 +brandName.brandName_keyword:衣)^100.0
    (+sortName:运动 +sortName:上衣)^80.0 
    (+sortName.sortName_pinyin:运 +sortName.sortName_pinyin:动 +sortName.sortName_pinyin:上 +sortName.sortName_pinyin:衣)^80.0 
    (+productName:运动 +productName:上衣)^60.0 
    (+productKeyword:运动 +productKeyword:上衣)^20.0
)

cross_fields:所有输入的Token必须在同一组的字段上全部匹配。

首先ES会对cross_fields进行查询重写分组,分组的依据是search_analyzer。具体到我们的例子中【brandName.brandName_pinyin、brandName.brandName_keyword、sortName.sortName_pinyin】这三个字段的search_analyzer是standard,而其余的字段是fulltext_analyzer,因此最终被分为了两组。

(
  (
    +(brandName.brandName_pinyin:运^100.0 | sortName.sortName_pinyin:运^80.0 | brandName.brandName_keyword:运^100.0) 
    +(brandName.brandName_pinyin:动^100.0 | sortName.sortName_pinyin:动^80.0 | brandName.brandName_keyword:动^100.0) 
    +(brandName.brandName_pinyin:上^100.0 | sortName.sortName_pinyin:上^80.0 | brandName.brandName_keyword:上^100.0) 
    +(brandName.brandName_pinyin:衣^100.0 | sortName.sortName_pinyin:衣^80.0 | brandName.brandName_keyword:衣^100.0)
  ) 
  (
    +(productKeyword:运动^20.0 | brandName:运动^100.0 | sortName:运动^80.0 | productName:运动^60.0) 
    +(productKeyword:上衣^20.0 | brandName:上衣^100.0 | sortName:上衣^80.0 | productName:上衣^60.0)
  )
)

继续探索和思考

如何让best_fields和most_fields也可以匹配出商品?

最常见的做法就是使用_all字段或者copyTo字段来实现,比如我们mapping里面的bigSearchField字段。

如何改进cross_fields的搜索结果?

由于cross_fields需要根据search_analyzer进行分组,因此像搜索【运动 shangyi】这样的输入时是无法匹配到商品的,因此应该尽可能地减少分组既尽量使用统一的search_analyzer,或者在search时强制指定search_analyzer覆盖mapping里定义的search_analyzer。

把operator改成OR会如何?

在上面的例子中,我们设置的operator均为AND,意味着所有搜索的Token都必须被匹配。那设置成OR会怎么样以及什么场景下该使用OR呢?

在使用OR的时候要特别注意,因为只要有一个Token匹配就会把商品搜索出来,比如上面的搜索【运动 上衣】的时候,会把鞋子的商品也匹配出来,这样搜索的准确度会远远降低。

在一些特殊的搜索中,比如我们搜索【耐克 阿迪达斯 上衣】,如果使用operator为AND,则无论使用哪种multi-search-type都无法匹配出商品(想想为什么?),此时我们可以设置operator为OR并且设置minimum_should_match为60%,这样就可以搜索出属于耐克和阿迪达斯的上衣了,这种情况相当于一种智能的搜索降级了。

/gino_product/_search
{
  "query": {
    "multi_match": {
      "query": "耐克 阿迪达斯 上衣",
      "fields": [
        "brandName^100",
        "brandName.brandName_pinyin^100",
        "brandName.brandName_keyword^100",
        "sortName^80",
        "sortName.sortName_pinyin^80",
        "productName^60",
        "productKeyword^20"
      ],
      "type": "cross_fields",
      "operator": "OR",
      "minimum_should_match": "60%"
    }
  }
}

再谈相关性评分

Elasticsearch相关性打分机制学习一文中我们曾经探讨过best_fields和cross_fields相关性评分的机制,其中的例子使用的相同的search_analyzer。那对于分组情况下,cross_fields评分又是如何计算的呢?

我们还是用上面的例子,增加explain参数来看一下。

POST /gino_product/_search
{
  "explain": true,
  "query": {
    "multi_match": {
      "query": "运动 上衣",
      "fields": [
        "brandName^100",
        "brandName.brandName_pinyin^100",
        "brandName.brandName_keyword^100",
        "sortName^80",
        "sortName.sortName_pinyin^80",
        "productName^60",
        "productKeyword^20"
      ],
      "type": "cross_fields",
      "operator": "AND"
    }
  }
}

详细ES响应报文:cross_fields_scoring.json

通过上述validate API得到的分组信息和explain得到的评分详情信息,可以总结出一个cross_fields评分公式:

score(q, d) = coord(q, d) * ∑(∑(max(score(t, f))))
  • coord(q, d): 分组匹配因子,比如上面我们只有一个分组匹配,coord就是0.5(两个分组中匹配了一个分组);
  • score(t, f): 搜索的一个Token和一个特定的字段的相关性评分(使用TFIDF)计算;
  • max:搜索的一个Token在所有字段评分中取最大值;
  • 分组内求和:一个分组内搜索的所有Token的最大值进行求和;
  • 分组间求和:所有分组的得分最终进行求和计算;

小结

  • best_fields对搜索为单个Token的情况下效果更好,比如搜索【耐克】的时候品牌为耐克和商品关键字包含耐克的时候前者相关性得分更高;但是对于都是为多个Token需要跨字段匹配时,只能引进大字段来匹配,这样权重的设置就失去意义了;
  • most_fields和best_fields类似,其优点在于能够尽可能多地匹配,相关性评分机制更合理;
  • cross_fields最大的优点在于能够跨字段匹配,而且充分利用到了各个字段的权重设置。但是需要注意的是匹配时是根据search_analyzer进行分组,不同分组直接的匹配无法跨字段。

参考材料

<think>嗯,用户问的是Elasticsearch中的multi_match查询,需要详细解释。首先,我得回顾一下multi_match的基本概念和用途。multi_match是用于在多个字段中执行相同的查询,可以替代多个match查询的组合,简化查询语句。这点我记得没错。 接下来,需要介绍它的基本语法结构。用户可能想知道怎么写一个multi_match查询,包括查询字符串、字段列表以及可选的类型参数。例如,用"query"指定搜索内容,"fields"列出字段,可能还要提到通配符的使用,比如title和content的加权,比如title^3。 然后,要讲解不同的类型(type参数)。这里可能需要详细说明每种类型的适用场景,比如best_fields、most_fields、cross_fields、phrase、phrase_prefix以及bool_prefix。比如best_fields是默认的,找任一字段最佳匹配,使用dis_max;most_fields则是合并多个字段的评分;cross_fields处理跨字段的情况,比如姓名分开存储时的查询。 还要提到每种类型的内部实现机制,比如best_fields对应dis_max查询,most_fields使用bool查询的should子句组合。cross_fields如何将查询条件拆分到不同字段,以及它们的评分方式差异。 然后,可能需要举例说明不同场景下的应用。比如,在电商搜索中,商品名称和描述字段使用best_fields,而跨字段如姓名分开存储时用cross_fields。还要说明不同查询类型对评分的影响,以及如何影响搜索结果排序。 用户可能还想知道如何选择正确的类型,这部分需要给出一些指导原则,比如字段内容相似用best_fields,不同用most_fields,需要跨字段匹配用cross_fields等。同时,注意事项比如通配符字段的性能问题,避免过多字段,以及不同类型对评分的影响。 需要检查是否有遗漏的重要点,比如模糊匹配、运算符设置(and/or),或者与multi_match相关的其他参数,比如analyzer、boost等。可能还要提到版本差异,确保用户使用的Elasticsearch版本支持提到的类型。 最后,总结一下multi_match的优势,比如简化多字段查询,灵活性高,支持不同策略。同时提醒用户根据实际需求调整类型和参数,结合测试优化查询效果。确保语言通俗易懂,结构清晰,分点说明,让用户能够一步步理解multi_match的用法和应用场景。</think>Elasticsearch 的 `multi_match` 查询是用于在多个字段中执行相同搜索条件的查询,是 `match` 查询的多字段扩展版本。以下是详细说明: --- ### **一、基础语法** ```json { "query": { "multi_match": { "query": "搜索关键词", "fields": ["字段1", "字段2^3", "wildcard字段*"], "type": "best_fields" } } } ``` - **query**: 要搜索的文本内容。 - **fields**: 指定搜索的字段列表,支持通配符(如 `title.*`)。 - 字段加权:通过 `^` 符号提升字段权重(例如 `title^3` 表示 `title` 字段权重是默认的 3 倍)。 - **type**: 指定匹配策略(核心参数,下文详解)。 --- ### **二、type 类型详解** #### 1. **best_fields(默认值)** - **用途**:在任一字段中找到最佳匹配的文档(类似 `OR` 逻辑)。 - **原理**:内部使用 `dis_max` 查询,取所有字段中的最高评分。 - **适用场景**:字段内容高度相似(例如 `title` 和 `description`)。 - **可选参数**: - `tie_breaker`:为其他字段的匹配评分添加权重(0~1),默认 0。 #### 2. **most_fields** - **用途**:合并多个字段的匹配评分(类似 `OR` 叠加评分)。 - **原理**:内部使用 `bool` 查询的 `should` 子句组合多个 `match` 查询。 - **适用场景**:字段内容不同且需综合评分(例如不同语言的翻译字段)。 #### 3. **cross_fields** - **用途**:将搜索条件拆分成多个词项,跨字段匹配(类似“组合查询”)。 - **原理**:将查询词项分配到不同字段,要求所有词项都匹配(类似 `AND`)。 - **适用场景**:字段内容互补(例如 `first_name` 和 `last_name` 分开存储时搜索全名)。 #### 4. **phrase** - **用途**:精确匹配短语(类似 `match_phrase` 的多字段版本)。 - **原理**:在所有字段中搜索完整短语。 - **可选参数**: - `slop`:允许短语中词项的最大间隔(默认 0)。 #### 5. **phrase_prefix** - **用途**:支持最后一个词项的模糊匹配(类似 `match_phrase_prefix`)。 - **原理**:在所有字段中搜索短语前缀。 - **可选参数**: - `max_expansions`:限制最后一个词项的扩展数量(默认 50)。 #### 6. **bool_prefix** - **用途**:将查询拆分为词项,最后一个词项作为前缀匹配。 - **原理**:类似 `match_bool_prefix` 的多字段版本。 --- ### **三、典型场景示例** #### 场景 1:电商商品搜索 ```json { "query": { "multi_match": { "query": "无线蓝牙耳机", "fields": ["title^3", "description", "tags"], "type": "best_fields", "tie_breaker": 0.3 } } } ``` - **说明**:优先匹配 `title` 字段,同时兼顾 `description` 和 `tags` 字段。 #### 场景 2:跨字段姓名搜索 ```json { "query": { "multi_match": { "query": "张三 北京", "fields": ["first_name", "last_name", "address"], "type": "cross_fields", "operator": "and" } } } ``` - **说明**:要求“张三”和“北京”必须同时在任意字段组合中出现。 --- ### **四、注意事项** 1. **性能影响**: - 避免使用过多字段(尤其是通配符 `*`),可能降低查询速度。 - `phrase` 和 `phrase_prefix` 类型消耗资源较高。 2. **评分差异**: - `best_fields` 可能忽略其他字段的匹配信息,`most_fields` 会累加评分。 3. **字段分析器**: - 确保所有字段使用相同的分词器(否则 `cross_fields` 可能失效)。 --- ### **五、如何选择 type 类型?** | 场景特点 | 推荐类型 | |------------------------------|----------------| | 字段内容高度相似 | `best_fields` | | 字段内容差异大且需综合评分 | `most_fields` | | 需要跨字段组合匹配(如姓名) | `cross_fields` | | 精确短语匹配 | `phrase` | | 自动补全或前缀搜索 | `phrase_prefix` | --- 通过合理使用 `multi_match` 查询,可以灵活应对多字段搜索需求,但需根据数据特点选择匹配策略,并结合实际测试优化参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gmHappy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值