四、ElasticSearch的Search API
0、在ES中,为了实现对存储的数据进行查询分析,使用endpoint:_search。
可以实现对索引的不同查询,如:
A、实现对所有索引的泛查询:GET /_search
B、实现对一个索引的单独查询:GET /my_index/_search
C、实现对多个索引的指定查询:GET /my_index1,my_index2/_search
D、实现对符合指定要求的索引进行查询:GET /my_*/_search
在进行查询的时候,主要有两种方式:
A)URI Search。
特点:操作简单,直接通过命令行方便测试,但仅包含部分查询语法;
举例:
#URI Search方式进行查询
GET /my_index/_search?q=username:alfred
B)Request Body Search。
特点:ES提供的完备查询语法,使用Query DSL(Domain Specific Language)进行查询。
举例:
Request Body Search方式进行查询
GET /my_index/_search
{
"query":{
"match":{
"username":"alfred"
}
}
}
1、URI Search:
1)通过url query参数实现搜索,常用参数有:
A、q 指定查询的语句,使用query string syntax语法
B、df q中不指定字段时默认查询的字段 ===> 在不指定的时候默认查询所有字段
C、sort 排序
D、timeout 指定超时时间,默认不超时
E、from,size 用于分页
举例:
#使用url query查询示例:
GET my_index/_search?q=alfred&df=username&sort=age:asc&from=4&size=10&timeout=1s
解释:
查询索引my_index中username字段中包含alfred的文档,结果按age字段升序排列,返回第5——14个文档,若超过1s未结束,则以超时结束。
2)query string syntax语法
前置内容:term————单词,phrase————词语。
A、单词与词语语法:
单词:alfred way 等价于 alfred OR way
词语:"alfred way" 语句查询,要求先后顺序
泛查询:不指定字段,会在所有字段中去匹配其单词
指定字段查询:指定字段,在指定字段中匹配单词
B、Group分组设定,使用括号指定匹配的规则,举例:
#分组设定,使用括号进行分组
GET my_index/_search?q=username:(alfred OR way)AND lee
3)search api
A、泛查询:
#泛查询
GET my_index/_search?q=alfred
{
"profile":true #使用profile参数,可以明确地看到ES如何执行的查询条件
}
B、指定字段查询:
a、查询字段username中包含alfred的文档:
#查询字段username中包含alfred的文档
GET my_index/_search?q=username:alfred
b、查询字段username中包含alfred或way的文档
#查询字段username中包含alfred或way的文档
GET my_index/_search?q=username:alfred way
c、查询字段username为"alfred way"的文档
#查询字段username为"alfred way"的文档
GET my_index/_search?q=username:"alfred way"
d、分组后,查询字段username中包含alfred和包含way的文档
#分组后,查询字段username中包含alfred,包含way的文档
GET my_index/_search?q=username:(alfred way)
这个和b的结果一样,但是区别在于使用分组之后,不进行泛查询。
4)布尔操作符
A、AND(&&)、OR(||)、NOT(!),举例:
#查询索引my_index中username包含alfred但是不包含way的文档
GET my_index/_search?q=username:(alfred NOT way)
B、+对应must,-对应must_not,举例:
#查询索引my_index中一定包含lee,一定不含alfred,可能有way的文档
GET my_index/_search?q=username:(way +lee -alfred)
#或写成
GET my_index/_search?q=username:((lee && !alfred) || (way && lee && !alfred))
但是要注意一点:
在url中,+(加号)会被解析成空格,所以要使用encode后的结果,即:%2B,所以正确的查询语句应该为:
#查询索引my_index中一定包含lee,一定不包含alfred,可能包含way的文档
GET my_index/_search?q=username:(way %2Blee -alfred)
5)范围查询(支持数值和日期)
A、区间写法:闭区间使用[ ],开区间使用{ }
a、age:[1 TO 10] 1 <= age <= 10
b、age:[1 TO 10} 1 <= age < 10
c、age:[1 TO ] age >= 1
d、age:[* TO 10] age <= 10
B、算数符号写法:
a、age:>= 1
b、age:(>=1 && <= 10) / age:(+ >= 1 + <= 10)
举例:
#查询索引my_index中username字段包含alfred或年龄大于20的文档
GET my_index/_search?q=username:alfred age>20
#查询索引my_index中username字段包含alfred且年龄大于20的文档
GET my_index/_search?q=username:alfred AND age>20
C、还可以对日期进行范围查询,注意:年/月是从1月1号/1号开始算的:
#查询索引my_index中birth字段在1985和1990之间的文档
GET my_index/_search?q=birth:(>1985 AND < 1990)
6)通配符查询
?代表一个字符,*代表0个或多个字符,如:
name:a?lfred / name:a*d / name:alfred*
注意:通配符匹配的执行效率较低,且占用内存较多,不建议使用,如果没有特殊要求,也不要将?或者*放在最前面,因为意味着要匹配所有文档,可能会造成OOM。
7)此外,还支持正则表达式/模糊匹配/近似度查询等
A、正则表达式:举例:/[a]?l.*/
B、模糊匹配:fuzzy query。举例:
#模糊匹配。匹配与alfred差一个字符的词,比如:alfreds、alfret等
GET my_index/_search?q=username:alfred~1
C、近似度查询:proximity search。举例:
#近似度查询,查询字段username和"alfred way"差n个单词的文档
GET my_index/_search?q=username:"alfred way" ~5
使用场景常见于用户输入词的纠错中。
2、Request Body Search
ES自带的完备查询语句,将查询语句通过http request body发送到ES,主要参数有:
①query ===> 符合Query DSL语法的查询条件
②from,size
③timeout
④sort
Query DSL语法:
基于JSON定义的查询语言,主要包含两个类型:
1)字段类查询————如:term,match,range等。只针对一个字段进行查询
A、全文匹配
针对text类型的字段进行全文检索,会对查询语句进行“先分词再查询”处理,如:match、match_phrase等
a、match query
①对字段进行全文检索(最基本和最常用的查询类型),举例:
#match query
GET my_index/_search
{
"query":{
"match":{ #关键词
"username":"alfred way" #字段名和查询语句
}
}
}
从结果,可以返回匹配文件总数,返回文档列表,_score相关性得分等。
一般的执行流程为:
1.对查询语句分词==>2.根据字段的倒排索引列表,进行匹配算分==>3.汇总得分==>4.根据得分排序,返回匹配文档
②使用operator参数,可以控制单词间关系,有and / or:
#使用operator参数控制单词间关系
GET my_index/_search
{
"query":{
"match":{
"username":"alfred way",
"operator":"and" #and,同时包含alfred和way
}
}
}
③使用minimum_should_match参数控制需匹配的单词数
#使用minimum_should_match参数控制需匹配的单词数
GET my_index/_search
{
"query":{
"match":{
"username":"alfred way",
"minimum_should_match":"2"
}
}
}
b、相关性算分,其本质就是一个排序问题
计算文档与待查询语句之间的相关度,一般有四个重要概念:
a、Term Frequency 词频(正相关)
b、Document Frequency 文档频率(负相关)
c、Inverse Term Frequency 逆文本频率(正相关)
d、Field-length Norm 文档长度(负相关)
目前ES有两个相关性算分的模型:
①TF/IDF模型。经典模型。
在使用kibana进行查询时,使用explain参数,可以查看具体的计算方法。
#使用explain参数,可以查看具体的相关性的得分是如何计算的
GET my_index/_search
{
"explain":true, #设置为true
"query":{
"match":{
"username":"alfred"
}
}
}
注意:ES计算相关性得分是根据shard进行的,即分片的分数计算相互独立,所以在使用的时候要注意分片数,可以通过设定分片数为1来避免这个问题,主要是为了观察,不代表之后所有的分片全都设为1。一般放在创建索引后,未加数据之前。
#设定shards数量为1
PUT my_index
{
"settings":{
"number_of_shards":"1"
}
}
②BM25模型。5.x版本后的默认模型,是对TF/IDF的优化模型。best match,25指:迭代了25次才计算。
BM25的使用,降低了TF/IDF中因为TF过大导致的负面影响,在BM25中,一个单词的TF一直增长,到一定程度就趋于0变化。
c、match phrase query
对字段做全文检索,有顺序要求。
#使用match——phrase查询词语
GET my_index/_search
{
"query":{
"match_phrase":{ #关键词
"job":"java engineer"
}
}
}
通过使用slop参数,可以控制单词间间隔:
#使用slop参数,控制词语中的单词间隔
GET my_index/_search
{
"query":{
"match_phrase":{
"job":{
"query":"java engineer",
"slop":"1" #关键词,设定单词间隔
}
}
}
}
d、query string query
类似于URI Search中的q参数查询,举例:
#使用query_string查询
GET my_index/_search
{
"query":{
"query_string":{
"default_field":"username",
"query":{alfred AND way"
}
}
}
#或
GET my_index/_search
{
"query":{
"query_string":{
"fileds":["username","job"],
"query":"alfred OR (java AND ruby)"
}
}
}
e、simple query string query
类似于query string,但会忽略错误的查询语法,且仅支持部分查询语句。使用+,|,-分别代替AND,OR,NOT。
举例:
#使用simple query string query
GET my_index/_search
{
"query":{
"simple_query_string":{
"fields":[username],
"query":"alfred +way" #等价于 "query":"alfred AND way"
}
}
}
B、单词匹配
a、term/terms query
将待查询语句作为整个单词进行查询,不做分词处理,举例:
#使用term进行单查询
GET my_index/_search
{
"query":{
"term":{
"username":"alfred"
}
}
}
#使用terms进行多查询
GET my_index/_search
{
"query":{
"terms":{
"username":["alfred","way"]
}
}
}
此时如果直接使用alfred way作为username查询条件,是不会返回任何文档的。因为在username的倒排索引列表中,存在"alfred"和"way"的索引,但是不存在"alfred way"的索引。
b、range query
范围查询,主要针对数值类型和日期类型。
gt: greater than 大于
gte: greate than or equal to 大于等于
lt: less than 小于
lte: less than or equal to 小于等于
①对数值的查询
#range query对数值的查询
GET my_index/_search
{
"query":{
"range":{
"age":{
"gte":10,
"lte":20
}
}
}
}
②对日期的查询
#range query对日期的查询
GET my_index/_search
{
"query":{
"range":{
"birth":{
"lte":"1988-01-01" #或者使用 "lte":"now-30y",这种Date Math类型
}
}
}
}
Date Math类型:针对日期提供的一种更友好的计算方式。
当前时间用now代替,具体时间的引用,需要使用||间隔。年、月、日、时、分、秒跟date一致:y、M、w、d、h、m、s。举例:
#假设当前时间为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
2016-01-01||+1M/d => 2016-02-01 00:00:00
2)复合查询————如:bool查询等。包含一个/多个字段类查询/符合查询语句
A、constant_score query
将内部的查询结果文档得分全部设定为1或boost的值。返回的相关性得分全部为1或boost
#使用constant_score query
GET my_index/_Search
{
"query":{
"constant_score":{ #关键词
"match":{
"username":"alfred"
}
}
}
}
B、bool query
由一个/多个布尔子句组成,主要包含以下四个:
a、filter 只过滤符合条件的文档,不计算相关性得分,返回的相关性得分全部为0;
ES会对filter进行智能缓存,因此执行效率较高,在做简单匹配查询且不考虑得分的时候没推荐使用filter代替query
#使用filter查询
GET my_index/_search
{
"query":{
"bool":{ #关键词
"filter":[
"term":{
"username":"alfred"
}
]
}
}
}
b、must 文档必须符合must中的所有条件,影响相关性得分;
#使用must进行查询
GET my_index/_search
{
"query":{
"bool":{
"must":[
{
"match":{
"username":"alfred"
}
},
{
"match":{
"job":"specialist"
}
}
]
}
}
}
c、must_not 文档必须排除must_not中的所有条件;
#使用must_not进行查询
GET my_index/_search
{
"query":{
"bool":{
"must":[
{
"match":{
"job":"java"
}
}
],
"must_not":[
{
"match":{
"job":"ruby"
}
}
]
}
}
}
d、should 文档可以符合should中的条件,影响相关性得分,分为两种情况:
同时配合minimum_should_match控制满足调价你的个数/百分比。
①bool查询中只有should,不包含must的情况
#bool查询中只有should的情况
GET my_index/_search
{
"query":{
"bool":{
"should":[
{
"term":{"job":"java"} #条件1
},
{
"term":{"job":"ruby"} #条件3
}
{
"term":{"job":"specialist"} #条件3
}
],
"minimum_should_match":2 #至少需要满足两个条件
}
}
}
②bool查询中既有should,又包含must的情况,文档不必满足should中的条件,但是如果满足的话则会增加相关性得分。
#bool查询中同时包含should和must
GET my_index/_search
{
"query":{
"bool":{
"should":[ #同时包含should
{
"term":{"job":"ruby"}
}
],
"must":[ #同时包含must
{
"term":{"usernmae":"alfred"}
}
]
}
}
}
当一个查询语句位于query或filter上下文的时候,ES的执行结果也不同。
query | 查找和查询语句最匹配的文档,并对所有文档计算相关性得分 | query bool中的:must/should |
filter | 查找和查询语句最匹配的文档 | bool中的:filter/must_not constant_score中的:filter |
#query和filter上下文
GET my_index/_search
{
"query":{
"bool":{
"must":[ #query上下文
{
"term":{"title":"Search"}
},
{
"term":{"content":"ElasticSearch"}
}
],
"filter":[ #filter上下文
{
"term":{"status":"published"}
},
{
"range":{
"publish_date":{
"gte":"2015-01-01"
}
}
}
]
}
}
}
D、count API
获取符合条件的文档书,使用endpoint:_count。
#使用_count获取符合条件的文档数
GET my_index/_count #关键词
{
"query":{
"match":{
"username":"alfred"
}
}
}
E、Source Filtering
过滤返回结果中的_source中的字段,主要由以下两种方式:
a、GET my_index/_search?_source=username #url参数
b、使用Request Body Search:
#不返回_source
GET my_index/_search
{
"_source":false
}
#返回_source部分字段
GET my_index/_search
{
"_source":["username","age"]
}
#通配符匹配返回_source部分字段
GET my_index/_search
{
"_source":{
"includes":"*I*",
"encludes":"birth"
}
}