用过 Kibana 的同学应该都注意过其顶部的搜索框,像下图这样。
这个输入框接受符合 query string 语法的查询语句。在日常开发中我们常用的都是 elastic query dsl(domain specific language),都是 json 格式的,像下面这个简单的查询语句。
{ "query":{ "match":{ "name":"elastic" } } }
这个查询如果用 query string 来表示的话,如下所示:
name:elastic
看起来是不是简洁了很多呢?对于 kibana 这种 UI 界面,输入越是简单,越是接近自然语言,对于用户也就越是友好,所以 kibana 的搜索框默认选择了 query string 的搜索语法。虽然 query string 展示起来简洁了很多,但是要用好却没有那么简单,如果不好好阅读官方文档并做实验,那你会吃不少苦头。下面我就来帮大家扫清障碍。
注解:
如果非要用 query dsl 的话,kibana 也是支持的,你只需要把 query 中的内容放到搜索框中就可以了,比如上面的查询语句,你只要放下面的内容就可以查询了。
{ "match":{ "name":"elastic" } }
由来
elasticsearch 的 query string 其实就是 lucene 的 query language。elasticsearch 本身就是构建于 lucene 之上的,它支持 lucene 的 query language 也是很简单的事情。那这个 query language 是怎么来的呢?根据 lucene 文档中介绍,虽然 lucene 已经提供了构建查询条件的 API,但对于人类直接使用而言不够友好和自然,所以 lucene 提供了这样一种接近自然语言的查询语言,方便人工输入和记忆查询条件。lucene 不鼓励在代码中直接使用 query language,因为它还要经过一层 query parser 的转换,有性能损耗,另外 query language 也只覆盖了一部分查询 API,并不完备。
基本概念
这里先讲解两个术语: single term 和 phrase。前者是指单个词(分词后的最小单位),后者指短语。举例来说,word、sun 这些都是 single term,而用双引号包裹起来 “word sun“ 就成了 phrase 。所以这两者的区别在于 phrase 是由 term 组成的,包裹在双引号中。而 phrase 中的词在匹配是有顺序要求的,这也就是 elasticsearch 中 match query 和 match phrase query 的区别之一。
接下来再看一个术语:field,也就是字段。我们在使用 query dsl 来写搜索条件时,都会指明是要在哪一个 field 上做匹配。同理,在 query string 中也可以做到只匹配指定的 field,用法是将 字段名称: 放到查询内容前面即可,比如 name:tom
。当不指定 field 时,会使用默认的 field,默认配置为 _all字段,这个是可以自己指定的(index.query.default_field)。关于 _all 字段,可以简单理解为所有文档字段的拼接结果,这里就不展开讲了。比如下面这个查询语句就是查询 name 为 tom 的所有文档。
name:tom
如果不写 name: ,只写 tom
,那么相当于执行 _all:tom
这个查询。
另外* field* 是有作用域的,只对紧跟其后的 term 生效。
name:"Tom Lee"
上面这是一个 phrase 查询,查询匹配 Tom Lee 的所有文档。
name:Tom Lee
上面这个查询实际等效于
name:Tom OR _all:Lee
这也是在实际使用中很多人容易踩坑的地方,使用的过程中要切记,否则查询出的结果肯定不会如你预期的那样。
如果你想对一个 field 指定复杂的查询条件,那么可以使用括号将查询条件包起来用(field grouping)。比如下面的这个语句:
name:(tom lee)
等价于
name:(tom OR lee)
查询 name 为 tom 或者 lee的所有文档。
另外括号不仅可以作用在 field 上,还可以作用在外层的逻辑处理中。比如下面这种查询组合也是支持的(大家先忽略还没有讲到的操作符)。
(name:tom && age:10)||city:(shanghai beijing)
有心的读者可能会发现为什么 && 的语句还要加括号呢,它的优先级不是比 || 高吗?我 也是这么认为的,但动手测试后发现貌似 lucene 没有实现这个优先级的解析,所以大家使用的时候注意括号的使用。
下面再讲一下布尔操作符,大家对这个应该很熟悉了,比如 AND、OR、NOT等。这里要注意的一点是:query string 中的布尔操作符必须大写。如果小写,比如 and、or、not,query解析器会把他们当做普通 term 解析。比如下面这个语句:
name:tom and age:10
上面的查询实际对应下面的语句:
name:tom OR _all:and OR age:10
爱思考的同学看到上面的解释应该就有疑问了。
“这个 OR 是从哪里来的呢?”
这个 OR 是默认的布尔操作符,当多个查询条件之间没有指定布尔关系时,就会使用 OR。
另外可以用 && 和 ||* 分别代替 AND 和 OR,使得查询语句更简洁易懂。
NOT 就是非操作,简写符号为 !。用法如下:
name:(tom NOT lee)
查询 name 为 tom,但不是 lee 的所有文档。
除去 AND、OR、NOT ,query string 还支持 + 和 -,分别对应 must 和 must not 的含义。
name:(tom +lee -alfred)
上面的语句查询 name 中 含有 lee,不含有 alfred,但可能含有 tom 的所有文档。
大家可以想一下如果用 AND、OR、NOT 来重写上面的语句是什么样子呢?
name:((lee && !alfred) || (tom && lee && !alfred))
你是不是发现 + 和 - 的好处了?
查询功能
讲完基础概念,我们再来看看 query string 支持的几个查询功能。
范围查询 range search
数字、日期类型等都是可以指定范围的,对于这类可以使用范围查询,闭区间用[],开区间用{}。举几个例子大家一看就明白了。
age:[1 TO 10] 意为 1<=age<=10 age:[1 TO 10} 意为 1<=age<10 age:[1 TO ] 意为 age>=1 age:[* TO 10] 意为 age<=10
日期也是一样的用法,只要将数字替换为 2017-01-01 即可。
如果你觉得这样写太麻烦了,那可以使用简略写法,如下:
age:(>=1 && <=10) 或者 age:(+>=1 +<=10) age:(>=1 && <10) 或者 age:(+>=1 +<10) age:>=1 age:<=10
通配符查询 wildcard search
大家对于通配符应该都不陌生,有 ? 和 * 两个,前者代表一个字符,后者代表0或多个字符。
name:t?m name:tom* name:t*m
上面的使用方式都可以,但是不能把 ? 和 *** 放在最前面,因为这会导致 elasticsearch 将所有的分词都比对一遍,效率低下。
通配符查询的效率很低,也会占用较多的内存(因为要把所有符合条件的分词进行比对),建议大家谨慎使用。
正则查询 regular expressionsearch
正则查询如同字面意义所讲的,支持正则表达式进行匹配。
name:/[mb]oat/
但这里并不支持所有的正则语法,使用的时候要注意查看官方文档说明。另外正则查询的内存压力也很大,要谨慎使用。
模糊查询 fuzzy search
所谓模糊查询是指允许搜索和匹配的词(term)之间有差异,比如搜索 surprize,可以匹配到surprise。
name:roam~
上面的语句会匹配到 foam 和 roams,波浪号后面可以指定一个 0~2 的浮点值,用以表示模糊度,我在实际使用中用的不多,就不展开来讲了。
近似度查询 proximity search
所谓近似度查询是指在一个短语(phrase)中,词(term)与词之间距离的匹配。
name:"tom lee"~2
匹配时允许 tom 和 lee 之间有 2 个词的距离,笔者用的也不多,所以不班门弄斧了。
提升查询权重 boosting term
查询时如果想改变某个查询条件的权重,可以使用 ^ 来实现。
name:(tom^4 lee)
上面的查询表示,当 name 中包含 tom 时,其权重是 lee 的4倍,这就意味着相应得分也会高,排序也会靠前。
特殊字符过滤
query string 本身已经占据了一些关键字,如下
+ - && || ! ( ) { } [ ] ^ " ~ * ? : \ /
当遇到这些关键词时,需要使用 \ 做转义,比如如果你要搜索 (1+1):2
,那么查询条件需要写成\(1\+1\)\:2
。
总结
至此,query string就讲完了,大家可以去愉快地和 kibana 玩耍了,遇到问题时欢迎和笔者我讨论哦~
参考文档
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#query-string-syntax
https://www.timroes.de/2016/05/29/elasticsearch-kibana-queries-in-depth-tutorial/
http://lucene.apache.org/core/6_4_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package.description
http://www.lucenetutorial.com/lucene-query-syntax.html