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上定义的analyzer和search_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进行分组,不同分组直接的匹配无法跨字段。
参考材料
- ElaticSearch Reference > Multi Match Query
- https://www.elastic.co/guide/cn/elasticsearch/guide/current/most-fields.html
elasticsearch 中的Multi Match Query的更多相关文章
- Elasticsearch Query DSL 整理总结(四)—— Multi Match Query
目录 引言 概要 fields 字段 通配符 提升字段权重 multi_match查询的类型 best_fields 类型 dis_max 分离最大化查询 best_fields 维权使者 tie_b ...
- elasticsearch 嵌套对象使用Multi Match Query、query_string全文检索设置
参考: https://www.elastic.co/guide/en/elasticsearch/reference/1.7/mapping-nested-type.html https://sta ...
- ElasticSearch中term和match探索
一.创建测试数据 1.创建一个index curl -X PUT http://127.0.0.1:9200/student?pretty -H "Content-Type: applica ...
- Elasticsearch Query DSL 整理总结(二)—— 要搞懂 Match Query,看这篇就够了
目录 引言 构建示例 match operator 参数 analyzer lenient 参数 Fuzziness fuzzniess 参数 什么是模糊搜索? Levenshtein Edit Di ...
- Elasticsearch 5.x 关于term query和match query的认识
http://blog.csdn.net/yangwenbo214/article/details/54142786 一.基本情况 前言:term query和match query牵扯的东西比较多, ...
- ES 20 - 查询Elasticsearch中的数据 (基于DSL查询, 包括查询校验match + bool + term)
目录 1 什么是DSL 2 DSL校验 - 定位不合法的查询语句 3 match query的使用 3.1 简单功能示例 3.1.1 查询所有文档 3.1.2 查询满足一定条件的文档 3.1.3 分页 ...
- Elasticsearch.Net 异常:[match] query doesn't support multiple fields, found [field] and [query]
用Elasticsearch.Net检索数据,报异常: )); ElasticLowLevelClient client = new ElasticLowLevelClient(settings); ...
- elasticsearch中常用的API
elasticsearch中常用的API分类如下: 文档API: 提供对文档的增删改查操作 搜索API: 提供对文档进行某个字段的查询 索引API: 提供对索引进行操作,查看索引信息等 查看API: ...
- 如何在Elasticsearch中安装中文分词器(IK+pinyin)
如果直接使用Elasticsearch的朋友在处理中文内容的搜索时,肯定会遇到很尴尬的问题--中文词语被分成了一个一个的汉字,当用Kibana作图的时候,按照term来分组,结果一个汉字被分成了一组. ...
随机推荐
- python浮点数与整数间的转化
舍弃小数部分 >>> math.trunc(12.533222) 12 >>> round(12.2544) 12 按给定小数位数四舍五入 >>> ...
- linux的svn服务器搭建--Subversion Edge
linux下的collabnetsubversionedge的安装: 安装条件(运行环境) jdk + python + httpd 1.root用户下建立svnroot用户,及设定密码 userad ...
- 01.基础架构:一条SQL查询语句是如何执行的?学习记录
01.基础架构:一条SQL查询语句是如何执行的?学习记录http://naotu.baidu.com/file/1c8fb5a0f2497c3a2655fed89099cb96?token=ff25d ...
- SQL执行顺序和coalesce以及case when的用法
1.mysql的执行顺序 from on join where group by having select distinct union //UNION 操作符用于合并两个或多个 SELECT ...
- cornerNet部分学习内容记录
cornerNet来源灵感是基于多人姿态估计的从下往上思想,预测角的热图,根据嵌入式向量对角进行分组,其主干网络也来自于姿态估计的环面网络. cornerNet的总体框架结构图如下: CornerN ...
- firewall&iptables
一.firewall 查看firewall状态 firewall-cmd --state 如果firewall为关闭状态,先启动firewall systemctl start firewalld 添 ...
- boost asio scalability and multithreading
A library such as Boost.Asio is typically used to achieve greater efficiency. With no need to wait f ...
- SQL Server索引管理之六大铁律
索引是以表列为基础的数据库对象.索引中保存着表中排序的索引列,并且纪录了索引列在数据库表中的物理存储位置,实现了表中数据的逻辑排序.通过索引,可以加快数据的查询速度和减少系统的响应时间;可以使表和表之 ...
- CSS中的flex布局
1.flex 布局的概念 Flex是Flexible Box的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性.任何一个容器都可以指定为 Flex 布局,行内元素也可以通过 ...
- 【Linux】【Kibana】解决Kibana启动失败:Data too large问题
今天重启Kibana容器,结果启动不了,一看日志发现是Data数据量太大报错. FATAL [circuit_breaking_exception] [parent] Data too large, ...