文章目录
- [ES高级查询Query DSL](#ES高级查询Query DSL)
-
- match_all
- 术语级别查询
-
- [term query术语查询](#term query术语查询)
- [terms query多术语查询](#terms query多术语查询)
- [range query范围查询](#range query范围查询)
- [exists query](#exists query)
- [ids query](#ids query)
- [prefix query前缀查询](#prefix query前缀查询)
- [wildcard query通配符查询](#wildcard query通配符查询)
- [fuzzy query模糊查询](#fuzzy query模糊查询)
- 全文检索
-
- [match query匹配查询](#match query匹配查询)
- [multi_match query 多字段查询](#multi_match query 多字段查询)
- [match_phrase query短语查询](#match_phrase query短语查询)
- [query_string query](#query_string query)
- simple_query_string
- [bool query布尔查询](#bool query布尔查询)
- highlight高亮
ES高级查询Query DSL
ES中提供了一种强大的检索数据方式,这种检索方式称之为Query DSL(Domain Specified Language 领域专用语言) ,
Query DSL是利用Rest API传递JSON格式的请求体(RequestBody)数据与ES进行交互,这种方式的丰富查询语法让ES检索变得更强大,更简洁。
标准格式如下
bash
GET /索引名/_doc/_search {JOSN请求体}
# 也可以省略_doc 简写成下面这种方式
GET /索引名//_search {JOSN请求体}
案例
bash
# 无条件查询,默认返回10条数据
GET /sys_user/_search
{
"query": {
"match_all": {}
}
}
案例数据生成,接下来的案例很多都是操作改索引
bash
# 删除现有索引
DELETE /sys_user
# 重新创建索引 指定ik分词器
PUT /sys_user
{
"settings" : {
"index" : {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
# 创建文档,指定id
PUT /sys_user/_doc/1
{
"name": "张三",
"sex": 1,
"age": 25,
"address": "广州天河公园",
"remark": "java developer"
}
PUT /sys_user/_doc/2
{
"name": "李四",
"sex": 1,
"age": 28,
"address": "广州荔湾大厦",
"remark": "java assistant"
}
PUT /sys_user/_doc/3
{
"name": "王五",
"sex": 0,
"age": 26,
"address": "广州白云山公园",
"remark": "php developer"
}
PUT /sys_user/_doc/4
{
"name": "赵六",
"sex": 0,
"age": 22,
"address": "长沙橘子洲",
"remark": "python assistant"
}
PUT /sys_user/_doc/5
{
"name": "张龙",
"sex": 0,
"age": 19,
"address": "长沙麓谷企业广场",
"remark": "java architect assistant"
}
PUT /sys_user/_doc/6
{
"name": "赵虎",
"sex": 1,
"age": 32,
"address": "长沙麓谷兴工国际产业园",
"remark": "java architect"
}
PUT /sys_user/_doc/7
{
"name": "李虎",
"sex": 1,
"age": 32,
"address": "广州番禺节能科技园",
"remark": "java architect"
}
PUT /sys_user/_doc/8
{
"name": "张星",
"sex": 1,
"age": 32,
"address": "武汉东湖高新区未来智汇城",
"remark": "golang developer"
}
match_all
使用match_all
,匹配所有文档,默认只会返回10条数据。
原因:_search
查询默认采用的是分页查询,每页记录数size的默认值为10。如果想显示更多数据,指定size
bash
# 无条件查询,_search默认返回10条数据
GET /sys_user/_search
# 等价于
GET /sys_user/_search
{
"query": {
"match_all": {}
}
}
返回源数据_source
_source
关键字,是一个数组,在数组中指定用来指定暂时哪些字段,类似于Mysql的select 字段 from table
或MongoDB中的 $project
映射
bash
# _source关键字的相关操作
# 返回指定字段
GET /sys_user/_search
{
"query": {
"match_all": {}
},
"_source": ["name","age"]
}
#不查看源数据,仅查看元字段
GET /sys_user/_search
{
"query": {
"match_all": {}
},
"_source": false
}
# 只看以a开头的字段
GET /sys_user/_search
{
"query": {
"match_all": {}
},
"_source": "a*"
}
返回指定条数size
size 关键字: 指定查询结果中返回指定条数。 默认返回值10条
bash
# 通过size关键字指定返回结果数
GET /sys_user/_search
{
"query": {
"match_all": {}
},
"size": 20
}
分页查询from&size
size:显示应该返回的结果数量,默认是 10
from:显示应该跳过的初始结果数量,默认是 0
from 关键字用来指定起始返回位置,和size关键字连用可实现分页效果
bash
# 通过from + size关键字实现分页
# 跳过前面5个文档,共显示两个文档
GET /sys_user/_search
{
"query": {
"match_all": {}
},
"from": 5,
"size": 2
}
指定字段排序sort
注意:sort排序会让 _score
相关性得分失效
bash
# 使用sort关键字,按age字段降序 排序
GET /sys_user/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
# sort排序 + from size分页
GET /sys_user/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order": "desc"
}
}
],
"from": 5,
"size": 3
}
术语级别查询
术语级别查询(Term-Level Queries)指的是搜索内容不经过文本分析直接用于文本匹配
搜索的对象大多是索引的非text类型字段。
term query术语查询
Term查询,对输入不做分词。会将输入作为一个整体,在倒排索引中查找准确的词项,
术语查询直接返回包含搜索内容的文档,常用来查询索引中某个类型为keyword的文本字段,类似于SQL的"="查询,使用十分普遍。
注意:最好不要在term查询的字段中使用text字段,因为text字段会被分词,这样做既没有意义,还很有可能什么也查不到。
bash
# 对bool,日期,数字,结构化的文本可以利用term做精确匹配
# term 精确匹配
GET /sys_user/_search
{
"query": {
"term": {
"age": {
"value": "25"
}
}
}
}
# 思考: 查询广州白云是否有数据,为什么?
# 没有数据,因为address是text类型,索引数据时会创建倒排索引,而直接用 term 广州白云这个词 倒排索引中不存在所以查询不到数据
GET /sys_user/_search
{
"query": {
"term": {
"address": {
"value": "广州白云"
}
}
}
}
# 采用term精确查询, 查询字段映射类型为keyword
# 能查询都数据,因为address字段是类型为text 子类型为keyword,会创建一个完整address字段内容在倒排索引中
GET /sys_user/_search
{
"query": {
"term": {
"address.keyword": {
"value": "广州白云山公园"
}
}
}
}
在ES中,Term查询,对输入不做分词。会将输入作为一个整体,在倒排索引中查找准确的词项,并且使用相关度算分公式为每个包含该词项的文档进行相关度算分。
可以通过 constant_score
将查询转换成一个 Filtering
,避免算分,并利用缓存,提高性能。
- 将Query 转成 Filter,忽略TF-IDF计算,避免相关性算分的开销
- Filter可以有效利用缓存
bash
# 使用constant_score + filter 避免相关性算分
GET /sys_user/_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"address.keyword": "广州白云山公园"
}
}
}
}
}
term处理多值字段时,term查询是包含,不是等于。
bash
POST /employee/_bulk
{"index":{"_id":1}}
{"name":"小明","interest":["跑步","篮球"]}
{"index":{"_id":2}}
{"name":"小红","interest":["跳舞","画画"]}
{"index":{"_id":3}}
{"name":"小丽","interest":["跳舞","唱歌","跑步"]}
terms query多术语查询
terms
用于指定字段上匹配多个词项,这里会精确匹配指定字段中包含的任何一个词项
bash
# trems 多术语匹配
GET /sys_user/_search
{
"query": {
"terms": {
"remark.keyword": [
"java developer",
"java assistant"
]
}
}
}
}
range query范围查询
关键字
- range 范围关键字
- gte 大于等于
- lte 小于等于
- gt 大于
- lt 小于
- now 当前时间
bash
# 查询age在[18,25]之间的文档,并对结果做一个降序排序
GET /sys_user/_search
{
"query": {
"range": {
"age": {
"gte": 18,
"lte": 25
}
}
},
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
日期范围的比较 可以使用now
表示当前时间
bash
# 日期范围的比较
PUT /product/_bulk
{"index": {"_id": 1}}
{"productId": "XHDK-1293","price": 100,"date": "2022-01-01"}
{"index": {"_id": 2}}
{"productId": "XHDK-3566","price": 200,"date": "2023-01-01"}
# 当前时间 - 2年 当然你也可以直接写"gte": "2022-08-12"
GET /product/_search
{
"query": {
"range": {
"date": {
"gte": "now-2y"
}
}
}
}
exists query
使用exists
关键字查询,查询文档中所有存在该字段的文档
字段不存在 和 字段值为null的文档都查询不出来
bash
# 查询所有存在name字段的文档数据
GET /sys_user/_search
{
"query": {
"exists": {
"field": "name"
}
}
}
bash
# 索引一个文档,没有name字段
POST /sys_user/_doc
{
"sex": 1,
"age": 25,
"address": "广州天河公园111",
"remark": "java developer111"
}
# 索引一个文档,name字段的值为null
POST /sys_user/_doc
{
"name": null,
"sex": 1,
"age": 25,
"address": "广州天河公园222",
"remark": "java developer222"
}
# 索引一个文档,name字段的值为""
POST /sys_user/_doc
{
"name": "",
"sex": 1,
"age": 25,
"address": "广州天河公园333",
"remark": "java developer333"
}
# 最终只能查询到 name字段的值为"" 的文档, name字段不存在 和 name值为null的文档都查询不出来
GET /sys_user/_search
{
"query": {
"exists": {
"field": "name"
}
}
}
ids query
ids
关键字,值为数组类型,可以根据一组id查询文档数据
bash
# 使用ids关键字,查询一组id对应的文档
GET /sys_user/_search
{
"query": {
"ids": {
"values": [1,2,3]
}
}
}
prefix query前缀查询
直接根据传入的前缀去倒排索引中进行匹配,判断倒排索引中的每个term是否以所指定的前缀开头。它不会分析要搜索字符串,传入的前缀就是想要查找的前缀
默认状态下,前缀查询不做相关性分数计算,它只是将所有匹配的文档返回,然后赋予所有相关分数值为1。它的行为更像是一个过滤器而不是查询。两者实际的区别就是过滤器是可以被缓存的,而前缀查询不行。
bash
# 使用prefilx前缀匹配查询
GET /sys_user/_search
{
"query": {
"prefix": {
"address": {
"value": "广州"
}
}
}
}
wildcard query通配符查询
工作原理和prefix相同,只不过它不是只比较开头,它能支持更为复杂的匹配模式。
bash
# 使用wildcard通配符匹配
GET /sys_user/_search
{
"query": {
"wildcard": {
"address": {
"value": "*白*"
}
}
}
}
fuzzy query模糊查询
使用fuzziness属性来进行模糊查询,从而达到搜索有错别字的情形。
fuzzy 查询会用到两个很重要的参数,fuzziness,prefix_length
-
fuzziness
对输入的关键字进行几次操作可以和倒排索引中的term词匹配。
新增一个字符,删除一个字符,修改一个字符,每次操作可以记做编辑距离为1;
该参数默认值为0,即不开启模糊查询; fuzzy 模糊查询 最大模糊错误必须在0-2之间
-
prefix_length
输入的关键字前面几个字符必须要和倒排索引的term词匹配,关键字前面几个字符不允许写错
默认值为0
bash
# 使用fuzzy关键字进行模糊查询
# 允许一个错别字,但是第一个字不能是错别字
GET /sys_user/_search
{
"query": {
"fuzzy": {
"address": {
"value": "白晕山",
"fuzziness": 1,
"prefix_length": 1
}
}
}
}
全文检索
全文检索查询旨在基于相关性搜索和匹配文本数据。
这些查询会对输入的文本进行分析,将其拆分为词项(单个单词),并执行诸如分词、词干处理和标准化等操作。
以相关性为基础进行搜索和匹配。全文检索查询使用相关性算法来确定文档与查询的匹配程度,并按照相关性进行排序。相关性可以基于词项的频率、权重和其他因素来计算。
match query匹配查询
match在匹配时会对所查找的关键词进行分词,然后按分词匹配查找。
match支持以下参数:
-
query : 指定匹配的值
-
operator : 匹配条件类型
-
- and : 条件分词后都要匹配
- or : 条件分词后有一个匹配即可(默认)
-
minmum_should_match : 最低匹配度,即条件在倒排索引中最低的匹配度
bash
# 下面两种写法一样,两种写法都是operator分词后or的效果
GET /sys_user/_search
{
"query": {
"match": {
"address": "广州白云山"
}
}
}
# 如果只有关键词需要指定,那么match关键词下的query参数就可以省略
GET /sys_user/_search
{
"query": {
"match": {
"address": {
"query": "广州白云山"
}
}
}
}
# 分词后 and的结果
GET /sys_user/_search
{
"query": {
"match": {
"address": {
"query": "广州白云山",
"operator": "and"
}
}
}
}
# 如果分词后是or操作,我们也可以添加minmum_should_match 来指定最低匹配度
# 至少要匹配两个分词
GET /sys_user/_search
{
"query": {
"match": {
"address": {
"query": "广州白云山公园",
"operator": "or",
"minimum_should_match": 2
}
}
}
}
对于match查询,其底层逻辑的概述:
- 分词:首先,输入的查询文本会被分词器进行分词。分词器会将文本拆分成一个个词项(terms),如单词、短语或特定字符。分词器通常根据特定的语言规则和配置进行操作。
- 倒排索引:ES使用倒排索引来加速搜索过程。倒排索引是一种数据结构,它将词项映射到包含这些词项的文档。每个词项都有一个对应的倒排列表,其中包含了包含该词项的所有文档的引用。
- 匹配计算:一旦查询被分词,ES将根据查询的类型和参数计算文档与查询的匹配度。对于match查询,ES将比较查询的词项与倒排索引中的词项,并计算文档的相关性得分。相关性得分衡量了文档与查询的匹配程度。
- 结果返回:根据相关性得分,ES将返回最匹配的文档作为搜索结果。搜索结果通常按照相关性得分进行排序,以便最相关的文档排在前面。
multi_match query 多字段查询
让查询关键词在文档内的多个字段中进行查询
bash
# 使用multi_match,让输入的关键词在多个字段中进行查找匹配
# 这其中也有 operator minimum_should_match 这些参数
GET /sys_user/_search
{
"query": {
"multi_match": {
"query": "长沙赵六",
"fields": ["name", "address"],
"operator": "or",
"minimum_should_match": 1
}
}
}
match_phrase query短语查询
对搜索文本进行分词,再去倒排索引找每个分词,并要求分词相邻 。可以通过slop
参数设置允许分词之间出现的最大间隔距离
我们先来使用分词器来看看一个文本是如何分词的
bash
GET _analyze
{
"analyzer": "ik_max_word",
"text": "广州白云山"
}
那么我们现在输入文本广州白云山
应该是能查询到数据,因为这两个分词是相邻的,如果输入文本广州白云
应该是查询不到数据,因为这两个分词是不相邻的
bash
# 能查询到数据,因为广州 白云山 这两个分词是相邻的,
GET /sys_user/_search
{
"query": {
"match_phrase": {
"address": "广州白云山"
}
}
}
# 查询不到数据,因为 广州 白云 这两个分词是不相邻
GET /sys_user/_search
{
"query": {
"match_phrase": {
"address": "广州白云"
}
}
}
# slop参数设置允许分词之间出现的最大间隔距离
GET /sys_user/_search
{
"query": {
"match_phrase": {
"address": {
"query": "广州白云",
"slop": 2
}
}
}
}
query_string query
对输入的文本在文档中所有字段进行匹配搜索
允许我们在单个查询字符串中指定AND | OR | NOT条件,必须大写
它也支持单字段查询和多字段查询,只不过这些功能我们通过match、multi_match也能实现
bash
# 未指定字段查询,查询文档中的所有字段的值
# AND必须大写 文档中必须包含这两个分词才能查询出来
GET /sys_user/_search
{
"query": {
"query_string": {
"query": "赵六 AND 橘子洲"
}
}
}
# 指定单字段 添加default_field参数
GET /sys_user/_search
{
"query": {
"query_string": {
"default_field": "address",
"query": "赵六 OR 橘子洲"
}
}
}
# 指定多字段 添加
GET /sys_user/_search
{
"query": {
"query_string": {
"fields": ["name", "address"],
"query": "张三 OR (赵六 AND 橘子洲)"
}
}
}
simple_query_string
类似于query_string ,但是会忽略错误的语法,同时只支持部分查询语法,不支持AND OR NOT ,如果存在AND这些会当作普通字符串处理。
支持部分逻辑:
+
代替AND|
代替OR-
代替NOT
bash
# simple_query_string 默认的operator是OR
GET /sys_user/_search
{
"query": {
"simple_query_string": {
"fields": ["name","address"],
"query": "广州公园",
"default_operator": "AND"
}
}
}
GET /sys_user/_search
{
"query": {
"simple_query_string": {
"fields": ["name","address"],
"query": "广州 + 公园"
}
}
}
bool query布尔查询
-
搜索上下文query context
需计算每个文档与搜索条件相关性得分,有一定性能开销,带文本分析的全文检索查询语句适合放在搜索上下文中
-
过滤上下文filter context
只判断搜索条件跟文档是否匹配,不计算相关性得分。可以使用缓存加快响应速度,术语级别查询是否放在过滤上下文中
类型 | 说明 |
---|---|
must | 可包含多个查询条件,每个条件均满足的文档才能被搜索到,每次查询需要计算相关度得分,属于搜索上下文 |
should | 可包含多个查询条件,不存在must和fiter条件时,至少要满足多个查询条件中的一个,文档才能被搜索到,否则需满足的条件数量不受限制,匹配到的查询越多相关度越高,也属于搜索上下文 |
filter | 可包含多个过滤条件,每个条件均满足的文档才能被搜索到,每个过滤条件不计算相关度得分,结果在一定条件下会被缓存, 属于过滤上下文 |
must_not | 可包含多个过滤条件,每个条件均不满足的文档才能被搜索到,每个过滤条件不计算相关度得分,结果在一定条件下会被缓存, 属于过滤上下文 |
案例
生成案例数据
bash
PUT /books
{
"settings": {
"number_of_replicas": 1,
"number_of_shards": 1
},
"mappings": {
"properties": {
"id": {
"type": "long"
},
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"language": {
"type": "keyword"
},
"author": {
"type": "keyword"
},
"price": {
"type": "double"
},
"publish_time": {
"type": "date",
"format": "yyy-MM-dd"
},
"description": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
POST /_bulk
{"index":{"_index":"books","_id":"1"}}
{"id":"1", "title":"Java编程思想", "language":"java", "author":"Bruce Eckel", "price":70.20, "publish_time":"2007-10-01", "description":"Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉。"}
{"index":{"_index":"books","_id":"2"}}
{"id":"2","title":"Java程序性能优化","language":"java","author":"葛一鸣","price":46.5,"publish_time":"2012-08-01","description":"让你的Java程序更快、更稳定。深入剖析软件设计层面、代码层面、JVM虚拟机层面的优化方法"}
{"index":{"_index":"books","_id":"3"}}
{"id":"3","title":"Python科学计算","language":"python","author":"张若愚","price":81.4,"publish_time":"2016-05-01","description":"零基础学python,光盘中作者独家整合开发winPython运行环境,涵盖了Python各个扩展库"}
{"index":{"_index":"books","_id":"4"}}
{"id":"4", "title":"Python基础教程", "language":"python", "author":"Helant", "price":54.50, "publish_time":"2014-03-01", "description":"经典的Python入门教程,层次鲜明,结构严谨,内容翔实"}
{"index":{"_index":"books","_id":"5"}}
{"id":"5","title":"JavaScript高级程序设计","language":"javascript","author":"Nicholas C. Zakas","price":66.4,"publish_time":"2012-10-01","description":"JavaScript技术经典名著"}
bool 查询案例
bash
# 使用must类型,需满足所有查询条件,属于搜索上下文
GET /books/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "Java编程"
}
},
{
"match": {
"description": "性能优化"
}
}
]
}
}
}
# 使用 should 满足任意一个查询条件即可
# 通过 minimum_should_match 参数指定需要满足条件的个数
GET /books/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"title": "java编程"
}
},
{
"match": {
"description": "性能优化"
}
}
],
"minimum_should_match": 1
}
}
}
# 使用 filter 需满足所有查询条件
GET /books/_search
{
"query": {
"bool": {
"filter": [
{
"range": {
"price": {
"gte": 20,
"lte": 100
}
}
},
{
"term": {
"language": "java"
}
}
]
}
}
}
highlight高亮
highlight 关键字: 可以让符合条件的文档中的关键词高亮。
highlight相关属性:
- pre_tags 前缀标签
- post_tags 后缀标签
- tags_schema 设置为styled可以使用内置高亮样式
- require_field_match 多字段高亮需要设置为false
基本使用
示例数据
bash
#指定ik分词器
PUT /products
{
"settings" : {
"index" : {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
PUT /products/_doc/1
{
"proId" : "2",
"name" : "牛仔男外套",
"desc" : "牛仔外套男装春季衣服男春装夹克修身休闲男生潮牌工装潮流头号青年春秋棒球服男 7705浅蓝常规 XL",
"timestamp" : 1576313264451,
"createTime" : "2019-12-13 12:56:56"
}
PUT /products/_doc/2
{
"proId" : "6",
"name" : "HLA海澜之家牛仔裤男",
"desc" : "HLA海澜之家牛仔裤男2019时尚有型舒适HKNAD3E109A 牛仔蓝(A9)175/82A(32)",
"timestamp" : 1576314265571,
"createTime" : "2019-12-18 15:56:56"
}
测试
bash
GET /products/_search
{
"query": {
"term": {
"name": {
"value": "牛仔"
}
}
},
"highlight": {
"fields": {
"*": {}
}
}
}
自定义高亮标签
可以在highlight中使用pre_tags
和post_tags
bash
GET /products/_search
{
"query": {
"multi_match": {
"query": "牛仔",
"fields": ["name","desc"]
}
},
"highlight": {
"pre_tags": ["<span style='color:red'>"],
"post_tags": ["</span>"],
"fields": {
"*": {}
}
}
}
多字段高亮
之前的案例中,都是查询匹配哪些字段,highlight高亮标签中才有这些字段数据。我们也可以指定多字段都可以有高亮数据
使用多字段高亮就需要把require_field_match
参数的值设置为false
bash
# 多字段高亮
# require_field_match属性设置为false 在fields中指定多个字段
GET /products/_search
{
"query": {
"match": {
"name": "牛仔"
}
},
"highlight": {
"pre_tags": ["<span style='color:red'>"],
"post_tags": ["</span>"],
"require_field_match": "false",
"fields": {
"name": {},
"desc": {}
}
}
}