Elasticsearch系列---实战搜索语法
概要
本篇介绍Query DSL的语法案例,查询语句的调试,以及排序的相关内容。
基本语法
空查询
最简单的搜索命令,不指定索引和类型的空搜索,它将返回集群下所有索引的所有文档(默认显示10条):
GET /_search
{}
搜索多个索引
GET /index1,index2/_doc/_search
{}
指定分页搜索
GET /_search
{
"from": 0,
"size": 10
}
get带request body
HTTP协议,GET请求带body是不规范的做法,但由于ES搜索的复杂性,加上HTTP协议GET/POST方法表述的语义,GET更适合用来表述查询的动作,虽然不规范,但还是这么用了。现在大多数浏览器也支持GET+request body,如果遇到不支持的,换成POST即可。了解一下就行,不用太慌张。
查询表达式Query DSL
Query DSL是一种非常灵活、可读性高的查询语言,body为JSON格式,绝大部分功能都可以用它来展现,并且这种查询语句更纯粹,让学习者更专注于本身的功能,避免Client API的干扰。
上一节的空查询,等价于这个:
GET /_search
{
"query": {
"match_all": {}
}
}
基本语法
# 查询语句结构
{
QUERY_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
# 针对某个字段的查询
{
QUERY_NAME: {
FIELD_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
}
合并查询语句
再复杂的查询语句,也是由一个一个的查询条件叠加而成的,查询语句有两种形式:
- 叶子语句:单个条件组成的语句,如match语句,类似mysql的"id = 1"这种。
- 复合语句:有多个条件,需要合并在一起才能组成一个完整的语句,需要使用bool进行组合,里面的条件可以用must必须匹配、must not必须不匹配、should可以匹配修饰,也可以包含过滤器filter。类似mysql的"(status = 1 && language != 'french' && (author = 'John' || author = 'Tom'))"这种。
举个例子:
{
"bool": {
"must": { "match": { "status": 1 }},
"must_not": { "match": { "language": "french" }},
"should": { "match": { "author": "John Tom" }},
"filter": { "range": { "length" : { "gt" : 30 }} }
}
}
复合语句可以嵌套,来实现更复杂的查询需求,在上面的例子上简单延伸一下:
"bool": {
"must": { "match": { "status": 1 }},
"must_not": { "match": { "language": "french" }},
"should": [
{"match": { "author": "John Tom" }},
{"bool": {
"must": { "match": { "name": "friend" }},
"must_not": { "match": { "content": "star" }}
}}
],
"filter": { "range": { "length" : { "gt" : 30 }} }
}
复合语句相关性分数计算
每一个子查询都独自地计算文档的相关性得分。一旦他们的得分被计算出来,bool 查询就将这些得分进行合并并且返回一个代表整个布尔操作的得分,得分高的显示在前面,filter内的条件不参与分数计算。
过滤器filter
我们还是以英文儿歌的索引为案例,看一个搜索需求:歌词内容包含friend,同时歌长大于30秒的记录
GET /music/children/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"content": "friend"
}
}
],
"filter": {
"range": {
"length": {
"gte": 30
}
}
}
}
}
}
filter与query
- 过滤情况filtering context
仅按照搜索条件把需要的数据筛选出来,不计算相关度分数。
- 查询情况query context
匹配条件的数据,会根据搜索条件的相关度,计算每个document的分数,然后按照分数进行排序,这个才是全文搜索的情况。
性能差异
filter只做过滤,不作排序,并且会缓存结果到内存中,性能非常高。
query匹配条件,要做评分,没有缓存,性能要低一些。
应用场景
filter一个非常重要的作用就是减少不相关数据对query的影响,提升query的性能,二者常常搭配在一起使用。
组合使用的时候,把期望符合条件的document的搜索条件放在query里,把要滤掉的条件放在filter里。
constant_score查询
如果一个查询只有filter过滤条件,可以用constant_score来替代bool查询,这样的查询语句更简洁、更清晰,只是没有评分,示例如下:
GET /music/children/_search
{
"query": {
"constant_score": {
"filter": {
"term": { "content": "gymbo"}
}
}
}
}
filter内不支持terms语法,注意一下。
最常用的查询
再复杂的查询语句,也是由最基础的查询变化而来的,而最常用的查询其实也就那么几个。
- match_all查询
查询简单的匹配所有文档
GET /_search
{
"query": {
"match_all": {}
}
}
- match查询
无论是全文搜索还是精确查询,match查询是最基本的标准
# 全文搜索例子
{ "match": { "content": "loves smile" }}
# 精确搜索
{ "match": { "likes": 15 }}
{ "match": { "date": "2019-12-05" }}
{ "match": { "isOwner": true }}
{ "match": { "keyword": "love you" }}
对于精确值的查询,我们可以使用filter来替代,filter有缓存的效果。
- multi_match查询
可以在多个字段上执行相同的match查询
{
"multi_match": {
"query": "my sunshine",
"fields": [ "name", "content" ]
}
}
- range查询
查询指定区间内的数字或时间,query和filter都支持,一般是filter用得多,允许的操作符如下:
- gt 大于
- gte 大于或等于
- lt 小于
- lte 小于或等于
{
"range": {
"length": {
"gte": 45,
"lt": 60
}
}
}
- term查询
用于精确值匹配,精确值可以是数字,日期,boolean或keyword类型的字符串
{ "term": { "likes": 15 }}
{ "term": { "date": "2019-12-05" }}
{ "term": { "isOwner": true }}
{ "term": { "keyword": "love you" }}
建立索引时mapping设置为not_analyzed时,match等同于term,用得多的是match和range。
- terms查询
跟term类似,只是允许一次指定多个值进行匹配,只要有任何一个匹配上,都满足条件
{ "terms": { "content": [ "love", "gymbo", "sunshine" ] }}
查询语句调试
复杂的查询语句,可能会有几百行,可以先使用调试工具检测一下查询语句,定位不合法的搜索及原因,完整语法如下:
GET /index/type/_validate/query?explain
{
"query": {
...
}
}
explain参数可以提供更详细的查询不合法的信息,便于问题定位。写一个错误的例子,比如使用中文标点符号:
GET /music/children/_validate/query?explain
{
"query": {
"terms": { "content“: [ "love", "gymbo", "sunshine" ] }
}
}
错误提示如下:
{
"valid": false,
"error": """
ParsingException[Failed to parse]; nested: JsonParseException[Unexpected character ('l' (code 108)): was expecting a colon to separate field name and value
at [Source: org.elasticsearch.transport.netty4.ByteBufStreamInput@5e57280e; line: 3, column: 33]];; com.fasterxml.jackson.core.JsonParseException: Unexpected character ('l' (code 108)): was expecting a colon to separate field name and value
at [Source: org.elasticsearch.transport.netty4.ByteBufStreamInput@5e57280e; line: 3, column: 33]
"""
}
valid关键字,true为验证通过,false为不通过,如上提示信息,会指明3行33列错误,原因是使用了中文的引号。将语法修正后,得到的正确响应如下:
{
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"valid": true,
"explanations": [
{
"index": "music",
"valid": true,
"explanation": "+content:(gymbo love sunshine) #*:*"
}
]
}
排序
查询请求得到的结果,默认排序是相关性得分降序。如果我们只使用filter过滤,符合filter条件的文档,评分都是一样的(bool的filter得分是null,constant_score得分是1),结果文档还是随机返回,显然这样的排序不符合我们的预期。
sort排序规则
为此,我们可以使用sort属性,对文档进行排序,sort的用法与mysql如出一辙,示例如下:
GET /music/children/_search
{
"query": {
"bool": {
"filter": { "range": { "length" : { "gt" : 30 }} }
}
},
"sort": [
{
"length": {
"order": "desc"
}
}
]
}
sort内可以同时指定多个排序字段,一旦使用sort排序后,_score得分将变成null,因为我们指定了排序规则,_score没有实际意义了,就不用耗费精力再去计算它。
字符串排序问题
我们知道text类型的字段,会有关键词分词处理,对这样的字段进行排序,结果往往都不准确,6.x版本以后的text类型,会再自动建立一个keyword类型的字段,这个字段是不分词的,所以这样就有了分工,text类型的负责搜索,keyword类型则负责排序。
我们回顾一下music索引的mapping信息(节选):
{
"music": {
"mappings": {
"children": {
"properties": {
"content": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
例如name字段,有一个text类型的,里面fields还有一个类型为keyword,名称也为keyword的字段,所以在排序的场景中,我们应该使用name.keyword,示例如下:
GET /music/children/_search
{
"sort": [
{
"name.keyword": {
"order": "asc"
}
}
]
}
小结
本篇介绍Query DSL的语法及基础实战内容,顺带点了一下filter与query的区别,面对复杂查询语句时,建议先用验证工具进行排查,最后介绍了一下排序方面的知识,基础语法、上机案例多实践即可。
专注Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区

Elasticsearch系列---实战搜索语法的更多相关文章
- Elasticsearch系列---常见搜索方式与聚合分析
概要 本篇主要介绍常见的6种搜索方式.聚合分析语法,基本是上机实战,可以和关系型数据库作对比,如果之前了解关系型数据库,那本篇只需要了解搜索和聚合的语法规则就可以了. 搜索响应报文 以上篇建立的mus ...
- Elasticsearch系列---初识搜索
概要 本篇主要介绍搜索的报文结构含义.搜索超时时间的处理过程,提及了一下多索引搜索和轻量搜索,最后将精确搜索与全文搜索做了简单的对比. 空搜索 搜索API最简单的形式是不指定索引和类型的空搜索,它将返 ...
- Elasticsearch系列---实战零停机重建索引
前言 我们使用Elasticsearch索引文档时,最理想的情况是文档JSON结构是确定的,数据源源不断地灌进来即可,但实际情况中,没人能够阻拦需求的变更,在项目的某个版本,可能会对原有的文档结构造成 ...
- Elasticsearch URI search 查询语法整理
Elasticsearch URI search 一.请求体查询与空查询 1. 请求体查询(request body search) 简单查询语句(lite)是一种有效的命令行adhoc查询.但是,如 ...
- 干货 |《从Lucene到Elasticsearch全文检索实战》拆解实践
1.题记 2018年3月初,萌生了一个想法:对Elasticsearch相关的技术书籍做拆解阅读,该想法源自非计算机领域红火已久的[樊登读书会].得到的每天听本书.XX拆书帮等. 目前市面上Elast ...
- Google高级搜索语法
Google高级搜索语法 Google搜索果真是一个强悍的不得了的搜索引擎,今天转了一些 google的高级搜索语法 希望能帮助到大家. 一.allinanchor: anchor是一处说明性的文 ...
- elasticsearch的rest搜索--- 查询
目录: 一.针对这次装B 的解释 二.下载,安装插件elasticsearch-1.7.0 三.索引的mapping 四. 查询 五.对于相关度的大牛的文档 四. 查询 1. 查询的官网的文档 ...
- webpack 多页应用架构系列实战
阅读目录 1.webpack配置了解 2.webpack CommonsChunkPlugin公共代码剥离 3.了解ProvidePlugin的用途 回到顶部 1.webpack配置了解 webpac ...
- Kibana 搜索语法
Kibana 搜索语法 Kibana 支持三种搜索语法, 分别是 Lucene query 语法, 基于 json 的 ES query语法, 以及 Kuery 语法. 前两种语法可以直接使用, Ku ...
随机推荐
- MySQL统计同比环比SQL
大体思路: MySQL没有类似oracle方便的统计函数,只能靠自己去硬计算:通过时间字段直接增加年份.月份,然后通过left join关联时间字段去计算环比.同比公式即可 原始表结构: 求同比SQL ...
- @loj - 2250@ 「ZJOI2017」仙人掌
目录 @题目描述@ @solution@ @accepted code@ @details@ @题目描述@ 如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌.所谓简单环 ...
- LeetCode74 Search a 2D Matrix
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the follo ...
- 创建我的flask第一个应用(一)
本地python版本 (venv) dongge@dongge-PC:/data/webroot$ python --version Python 3.6.5 创建flask命令 $mkdir myp ...
- DECLARE
-- 修正用プログラム DECLARE CURSOR c_adv_fee_detail IS SELECT adv_fee.fee_mgmt_num, ...
- 怎样打开.jar格式文件,怎样运行.jar格式文件
当时第一次看到.jar文件不知道是什么鬼,以为是压缩包,直接就解压了,但是并没有什么用.所以在下为大家详细介绍如何打开.jar文件以及如何运行.jar文件.什么是.jar文件,简单的说就是java压缩 ...
- Python--day30--tcp协议(建立链接三次握手,断掉链接四次挥手)和UDP协议
TCP协议: tcp是可靠的,面向连接的.建立全双工通信. 建立链接的三次握手 链接一旦建立一定是全双工工通信,必然是双方通信. UDP协议: TCP协议和UDP协议的对比: QQ使用的是UDP,因为 ...
- HDU 5971"Wrestling Match"(二分图染色)
传送门 •题意 给出 n 个人,m 场比赛: 这 m 场比赛,每一场比赛中的对决的两人,一个属于 "good player" 另一个属于 "bad player" ...
- sdk uncaught third Error Cannot assign to read only property 'constructor' of object '#<V>' (小程序)
sdk uncaught third Error Cannot assign to read only property 'constructor' of object '#<V>' 在a ...
- Vue 动画的钩子函数
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...