概要

本篇介绍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语法,注意一下。

最常用的查询

再复杂的查询语句,也是由最基础的查询变化而来的,而最常用的查询其实也就那么几个。

  1. match_all查询

查询简单的匹配所有文档

GET /_search
{
"query": {
"match_all": {}
}
}
  1. match查询

无论是全文搜索还是精确查询,match查询是最基本的标准

# 全文搜索例子
{ "match": { "content": "loves smile" }} # 精确搜索
{ "match": { "likes": 15 }}
{ "match": { "date": "2019-12-05" }}
{ "match": { "isOwner": true }}
{ "match": { "keyword": "love you" }}

对于精确值的查询,我们可以使用filter来替代,filter有缓存的效果。

  1. multi_match查询

可以在多个字段上执行相同的match查询

{
"multi_match": {
"query": "my sunshine",
"fields": [ "name", "content" ]
}
}
  1. range查询

查询指定区间内的数字或时间,query和filter都支持,一般是filter用得多,允许的操作符如下:

  • gt 大于
  • gte 大于或等于
  • lt 小于
  • lte 小于或等于
{
"range": {
"length": {
"gte": 45,
"lt": 60
}
}
}
  1. 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。

  1. 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系列---实战搜索语法的更多相关文章

  1. Elasticsearch系列---常见搜索方式与聚合分析

    概要 本篇主要介绍常见的6种搜索方式.聚合分析语法,基本是上机实战,可以和关系型数据库作对比,如果之前了解关系型数据库,那本篇只需要了解搜索和聚合的语法规则就可以了. 搜索响应报文 以上篇建立的mus ...

  2. Elasticsearch系列---初识搜索

    概要 本篇主要介绍搜索的报文结构含义.搜索超时时间的处理过程,提及了一下多索引搜索和轻量搜索,最后将精确搜索与全文搜索做了简单的对比. 空搜索 搜索API最简单的形式是不指定索引和类型的空搜索,它将返 ...

  3. Elasticsearch系列---实战零停机重建索引

    前言 我们使用Elasticsearch索引文档时,最理想的情况是文档JSON结构是确定的,数据源源不断地灌进来即可,但实际情况中,没人能够阻拦需求的变更,在项目的某个版本,可能会对原有的文档结构造成 ...

  4. Elasticsearch URI search 查询语法整理

    Elasticsearch URI search 一.请求体查询与空查询 1. 请求体查询(request body search) 简单查询语句(lite)是一种有效的命令行adhoc查询.但是,如 ...

  5. 干货 |《从Lucene到Elasticsearch全文检索实战》拆解实践

    1.题记 2018年3月初,萌生了一个想法:对Elasticsearch相关的技术书籍做拆解阅读,该想法源自非计算机领域红火已久的[樊登读书会].得到的每天听本书.XX拆书帮等. 目前市面上Elast ...

  6. Google高级搜索语法

    Google高级搜索语法   Google搜索果真是一个强悍的不得了的搜索引擎,今天转了一些 google的高级搜索语法 希望能帮助到大家. 一.allinanchor: anchor是一处说明性的文 ...

  7. elasticsearch的rest搜索--- 查询

    目录: 一.针对这次装B 的解释 二.下载,安装插件elasticsearch-1.7.0   三.索引的mapping 四. 查询 五.对于相关度的大牛的文档 四. 查询 1. 查询的官网的文档   ...

  8. webpack 多页应用架构系列实战

    阅读目录 1.webpack配置了解 2.webpack CommonsChunkPlugin公共代码剥离 3.了解ProvidePlugin的用途 回到顶部 1.webpack配置了解 webpac ...

  9. Kibana 搜索语法

    Kibana 搜索语法 Kibana 支持三种搜索语法, 分别是 Lucene query 语法, 基于 json 的 ES query语法, 以及 Kuery 语法. 前两种语法可以直接使用, Ku ...

随机推荐

  1. spingboot项目在windows环境中运行时接收参数及日志中文乱码

    1.logback.xml配置 appender中添加 <param name="Encoding" value="UTF-8" /> <co ...

  2. Python中的简单实现UDP协议没有粘包问题

    服务端: import socket server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #数据报协议->udp server.bind ...

  3. Uva 568 【大整数】

    UVa568 题意:求N!(N<=10000)的最后一位非0数. 10000以内5^5 = 3125最多可以影响后5位.所以直接保存后五位就行. #include<iostream> ...

  4. Flask学习之二 模板

    继续学习flask 本部分Miguel Grinberg教程的翻译地址:http://www.pythondoc.com/flask-mega-tutorial/templates.html 英文原文 ...

  5. HZOJ Tree

    看到换根果断lct啊,然而其实我板子还没有打熟,还不会维护子树信息,于是就挂掉了…… 然而正解并不是lct. 其实好像很久很久以前将lca的时候好像讲到过一道换根的题,当时没有听懂. 直接说正解吧: ...

  6. input标签和fmt:formatDate 在jsp中同时使用引号解决办法

    input标签和fmt:formatDate 在jsp中同时使用引号解决办法 使用input标签设置默认值value并格式化fmt时间格式处理 格式化前: <input type="d ...

  7. es6 中let与const的简析

    1.let 它的作用类似于var,用来声明变量,但是所声明的变量,只在let命令所在的代码块内有效. if(true){ ; let b = ; } document.write(a); docume ...

  8. 学linux内核与学linux操作系统有什么区别!?

    linux内核包括:进程管理,存储管理,IO管理,文件系统等功能.linux操作系统则是linux内核再加上像shell或图形界面和其他的实用软件,比内核庞大的多.建议先学shell命令和linux下 ...

  9. oracle函数 ln(y)

    [功能]返回以e为底的y的对数(e为数学常量) [参数]y,数字型表达式 (条件y>0) [返回]数字 [示例] select exp(3),exp(-3),ln(20.0855369),ln( ...

  10. 函数的渐近的界&阶的比较

    一.函数的渐近的界   我们在研究算法性能的时候,往往会在意算法的运行时间,而运行时间又与算法输入的规模相关,对于一个算法,我们可以求出运行时间和输入规模的函数,当输入规模足够大时,站在极限的角度看, ...