Elasticsearch查询主要有两种形式:URI Search、Request Body Search。
URI Search: 操作简单,方便通过命令行测试,但是仅包含部分查询语法。
GET /my_index/_search?q=user:tom
Request Body Search: es提供的完备查询语法Query DSL。
1 | GET /my_index/_search |
URI Search
URI Search就是通过拼接关键字来进行搜索的,常用的参数如下:
q:指定查询语句,语法遵循 Query String Syntax
df:q中不指定字段时默认的查询字段,如果不指定,es会查询所有字段
sort:排序
timeout:指定超时时间,默认不超时
from,size:用于分页
如下语句是查询user字段包含tom的文档,结果按照age升序排列,返回5~14个文档,如果超过1s没有结束,则以超时结束:
GET /my_index/_search?q=tom&df=user&sort=age:asc&from=4&size=10&timeout=1s
Query String Syntax
使用下面的命令,创建文档:
1 | POST /test_search_index/doc/_bulk |
泛查询
不指定字段查询,等效于在所有字段去匹配该term。
GET /test_search_index/_search?q=alfred
因3个文档的username
字段都包含alfred
,所以上面的查询将返回3个文档。
如果需要查看查询语句的执行过程(为了方便调优查询语句),可以设置profile
为true:
1 | GET /test_search_index/_search?q=alfred |
服务器将会返回查询语句的执行过程:
"description" : """(MatchNoDocsQuery("failed [isMarried] query, caused by illegal_argument_exception:[Can't parse boolean value [alfred], expected [true] or [false]]") | username.keyword:alfred | MatchNoDocsQuery("failed [birth] query, caused by parse_exception:[failed to parse date field [alfred] with format [strict_date_optional_time||epoch_millis]]") | job:alfred | MatchNoDocsQuery("failed [age] query, caused by number_format_exception:[For input string: "alfred"]") | username:alfred | job.keyword:alfred)"""
指定字段
如下语句指定在username字段查询:
GET /test_search_index/_search?q=username:alfred
返回的结果和上面的泛查询是一样的。
term 与 pharse
如下查询alfred way等效于 alfred OR way:
GET /test_search_index/_search?q=username:alfred way
因查询的是username字段中包含alfred OR way的文档,结果是3个文档都返回。
如下查询加了双引号,表示查询的”alfred way” 词语,要求先后顺序:
GET /test_search_index/_search?q=username:"alfred way"
返回只有id为1的文档。
Group 分组设定
可以使用括号指定匹配的规则:
如下相当于查询username字段中包含alfred OR way的文档:
GET /test_search_index/_search?q=username:(alfred way)
结果是3个文档都会返回。
布尔操作符
AND(&&)、OR(||)、NOT(!)
username:(tom NOT lee)
注意大写,不能小写
如下查询的是在username字段中包含alfred,way使用的是泛查询:
GET /test_search_index/_search?q=username:alfred AND way
返回的是2个文档。
如下查询的是在username字段中包含alfred和way的文档:
GET /test_search_index/_search?q=username:(alfred AND way)
返回2个文档,id为2的文档不会返回。
如下查询的是在username字段中包含alfred不包含way的文档:
GET /test_search_index/_search?q=username:(alfred NOT way)
只返回id为2的文档。
must、must_not
+、-分别对应must和must_not
username:(tom +lee -alfred)
+在url中会被解析为空格,要使用encode后的结果:%2B。
如下是查询username字段中包含alfred但必须包含way的文档:
GET test_search_index/_search?q=username:(alfred %2Bway)
返回的是id为1、4的文档。
范围查询
范围查询支持数值和日期。
区间写法,闭区间用[]
,开区间用{}
:
age:[1 TO 10] 意为 1<= age <= 10
age:[1 TO 10} 意为 1<= age < 10
age:[1 TO } 意为 age >= 1
age:[* TO 10} 意为 age <= 10
算术符号写法:
age:>=1
age:(>=1 && <=10)或者age:(+>=1 +<=10)
如下查询的是username字段包含alfred或age大于20的文档:
GET /test_search_index/_search?q=username:alfred age:>20
返回的结果是全部的4个文档。
如下查询的是username字段包含alfred并且age大于20的文档:
GET /test_search_index/_search?q=username:alfred AND age:>20
返回的结果是id为2和4的文档。
如下查询的是birth在1980至1990的文档:
GET /test_search_index/_search?q=birth:(>1980 AND <1990)
返回的结果是id为2、3、4的文档。
通配符查询
?
代表1个字符,*
代表0或多个字符,如username:t?m
、username:tom*
、username:t*m
。
通配符匹配执行效率低,且占用较多内存,不建议使用。
如无特殊要求,不要将
?
、*
放在最前面。
如下查询的是在username字段中以alf开头的所有文档:
GET /test_search_index/_search?q=username:alf*
正则表达式
es的查询也支持正则表达式匹配:username:/[mb]oat/
。
如下的查询将会返回全部的4个文档:
GET /test_search_index/_search?q=username:/[a]?l.*/
模糊匹配
如username:roam~1
: 匹配与roam差一个字符的词,比如foam、roams等。
如下查询将会匹配username字段中包含alfred的文档:
GET /test_search_index/_search?q=username:alfed~1
GET /test_search_index/_search?q=username:alfd~2
近似度查询
如"fox quick"~5
以term为单位进行差异比较,比如"quick fox"
、"quick brown fox"
都会被匹配。
如下查询将会返回id为1、2、3的文档:
GET /test_search_index/_search?q=job:"java engineer"~3
Request Body Search
Request Body Search 就是将http request body 发送到es,主要参数如下:
query:符合 Query DSL语法
from,size
timeout
sort
Query DSL
这是一个基于JSON定义的查询语言,主要包含两种类型:
字段查询: 如 term、match、range等,只针对某一字段进行查询
复合查询: 如bool查询等,包含一个或多个字段类查询或复合查询语句
字段类查询
全文匹配: 针对text类型的字段进行全文检索,会对查询语句先进行分词处理。如match、match_phrase等query类型。
单词匹配: 不会对查询语句做分词处理,直接去匹配字段的倒排索引,如term、terms、range等query类型
match query
如下查询首先会对"alfred way"
进行分词,返回的是包含alfred或way的文档:
1 | GET /test_search_index/_search |
如服务器会返回以下3个文档:took
: 查询耗时、hits.total
:返回的文档个数、_score
:文档相关度得分:
1 | { |
通过设置match query的operator
参数可以控制单词间的匹配关系,可选项为or和and,默认为or。
如下查询,返回的是username字段中同时包含alfred和way的文档:
1 | GET /test_search_index/_search |
通过设置minimum_should_match
参数可以控制需要匹配的单词数。
如下查询,返回的是job字段至少包含java、ruby、engineer的2个以上单词匹配的文档:
1 | GET /test_search_index/_search |
match phrase query
按词语查询,对待查询的词语有顺序要求:
如下查询的是job字段包含"java engineer"
的文档:
1 | GET /test_search_index/_search |
通过slop
参数可以控制单词间的间隔:
如下查询的是job字段java和engineer相隔0个或1个单词的文档,如:java engineer
、java senior engineer
:
1 | GET /test_search_index/_search |
query string query
类似于 URI search中的q参数查询:
如下查询的是username字段中包含alfred和way的文档:
1 | GET /test_search_index/_search |
通过fields
参数可以设置对多个字段进行查询:
如下查询的是username和job字段,条件是包含alfred或同时包含java和ruby:
1 | GET /test_search_index/_search |
simple query string query
类似于query string,但是会忽略掉错误的查询语法,并且仅支持部分的查询语法。不能使用 AND,OR,NOT等关键词:
+
代替 AND|
代替 OR-
代替 NOT
如下查询的username字段中包含alfred和way的文档:
1 | GET /test_search_index/_search |
term query
将查询语句作为整个单词进行查询,即不对查询语句做分词。
如下查询的是username字段中包含alfred的文档:
1 | GET /test_search_index/_search |
如下查询的是username字段中包含"alfred way"
的文档,返回的文档将会是0个,因为username倒排索引并没有存储"alfred way"
词语,而是存储的是"alfred way"
分词后的结果:
1 | GET /test_search_index/_search |
terms query
一次传入多个单词进行查询。
如下查询的是username字段中包含alfred和way的文档:
1 | GET /test_search_index/_search |
range query
范围查询主要针对日期和数值类型:gt(大于)、gte(大于等于)、lt(小于)、lte(小于等于)
如下查询的是age在10-20之间的文档:
1 | GET /test_search_index/_search |
如下查询的是birth大于等于1990-01-01
的文档:
1 | GET /test_search_index/_search |
Date Math
Date Math是针对日期的一种更友好的计算格式,可以实现最近几天、最近几个小时等功能:
now-1d
now
表示当前时间,也可以是具体的日期,如:2018-01-01,使用具体日期的时候需要用||
做隔离。-1d
表示减一天、+1h
表示加1个小时、/d
将时间舍入到天
主要的时间单位有:y(year)、M(months)、w(week)、d(days)、h(hours)、m(minutes)、s(seconds)
假设now为2018-01-02 12:00:00
,那么如下的计算结果实际为:
计算公式 | 实际结果 |
---|---|
now+1h | 2018-01-02 13:00:00 |
now-1h | 2018-01-02 11:00:00 |
now-1h/d | 2018-01-02 00:00:00 |
2017-01-01||+1M/d
:表示为2017-02-01 00:00:00
如下查询的是birth距离当前日期30年的文档:
1 | GET /test_search_index/_search |
如下查询的是birth距离2010年20年的文档:
1 | GET /test_search_index/_search |
复合查询
复合查询是指包含字段类查询或复合查询的类型,主要有以下几种:
constant_score query
bool query
dis_max query
function_score query
boosting query
constant_score query
constant_score query将其内部的查询结果文档得分都设定为1或者boost的值,多用于结合bool查询实现自定义得分。
如下查询返回的结果中的_score
将会设置为1.0
:
1 | GET /test_search_index/_search |
bool query
bool query由一个或多个bool子句组成,主要包含以下4个:
filter | 只过滤符合条件的文档,不计算相关性得分 |
must | 文档必须符合must中的所有条件,会影响相关性得分 |
must_not | 文档必须不符合must_not中的所有条件 |
should | 文档可以符合should中的条件,会影响相关性得分 |
filter
filter查询只返回符合条件的文档而不关心相关性,不进行相关性算分。
es对filter有智能缓存,所以其执行效率很高
做简单查询而不考虑相关性算分的时候,推荐使用filter代替query等
如下查询服务器返回的_score
为0,表示不计算相关性算分:
1 | GET /test_search_index/_search |
must
must指定了必须符合的条件。
如下查询的username包含alfred并且job包含specialist关键词的文档列表,es会对结果进行相关性算分:
1 | GET /test_search_index/_search |
must_not
must_not是指排除must_not中符合条件的文档。
如下查询job中包含java关键词但不包含ruby关键词的文档列表:
1 | GET /test_search_index/_search |
should
should的使用有两种情况:
bool查询中只包含should,不包含must查询
bool查询中同时包含should和must查询
只包含should时,文档必须满足至少一个条件,可以通过minimum_should_match
可以控制满足条件的个数或者百分比。
如下查询的是job中必须包含java、ruby、specialist两个或两个以上匹配的文档:
1 | GET /test_search_index/_search |
同时包含should和must时,文档不必满足should中的条件,但是如果满足条件,会增加相关性得分。
如下查询username包含alfred的文档,同时将job包含ruby的文档排在前面:
1 | GET /test_search_index/_search |
query context 和 filter context的区别
当一个查询语句位于Query或者Filter上下文时,es执行的结果会不同,对比如下:
count
用于返回符合条件的文档数:
1 | GET /test_search_index/_count |
source filtering
过滤返回结果中_source的字段,节省网络开销。
可以在URL参数_source中指定返回的字段:
GET /test_search_index/_search?_source=username
禁止返回_source:
1 | GET /test_search_index/_search |
返回部分字段:
1 | GET /test_search_index/_search |