[版权声明]:本文章由danvid发布于http://danvid.cnblogs.com/,如需转载或部分使用请注明出处

  

  在业务中经常会遇到类似数据库的"like"的模糊匹配需求,而es基于分词的全文检索也是有类似的功能,这个就是短语匹配match_phrase,但往往业务需求都不是那么简单,他想要有like的功能,又要允许有一定的容错(就是我搜索"东方宾馆"时,"广州花园宾馆酒店"也要出来,这个就不是单纯的"like"),下面就是我需要解析的问题(在此吐槽一下业务就是这么变态。。)

  描述一个问题时首先需要描述业务场景:假设es中有一索引字段name存储有以下文本信息:

doc[1]:{"name":"广州东方宾馆酒店"}

doc[2]:{"name":"广州花园宾馆酒店"}

doc[3]:{"name":"东方公园宾馆"}

需求要求在输入:"东方宾馆"的时候doc[1]排最前面doc[3]排第二doc[2]排第三,对于这个需求从简单的全文检索match来说,doc[3]:{"name":"东方公园宾馆"}应该是第一位(注意:为了简化原理分析,分词我们使用standard即按单个字分词)

  业务分析:显然对于上面的业务场景如果单独使用match的话,显然是不合适,因为按照standard分词,doc[3]的词条长度要比doc[1]的词条长度短,而词频又是都出现了[东][方][宾][馆]4个词,使用match匹配的话就会吧doc[3]排到最前面,显然业务希望把输入的文字顺序匹配度最高的数据排前面,因为我确实要找的是"广州东方宾馆酒店"而不是"东方公园宾馆"你不能把doc[3]给我排前面,OK业务逻辑好像是对的那么怎么解决问题;

  解决问题前介绍一哈match_phrase原理(match的原理我就不说了自己回去看文档),简单点说match_phrase就是高级"like"。api如下:

GET test_index/_search
{
"query": {
"match_phrase" : {
"message" : {
"query" : "东方宾馆",
"slop" : 2
}
}
}
}

es在给文本分词的时候,除了分词之外还有一个词条标记,就是position,例如我按照standard对以上三个doc的name进行分词会变成这样:

doc[1]:广[0],州[1],东[2],方[3],宾[4],馆[5],酒[6],店[7];

doc[2]:广[0],州[1],花[2],园[3],宾[4],馆[5],酒[6],店[7];

doc[3]:东[0],方[1],公[2],园[3],宾[4],馆[5];

query文本分词:东[0],方[1],宾[2],馆[3];

使用match_phrase时:

1.es会先过滤掉不符合的query条件的doc,即doc[2]中没有"东方"两个词汇,会被过滤掉

2.es会根据分词的position对分词进行过滤和评分,这个是就slop参数,默认是0,意思是查询分词只需要经过距离为0的转换就可以变成跟doc一样的文档数据,例如:对于doc[1]来说slop就是0了,对于doc[3]slop就是2,即"宾"和"馆"最大位移这两个分词只需要最多移动2个位置就可以变成"东方宾馆"(反过来也一样,query的文本中的"宾"和"馆"只需要移动2个位置就可以变成"东方**宾馆"),用数学的理解就是doc[3]的宾[4]-东[0]=4,query文本中的宾[2]-东[0]=2,那么转换距离slop就是4-2=2,同理doc[3]的馆[5]-东[0]=5,query的是3,slop结果也是2,那么"宾"和"馆"最大的slop就是2,则query时设置slop等于2就能把doc[3]匹配出来,当设置为0时就是我们数据库的"like"

  原理解析完了,就知道使用match只能匹配相关度即tf/idf,而分词之间的位置关系却无法保证,而match_phrase能保证分词间的邻近关系,那么就可以利用两者优势,结合搜索进行评分

GET test_index/_search
{
"query": {
"bool": {
"must": {
"match": {
"name": {
"query": "东方宾馆"
}
}
},
"should": {
"match_phrase": {
"name": {
"query": "东方宾馆",
"slop": 0
}
}
}
}
}
}

这样就的结果就是相当于match_phrase帮match进行了相关度分数的加分,当然你也可以通过修改slop的参数来进行步控制分数,这个就根据用户需求来了;

    性能问题:其实使用match_phrase性能是要比单纯的全文检索性能低的,因为他要计算位置嘛,那么想提高性能可以通过先使用match进行过滤数据,然后利用rescore api对已经match的结果进行加分,这样就减少了部分不必要的非match过滤:

GET test_index/_search
{
"query": {
"match": {
"name":"东方宾馆"
}
},
"rescore": {
"window_size": 30,
"query": {
"rescore_query": {
"match_phrase": {
"name": {
"query": "东方宾馆",
"slop": 0
}
}
}
}
}
}
#window_size 是每一分片进行重新评分的顶部文档数量这个只要大于你可能分页的总数*每页的数量即可(pageNumber*pageSize)实际上不需要这么多因为翻页不可能很深,这个根据业务调整即可。

总结及注意点:

1.rescore其实跟bool结合一样是评分的相加,评分不在这里细说了;

2.虽然可以提高相关度评分,但是还是存在可能match很低+一个很低的match_phrase结果没有单独只匹配了一个match的分数高的情况,但是这是很极限了,也是符合相关度评分原理的;

3.由于match_phrase是在搜索阶段进行的计算,会影响搜索效率,据说比term查询慢20倍,所以不要进行大文本量的字段搜索,尽量进行标题,名字这种类型的搜索才使用这个;

4.本文章没有讨论在文本数据重复时的情况,即文本中有多个"[东][方][宾][馆]"和query文本中有多个"[东][方][宾][馆]"分词的情况,但是原理是一样的还是取距离转换的最小值;

5.文中使用了standard分词,实际上可能会用不同的分词器,但是建议使用match_phrase时使用标准的一个个分词,这样是方便进行邻近搜索的控制的,如果使用ik等分词,执行match_phrase时分词是不可控的,所以结果也是不可控。match使用ik,match_phrase用standard结合一起使用也是可以的;

6.邻近搜索效率较低,其实可以通过增加词库的方式进行单纯使用match匹配效率是最高的,前提是你知道客户会搜索什么,这又是另一个研究话题了

更新[2019-05-22]:

补充:短语匹配match_phrase必须要满足下面的要求才能认定和["东方宾馆"]这个词条匹配(以standard分析器为例)

1.搜索的词必须有且仅有["东","方","宾","馆"]这几个词(对于中文是字)的一个或者多个,如果有其他的词(对于中文是字)是不会匹配到的,slop不是完全等同于莱文斯坦距离,可以理解成字符的偏移

2.查询词中偏移量应该跟文档中词的偏移量一样,或者在slop的偏差范围内,就是上文解析的意思。

这里讲解一下fuzzy和match_phrase的区别

1.fuzzy是词/项级别的模糊匹配,match_phrase是基于短语级别的

例如对于英文(standard分析器)来说"dog cat bird"来说"dog"就是一个词/词项,而"dog cat"就是一个短语,因此作用范围不一样

2.fuzzy是基于莱文斯坦距离的,所以fuzzy是可以容错的例如你输入"dcg" 你也可以匹配到"dog cat bird",但是这里注意的是你的查询只能是单词条的查询,不能"dcg cat",如果你需要查询短语里面的拼写错误,可以使用match的fuzziness参数,match_phrase是不允许出现不存在的词条的。

下面是对于fuzzy和match_phrase和match 添加fuzziness参数进行对比

文档内容是{"name":"dog cat bird"} 分析器是standard
--------------------------------------------------------------------------------------------------
1.使用拼写错误的"cot"可以使用fuzzy匹配但是,如果是下面这种,短语是不可以的,输入应当是词条,而不是短语 GET test_save/_search
{
"query": {
"fuzzy": {
"name":{
"value": "bird cot",
"fuzziness":
}
}
}
}
--------------------------------------------------------------------------------------------------
2.这里可以匹配到因为match先分解成bird 和 cot 其中bird可以匹配到,同时cot也是可以匹配到,只不过分数要比输入"bird cat"要低
GET test_save/_search
{
"query": {
"match": {
"name":{
"query": "bird cot",
"fuzziness":
}
}
}
}
-----------------------------------------------------------------------------------------------
3.这里由于cot和文本中的cat不是同一个词,所以是无法匹配到的
GET test_save/_search
{
"query": {
"match_phrase": {
"name": {
"query": "bird cot",
"slop":
}
}
}
}

以上是对于英文的单词的解析,对于中文其实也是一样,只是由于中文如果使用standard一个词项就是一个字,因此使用因此分词后的数据对于fuzzy模糊匹配来说意义不大,但是可以使用keyword进行

GET test_save/_search
{
"query": {
"fuzzy": {
"name.keyword":{
"value": "东日宾馆",
"fuzziness":
}
}
}
}
这样是可以匹配到"东方宾馆"的数据的,但是无法匹配"广州东方宾馆"因为莱文斯坦距离已经不止1了

其实短语匹配应该叫临近查询更适合些

以上就是对模糊查询和短语匹配的解析和补充~

[说明]:elasticsearch版本5.6.4

es 基于match_phrase的模糊匹配原理及使用的更多相关文章

  1. 基于vue实现模糊匹配(这里以邮箱模糊匹配为例,其他的模糊匹配都可以类比)

    html部分(主要部分): js: data: methods: 效果图:

  2. 转:使用Mongo Connector和Elasticsearch实现模糊匹配

    原文来自于:http://www.csdn.net/article/2014-09-01/2821485-how-to-perform-fuzzy-matching-with-mongo-connec ...

  3. 记一个同时支持模糊匹配和静态推导的Atom语法补全插件的开发过程: 序

    简介 过去的一周,都睡的很晚,终于做出了Atom上的APICloud语法提示与补全插件:apicloud_autocomplete.个中滋味,感觉还是有必要记录下来的.代码基于 GPL-3.0 开源, ...

  4. 茗洋Easy UI 1.3.2 部分问题解决系列专题[Combo模糊匹配中文问题 修复]

    本次给大家带来的EasyUI的我研究拓展的新特性 我使用的是  EasyUI 1.3.2版本的,项目是ASP.NET MVC3,但是本篇讲解用不上ASP.NET MVC,仅仅修改官方Demo你就知道怎 ...

  5. Combo模糊匹配中文问题

    茗洋Easy UI 1.3.2 部分问题解决系列专题[Combo模糊匹配中文问题 修复] 本次给大家带来的EasyUI的我研究拓展的新特性 我使用的是  EasyUI 1.3.2版本的,项目是ASP. ...

  6. Android自定义模糊匹配搜索控件(二)

    在项目中遇到一个需要通过某个字的值筛选匹配带出其他信息的需求,在这里将实现思路整理出来. 源码地址:https://github.com/whieenz/SearchSelect 先看效果图 上图中的 ...

  7. 基于PaddlePaddle的语义匹配模型DAM,让聊天机器人实现完美回复 |

    来源商业新知网,原标题:让聊天机器人完美回复 | 基于PaddlePaddle的语义匹配模型DAM 语义匹配 语义匹配是NLP的一项重要应用.无论是问答系统.对话系统还是智能客服,都可以认为是问题和回 ...

  8. mysql学习2:模糊匹配查询like,regexp,in

    mysql模糊匹配查询like,regexp,in   摘要 内容比较简单,无摘要.   关键词 模糊查询  like  regexp  in  contact   正文 下图是示例用到的数据表信息 ...

  9. JAVA基础之sql模糊匹配、外键以及jsp中include的用法

    一.SQL模糊匹配 适用于对字符串进行模糊搜索 格式:   字段名 Like '%关键词%'      %          表示这个位置可有任意个字符(没有也可以) %关键词%  只要包含关键词就算 ...

随机推荐

  1. etcd集群部署与遇到的坑

    在k8s集群中使用了etcd作为数据中心,在实际操作中遇到了一些坑.今天记录一下,为了以后更好操作. ETCD参数说明 —data-dir 指定节点的数据存储目录,这些数据包括节点ID,集群ID,集群 ...

  2. django模板总结

    1. 加载静态文件 html顶部:{% load staticfiles %} 调用: <link rel="stylesheet" type="text/css& ...

  3. ATM取款机数据库设计

    创建文件夹    USE master GO EXEC xp_cmdshell 'mkdir d:\bank', NO_OUTPUT 建库   --检验数据库是否存在,如果为真,删除此数据库--   ...

  4. 【转载】我为什么弃用OpenStack转向VMware vsphere

    我为什么弃用OpenStack转向VMware Vsphere,一切皆为简单.高效.因为我们在工作过程中涉及到大量的测试工作,每天都有成百个虚拟机的创建和销毁工作. 工作任务非常繁重,我们的持续集成平 ...

  5. pythone函数基础(9)操作数据库连接

    #操作数据库连接import pymysqlconn = pymysql.connect(host='118.24.3.40',user='jxz', password='123456',port=3 ...

  6. etcd-v2第一集

    网站:https://github.com/coreos/etcd 一些观点:https://yq.aliyun.com/articles/11035 1.etcd是键值存储仓库,配置共享和服务发现2 ...

  7. MySQL优化(四) 慢查询的定位及优化

    一.SQL语句优化的一般步骤: (1)通过 show status 命令了解各种 SQL 的执行效率: (2)定位执行效率较低的 SQL 语句(重点是 Select): (3)通过 explain 分 ...

  8. 【Mybatis】MyBatis快速入门(一)

    Mybatis简介 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可 ...

  9. 在cmd下可以import cv2,而Pycharm报错:找不到cv2

    平台:win10 x64+Pycharm+Anaconda3+opencv 安装教程:参考博客——http://blog.sina.com.cn/s/blog_cca23c300102xiy4.htm ...

  10. javascript 跨域 的几种方法

      1.jsonp方法 转:https://blog.csdn.net/liusaint1992/article/details/50959571 主要实现功能: 1.参数拼装. 2.给每个回调函数唯 ...