Elasticsearch Query DSL 整理总结(二)—— 要搞懂 Match Query,看这篇就够了
引言
昨天是感恩节,上幼儿园的女儿在老师的叮嘱下,晚上为我和老婆洗了脚(形式上的_),还给我们每人端了一杯水。看着孩子一天天的长大,懂事,感觉很开心,话说咱们程序员这么辛苦是为了什么?不就是为了老婆,孩子,热炕头,有一个温暖幸福的家庭,再捎带着用代码改变一下世界吗?想到这里,顿时觉得学习,创作博客的劲头也的更足了。哈哈,扯远了,书归正传,今天我们来聊聊 Match Query。
Match Query 是最常用的 Full Text Query 。无论需要查询什么字段, match
查询都应该会是首选的查询方式。它既能处理全文字段,又能处理精确字段。
构建示例
为了能够在后面能深入理解 Match Query 中的各个属性的意义,我们先构建一个 index 示例(有兴趣的同学只要将下面字段粘贴到 sense 中就可以创建)。
PUT matchtest
{
}
PUT matchtest/_mapping/people
{
"properties": {
"age": {
"type": "integer"
},
"hobbies": {
"type": "text"
},
"name": {
"type": "keyword"
}
}
}
PUT matchtest/people/1
{
"name" : "Jim",
"age": 10,
"hobbies": "football, basketball, pingpang"
}
PUT matchtest/people/2
{
"name" : "Tom",
"age": 12,
"hobbies": "swimming, football"
}
match
operator 参数
match
查询是一种 bool
类型的查询。什么意思呢?举个例子,查询 people type 的 hobbies 为 football basketball
GET matchtest/people/_search
{
"query": {
"match": {
"hobbies": "football basketball"
}
}
}
会将上面的两个文档都搜索出来。为什么?上面的查询其实隐藏了一个默认参数operator
, 它的默认值是 or
,也就是说上面的查询也可以写成这种形式
GET matchtest/people/_search
{
"query": {
"match": {
"hobbies": {
"query": "football basketball",
"operator": "or"
}
}
}
}
这样就比较容易理解了,既然是 or
操作符,就表示只要查询的文档的 hobbies
字段中含有 football
和 basketball
任意一个,就可以被匹配到。
如果将 operator
操作符的值改为 and
,则表示需要同时包含 football
和 basketball
, 得到的结果就只能是 文档 1 Jim 小朋友了。
analyzer
analyzer
属性是指在对查询文本分析时的分析器
- 如果没有指定则会使用字段mapping 时指定的分析器
- 如果字段在 mapping 时也没有明显指定,则会使用默认的 search analyzer。
这里我们也没有指定,就会使用默认的,就不举例了,在后面文章讲解 analyzer 时再拓展。
lenient 参数
默认值是 false
, 表示用来在查询时如果数据类型不匹配且无法转换时会报错。如果设置成 true
会忽略错误。
例如, 例子中的 age
是 integer
类型的,如果查询 age=xxy
,就会导致无法转换而报错。
GET matchtest/_search
{
"query": {
"match": {
"age" : {
"query": "xxx"
}
}
}
}
而如果将 lenient
参数设置为 true
,就会忽略这个错误
GET matchtest/_search
{
"query": {
"match": {
"age" : {
"query": "xxx",
"lenient": true
}
}
}
}
注意,如果将 age
字段的值设置为字符串 "10", 来查询,由于能够转换成整数,这时 elastic 内部会将 字符串先转换成整数再做查询,不会报错。
Fuzziness
fuzzniess 参数
fuzziness
参数可以使查询的字段具有模糊搜索的特性。来先了解下什么是模糊搜索。
什么是模糊搜索?
模糊搜索是指系统允许被搜索信息和搜索提问之间存在一定的差异,这种差异就是“模糊”在搜索中的含义。例如,查找名字Smith时,就会找出与之相似的Smithe, Smythe, Smyth, Smitt等。
——百度百科
通过模糊搜索可以查询出存在一定相似度的单词,那么怎么计算两个单词是否有相似度以及相似度的大小呢?这就要了解下另外一个概念:Levenshtein Edit Distance
Levenshtein Edit Distance
Levenshtein Edit Distance 叫做莱文斯坦距离**,是编辑距离的一种。指两个字串之间,由一个转成另一个所需的最少编辑操作次数。允许的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
例如,单词 "god" 只需要插入一个 'o' 字符就可以变为 "good",因此它们之间的编辑距离为 1。
fuzziness 参数取值规则
了解了上面两个概念,回过头再来看下 fuzziness
参数。
在查询 text
或者 keyword
类型的字段时, fuzziness
可以看做是莱文斯坦距离。
fuzziness
参数的取值如下
0,1,2
表示最大可允许的莱文斯坦距离AUTO
会根据词项的长度来产生可编辑距离,它还有两个可选参数,形式为
AUTO:[low],[high]
, 分别表示短距离参数和长距离参数;如果没有指定,默认值是AUTO:3,6
表示的意义如下0..2
单词长度为 0 到 2 之间时必须要精确匹配,这其实很好理解,单词长度太短是没有相似度可言的,例如 'a' 和 'b'。
3..5
单词长度 3 到 5 个字母时,最大编辑距离为 1
>5
单词长度大于 5 个字母时,最大编辑距离为 2
最佳实践:
fuzziness
在绝大多数场合都应该设置成AUTO
如果不设置 fuziness
参数,查询是精确匹配的。
来看例子,上面创建了一个 doc
PUT matchtest/people/1
{
"name" : "Jim",
"age": 10,
"hobbies": "football, basketball, pingpang"
}
设置 fuzziness
为 AUTO
,
- 其中
hobbies
字段的值football
长度 > 5, 此时我们找一个编辑距离为 2 的单词footba22
来查询,应该匹配到 - 其中
name
字段的值jim
长度在 3 和 5 之间,此时找一个编辑距离为 1 的单词jiO
应匹配到,而编辑距离为 2 的jOO
就不应匹配到。
来,验证下
GET matchtest/_search
{
"query": {
"match": {
"hobbies": {
"query": "footba22",
"fuzziness": "AUTO"
}
}
}
}
GET matchtest/_search
{
"query": {
"match": {
"name": {
"query": "jiO",
"fuzziness": "AUTO"
}
}
}
}
GET matchtest/_search
{
"query": {
"match": {
"name": {
"query": "jOO",
"fuzziness": "AUTO"
}
}
}
}
prefix_length
prefix_length
表示不能没模糊化的初始字符数。由于大部分的拼写错误发生在词的结尾,而不是词的开始,使用 prefix_length
就可以完成优化。注意 prefix_length
必须结合 fuzziness
参数使用。
例如,在查询 hobbies
中的 football
时,将 prefix_length
参数设置为 3,这时 foatball
将不能被匹配。
GET matchtest/_search
{
"query": {
"match": {
"hobbies": {
"query": "foatball",
"fuzziness": "AUTO",
"prefix_length": 3
}
}
}
}
TODO(max_expansions 参数对于 match 查询而言,没理解表示的意义,如果您知道这个参数的用法,请给我留言告知,不胜感谢! )
Zero terms Query
先看例子, 先创建一个文档 zero_terms_query_test
其中 message
字段使用 stop
分析器,这个分析器会将 stop words 停用词在索引时全都去掉。
PUT matchtest1
PUT matchtest1/_mapping/zero_terms_query_test
{
"properties": {
"message": {
"type": "text",
"analyzer": "stop"
}
}
}
PUT matchtest1/zero_terms_query_test/1
{
"message": "to be or not to be"
}
GET matchtest1/_search
{
"query": {
"match": {
"message": {
"query": "to be or not to be",
"operator": "and",
"zero_terms_query": "none"
}
}
}
}
那么就像 message 字段中的 to be or not to be
这个短语中全部都是停止词,一过滤,就什么也没有了,得不到任何 tokens, 那搜索时岂不什么都搜不到。
POST _analyze
{
"analyzer": "stop",
"text": "to be or not to be"
}
zero_terms_query
就是为了解决这个问题而生的。它的默认值是 none
,就是搜不到停止词(对 stop 分析器字段而言),如果设置成 all
,它的效果就和 match_all
类似,就可以搜到了。
GET matchtest1/_search
{
"query": {
"match": {
"message": {
"query": "to be or not to be",
"operator": "and",
"zero_terms_query": "all"
}
}
}
}
Cutoff frequency
查询字符串时的词项会分成低频词(更重要)和高频词(次重要)两类,像前面所说的停用词 (stop word)就属于高频词,它虽然出现频率较高,但在匹配时可能并不太相关。实际上,我们往往是想要文档能尽可能的匹配那些低频词,也就是更重要的词项。
要实现这个需求,只要在查询时配置 cutoff_frequency
参数就可以了。假设我们将 cutoff_frequency
设置成 0.01
就表示
- 任何词项在文档中超过 1%, 被认为是高频词
- 其他的词项会被认为低频词
从而将高频词(次重要的词)挪到可选子查询中,让它们只参与评分,而不参与匹配;高频词(更重要的词)参与匹配和评分。
这样一来,就不再需要 stopwords 停用词文件了,从而变成了动态生成停用词: 高频词就会被看做是停用词。这种配置只是对于词项比较多的场合如 email body,文章等适用,文字太少, cutoff_frequency
选项设置的意义就不大了。
cutoff_frequency
配置有两种形式
- 指定为一个分数(
0.01
)表示出现频率 - 指定为一个正整数(
5
)则表示出现次数
下面给个例子, 在创建的 3 个文档中都包含 "be " 的单词,在查询时将 cutoff_frequency
参数设置为 2, 表示 "be" 就是高频词,只会参与评分,但在匹配时不做考虑。
此时查询的内容为 "to be key" ,由于 "be" 词项是高频词,因为在文档中必须要存在 "to" 或者 "key" 才能匹配,因此文档 3 不能匹配。
PUT /matchtest2
PUT matchtest2/_mapping/cutoff_frequency_test
{
"properties": {
"message": {
"type": "text"
}
}
}
PUT matchtest2/cutoff_frequency_test/1
{
"message": "to be or not to be to be or"
}
PUT matchtest2/cutoff_frequency_test/2
{
"message": "be key or abc"
}
PUT matchtest2/cutoff_frequency_test/3
{
"message": "or to be or to to be or"
}
GET matchtest2/_search
{
"query": {
"match": {
"message": {
"query": "to be key",
"cutoff_frequency": 2
}
}
}
}
synonyms
synonyms 是指同义词,只要索引和字段中配置了同义词过滤器,match 查询是支持多词条的同义词扩展的。在应用过滤器后,解析器会对每个多次条同义词创建一个语句查询。
例如,同义词 USA, united states of America
就会构建出 (USA OR ("united states of America"))
。看下面例子:
PUT /matchtest4
{
"settings": {
"index" : {
"analysis" : {
"analyzer" : {
"synonym" : {
"tokenizer" : "whitespace",
"filter" : ["synonym"]
}
},
"filter" : {
"synonym" : {
"type" : "synonym",
"synonyms" : [
"USA, united states of America"
]
}
}
}
}
}
}
PUT /matchtest4/_mapping/synonyms_test
{
"properties": {
"message": {
"type": "text",
"analyzer": "synonym"
}
}
}
PUT /matchtest4/synonyms_test/1
{
"message": "united states of America people"
}
GET /matchtest4/_search
{
"query": {
"match": {
"message": {
"query": "USA"
}
}
}
}
小结
本文以代码实例的方式完整的讲解了 Match Query 的各种使用场景和参数意义。下篇会讲解 Match Phrase Query 敬请期待。
参考文档
系列文章列表
Query DSL
Java Rest Client API
- Elasticsearch Java Rest Client API 整理总结 (一)——Document API
- Elasticsearch Java Rest Client API 整理总结 (二) —— SearchAPI
- Elasticsearch Java Rest Client API 整理总结 (三)——Building Queries
Elasticsearch Query DSL 整理总结(二)—— 要搞懂 Match Query,看这篇就够了的更多相关文章
- Elasticsearch Query DSL 整理总结(四)—— Multi Match Query
目录 引言 概要 fields 字段 通配符 提升字段权重 multi_match查询的类型 best_fields 类型 dis_max 分离最大化查询 best_fields 维权使者 tie_b ...
- ExpandoObject与DynamicObject的使用 RabbitMQ与.net core(一)安装 RabbitMQ与.net core(二)Producer与Exchange ASP.NET Core 2.1 : 十五.图解路由(2.1 or earler) .NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了
ExpandoObject与DynamicObject的使用 using ImpromptuInterface; using System; using System.Dynamic; names ...
- Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) JAVA日志的前世今生 .NET MVC采用SignalR更新在线用户数 C#多线程编程系列(五)- 使用任务并行库 C#多线程编程系列(三)- 线程同步 C#多线程编程系列(二)- 线程基础 C#多线程编程系列(一)- 简介
Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) 一.前言 由于本篇文章较长,所以下面给出内容目录方便跳转阅读,当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅. 一.前言 ...
- Elasticsearch Query DSL 整理总结(三)—— Match Phrase Query 和 Match Phrase Prefix Query
目录 引言 Match Phase Query slop 参数 analyzer 参数 zero terms query Match Phrase 前缀查询 max_expansions 小结 参考文 ...
- Elasticsearch Query DSL 整理总结(一)—— Query DSL 概要,MatchAllQuery,全文查询简述
目录 引言 概要 Query and filter context Match All Query 全文查询 Full text queries 小结 参考文档 引言 虽然之前做过 elasticse ...
- Python 3 入门,看这篇就够了(超全整理)
史上最全Python资料汇总(长期更新).隔壁小孩都馋哭了 --- 点击领取 今天和大家分享的内容是Python入门干货,文章很长. 简介 Python 是一种高层次的结合了解释性.编译性.互动性和面 ...
- 【原创】阿里三面:搞透Kafka的存储架构,看这篇就够了
阅读本文大约需要30分钟.这篇文章干货很多,希望你可以耐心读完. 你好, 我是华仔,在这个 1024 程序员特殊的节日里,又和大家见面了. 从这篇文章开始,我将对 Kafka 专项知识进行深度剖析, ...
- 搞透 IOC,Spring IOC 看这篇就够了!
IOC与AOP属于Spring的核心内容,如果想掌握好Spring你肯定需要对IOC有足够的了解 @mikechen IOC的定义 IOC是Inversion of Control的缩写,多数书籍翻译 ...
- elasticsearch系列四:搜索详解(搜索API、Query DSL)
一.搜索API 1. 搜索API 端点地址 从索引tweet里面搜索字段user为kimchy的记录 GET /twitter/_search?q=user:kimchy 从索引tweet,user里 ...
随机推荐
- .net图表之ECharts随笔06-这才是最简单的
今天搞柱形图的时候,发现了一个更简单的用法.那就是直接使用带all的那个js文件 基本步骤: 1.为ECharts准备一个具备大小(宽高)的Dom 2.ECharts的js文件引入(echarts-a ...
- ASP.NET MVC 使用 Log4net 记录日志
Log4net 介绍 Log4net 是 Apache 下一个开放源码的项目,它是Log4j 的一个克隆版.我们可以控制日志信息的输出目的地.Log4net中定义了多种日志信息输出模式.它可以根据需要 ...
- #loj3090 [BJOI2019] 勘破神机
简单线性代数练习题 首先翻开具体数学生成函数一章,可以发现\(F(n),G(n)\)满足以下递推式 \[F(n)=F(n-1)+F(n-2),F(0)=1,F(1)=1\] \[G(n)=4G(n-2 ...
- OSX10.12搭建IPv6本地环境测试APP
前记 最近刚换了工作,生活终于又安定下来了,又可以更博了 正文 最近公司在上线APP(整体全是用JS去写的,就用了我原生的一个控制器),然后APP就去上线,就被苹果巴巴给拒了.通过阅读苹果回复的邮件, ...
- 7. Bagging & Random Forest
通过前面集成学习的介绍我们知道,欲得到泛化性能强的集成学习器,集成中个体学习器应尽量相互独立:虽然“独立”在现实任务中无法做到,但可以设法使基学习器尽可能具有较大差异. 1. Bagging 自助采样 ...
- javascript数据结构与算法---检索算法(二分查找法、计算重复次数)
javascript数据结构与算法---检索算法(二分查找法.计算重复次数) /*只需要查找元素是否存在数组,可以先将数组排序,再使用二分查找法*/ function qSort(arr){ if ( ...
- 安装ORACLE时 各Linux版本下载地址
oracle linux :https://edelivery.oracle.com/osdc/faces/SearchSoftware 需要注册oracle账号 redhat官方下载 https:/ ...
- js 开发过程中经验及总结记录
一 let 和 var 作用域 1 普通用法 for (var i = 0; i < 5; i++) { console.log(i); } console.log(i); //-- ...
- 用AOP思想改造一个服务器的数据存储
背景是有一个游戏服务器一直以来都是写SQL的, 后来改过一段时间的redis, 用的是别的员工写的类orm方式将实体类型映射成各种key-value对进行写入, 但是仍有一个缺点就是需要在增\删\改的 ...
- Centos 7 快速搭建IOS可用IPsec
安装 strongswan yum install -y http://ftp.nluug.nl/pub/os/Linux/distr/fedora-epel/7/x86_64/Packages/e/ ...