Elasticsearch中URI Search和RequestBody Search分析
前言
Elasticsearch
作为一款分布式搜索工具,其搜索功能非常强大,本文主要介绍下 Elasticsearch
中高级搜索的使用。
Search APIs
搜索 APIs
按照查询方式主要可以分为两大类,那就是:URI earch
和 Request Body Search
。在查询语句中,一般使用 _search
来表示当前是一个搜索语句。
- /_search:查询集群上的所有索引数据,一般不建议这么使用。
- index1,index2/_search:查询指定一个或者多个索引的数据。
- index*/_search:利用通配符查询当前集群上的索引数据。
URI Search
顾名思义,URI Search
指的是直接使用 URL
进行查询,参数直接拼在 URL
上。
URI Search
中,主要有以下参数:
- q:指定查询语句,使用
Query String Syntax
语法(KV
键值对)。 - df:默认字段,如果不指定,则会对所有字段进行查询。
- sort:排序。
- explain:对每一个结果,都会返回
_explanation
结果,包含了当前数据分值的计算方式和结果。 - from/size:用于分页,
from
表示从哪条数据开始,size
表示当前需要查询多少条数据。 - _source:
false
表示不返回源数据(_source
字段),默认为true
。 - _source_includes:表示
_source
内只返回当前指定的字段。 - _source_excludes:表示
_source
内不返回当前指定的字段,当前参数优先级大于_source_includes
。 - timeout:指定超时时间,默认没有超时时间。
bulk 插入演示数据
为了便于后面演示,我们通过 bulk
操作来批量插入一些比较直观的数据:
POST index_001/_doc/_bulk
{"index":{}}
{"id":"1","name":"lonely wolf","result":true}
{"index":{}}
{"id":"2","name":"lonely hello wolf","result":true}
{"index":{}}
{"id":"3","name":"lonely hello word wolf","result":true}
{"index":{}}
{"id":"4","name":"lonely","result":false}
{"index":{}}
{"id":"5","name":"wolf","result":false}
或者执行以下语句:
POST /_bulk
{"index":{"_index":"index_001"}}
{"id":"1","name":"lonely wolf","result":true}
{"index":{"_index":"index_001"}}
{"id":"2","name":"lonely hello wolf","result":true}
{"index":{"_index":"index_001"}}
{"id":"3","name":"lonely hello word wolf","result":true}
{"index":{"_index":"index_001"}}
{"id":"4","name":"lonely","result":false}
{"index":{"_index":"index_001"}}
{"id":"5","name":"wolf","result":false}
基础查询
- 指定字段查询:
# 指定name字段查询
GET index_001/_search?q=name:wolf
- 使用默认字段查询:
GET index_001/_search?q=wolf&df=name
上面这两句话查询效果是一样的,均可以查询出 4
条数据,执行 profile
分析一下,确实只匹配了 name
一个字段:
- 泛查询(不指定任何字段)
GET index_001/_search?q=wolf
这条语句也是返回 4
条数据,但是这条语句和上面不同的是其没有通过 q
指定筛选字段,也没有通过 df
指定默认字段,所以会查询所有字段:
执行 profile
查询可以发现,这条语句会查询所有字段,而且有些类型不匹配则会报错,所以这种查询效率是很低的,生产环境中应该尽量避免。
- 指定
source
查询
再看下面的一个 source
查询例子:
GET index_001/_search?q=name:wolf&_source_includes=name,result&_source_excludes=result&timeout=1ms
这个例子中因为同时指定了 _source_excludes
和 _source_includes
,但是因为 _source_excludes
优先级比较高,故而最终只会返回 name
一个字段:
Term 查询
GET /index_001/_search?q=name:lonely wolf
这个查询会返回所有数据,因为默认情况下这个查询会使用 Term
查询,会查询 name
为 lonely
或者 wolf
的字段,而如果想把 lonely wolf
作为一个整体,则可以使用 Phrase
查询。
布尔操作
在上面 Term
查询中,我们发现当两个 Term
查询在一起,默认使用的是 or
的操作,而如果要使用 and
,则可以使用布尔操作。
布尔操作支持以下符号(必须大写):AND
,OR
,NOT
,&&
,||
,!
。如下例子则只会查询出 3
条数据。
GET /index_001/_search?q=name:lonely AND wolf
# 建议使用 () 来明确表示分组
GET /index_001/_search?q=name:(lonely AND wolf)
同时,布尔操作还支持一些高级查询,如:+
表示 must
,-
表示 must not
:
GET /index_001/_search?q=name:(+lonely -wolf)
这句话就只能查询出 id
为 4
的这条数据,name
含有 lonely
关键字且不含 wolf
关键字。
Phrase 查询
假如我们想把一句话当成一个整体来查询,则可以使用 Phrase
查询:
GET /index_001/_search?q=name:"lonely wolf"
这个时候就只会查询出一条数据。
通配符和正则查询
通配符查询中,?
表示 1
个字符,*
表示 0
或者多个字符。
# 没有符合条件的数据GET /index_001/_search?q=name:lone?# 1-4条数据都符合条件GET /index_001/_search?q=name:lone*
通配符查询是一种 like
查询,效率相对会比较低,所以一般也不建议使用。
此外,还可以通过正则表达式查询:
# 查询出 id 为 2 或 3 的数据GET /index_001/_search?q=id:(2|3)
近似查询
有些时候我们查百度的时候发现输错了字也能被查出来,这就是利用了近似查询,如下所示:
# 输错一个字符,查询不出结果GET /index_001/_search?q=name:loneyy# 允许一个字符错误,查询出 4 条数据GET /index_001/_search?q=name:loneyy~1# 允许两个字符错误,查询出 4 条数据GET /index_001/_search?q=name:loniyy~2
另外,针对上面的 Phrase
查询中,因为是把两个单词作为一个整体,那么也可以通过近似查询来设置允许中间有其他字符,如下:
GET /index_001/_search?q=name:"lonely wolf" ~1
这里表示允许 lonely
和 wolf
之间插入一个其他字符,所以可以查询出 2
条数据:
Request Body Query
Request Body
查询是 Elasticsearch
中基于 json
格式提供的一种 DSL
语言(Query Domain Specific Language)。一般情况下,相比较于 URI Request
,虽然说 URI Query
也可以实现一定复杂程度的查询,但是一般情况下我们还是更推荐使用 Request Body
查询来实现更加复杂的一些组合查询。
通过 URI Search
中能实现的搜索方式,都可以通过 Request Body
来实现,下面就让我们一起来看看如何利用 Request Body
来进行搜索查询。
分页查询
Request Body
分页查询也是通过 from
和 size
来实现:
POST index_001/_search
{
"from": 0,
"size": 2
}
排序
排序通过 sort
来实现。注意,默认 text
类型不能排序,如果需要排序则使用 field.keyword
来查询:
POST index_001/_search
{
"sort": [
{
"name.keyword": {
"order": "desc"
}
}
]
}
source 查询
同样的,Request Body Query
也可以使用 source
来指定返回字段,而且也支持通配符的方式来指定:
POST index_001/_search
{
"_source": ["id","*e*"]
}
这时候查询会返回 id
,name
和 result
三个字段。
使用 match 查询
如果需要指定条件查询,在 Request Body
查询中则通过 match
来实现:
POST index_001/_search
{
"query": {
"match": {
"name": "lonely wolf"
}
}
}
同样的,这个查询默认的也是“或”的关系,所以这里也是能将 5
条数据全部查询出来,如果要使用 AND
,则可以通过以下方式实现:
POST index_001/_search
{
"query": {
"match": {
"name": {
"query": "lonely wolf",
"operator": "and"
}
}
}
}
这样子就只能查询出前三条数据。
如果要查询全部数据,则可以利用 match_all
进行查询:
POST index_001/_search
{
"query": {
"match_all": {}
}
}
match phrase 查询
上面的查询中,即使指定了 and
,也能查询出 3
条数据,如果需要精确查询,那么可以使用 match phrase
查询:
POST index_001/_search
{
"query": {
"match_phrase": {
"name": {
"query": "lonely wolf",
"slop": 0
}
}
}
}
这里如果不加 slot
,则默认为 0
,也就是不允许 lonely wolf
两个单词之间存在其他字符,这样子就只能查询出第一条数据。
脚本 script field 查询
有时候我们需要利用脚本来对搜索结果进行进一步处理,这时候就可以脚本来实现一些功能,不过需要注意的是,text
字段默认也是不能使用脚本,如果需要是用同样需要加上 .keyword
:
POST index_001/_search
{
"script_fields": {
"id-name": {
"script": {
"lang": "painless",
"source": "doc['id.keyword'].value + '-' + doc['name.keyword']"
}
}
},
"query": {
"match_all": {}
}
}
返回结果如下,新的结果会作为字段 id-name
返回:
总结
本文主要介绍了 Elasticsearch
中的两种查询方式,并分别介绍了一些非常常用的查询方式,下一篇我们会专门介绍下本文提到的 Term
查询和 Phrase
查询,并彻底理解这两种查询方式的区别。
请关注我,和孤狼一起学习进步。