Elasticsearch提供了基于JSON的完整查询DSL(特定于域的语言)来定义查询。 将查询DSL视为查询的AST(抽象语法树),它由两种子句组成:
match
,term
或range
查询。 这些查询可以自己使用。bool
语句 允许在你需要的时候组合其它语句,无论是 must
匹配、 must_not
匹配还是 should
匹配,同时它可以包含不评分的过滤器(filters
):查询子句的行为会有所不同,具体取决于它们是在查询上下文中还是在过滤上下文中使用。
POST /gb/_search
{
"query": {
"bool": {
"must": { "match": { "tweet": "elasticsearch" }},
"must_not": { "match": { "name": "mary" }},
"should": { "match": { "tweet": "full text" }},
"filter": { "range": { "age" : { "gt" : 30 }} }
}
}
}
默认情况下,Elasticsearch通过相关性得分对匹配的搜索结果进行排序,该得分衡量每个文档与查询的匹配程度。
相关性分数是一个正浮点数,在搜索API的_score
元字段中返回。 _score
越高,文档越相关。 虽然每种查询类型可以不同地计算相关性分数,但是分数计算还取决于查询子句是在查询上下文中还是在过滤上下文中运行。
在查询上下文中,查询子句回答以下问题:“此文档与该查询子句的匹配程度如何?” 除了确定文档是否匹配之外,查询子句还计算_score元字段中的相关性得分。
只要将查询子句传递给查询参数(例如搜索API中的查询参数),查询上下文就有效。
在过滤上下文中,查询子句回答问题“此文档是否与此查询子句匹配?” 答案是简单的“是”或“否”,即不计算分数。 过滤器上下文主要用于过滤结构化数据,例如:
每当将查询子句传递到过滤器参数(例如bool
查询中的filter
或must_not
参数,constant_score
查询中的filter
参数或过滤聚合)时,过滤上下文即生效。
以下是在搜索API的查询和过滤器上下文中使用的查询子句的示例。 此查询将匹配满足以下所有条件的文档:
GET /_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Search" }},
{ "match": { "content": "Elasticsearch" }}
],
"filter": [
{ "term": { "status": "published" }},
{ "range": { "publish_date": { "gte": "2015-01-01" }}}
]
}
}
}
复合查询就是将一些简单的查询组合在一起作为查询条件进行文档检索。
默认查询,用于组合多个叶子或复合查询子句(must
,should
,must_not
或filter
子句)。 must和should子句的分数组合在一起-匹配的子句越多越好-而must_not和filter子句在过滤器上下文中执行。
现实的查询需求从来都没有那么简单;它们需要在多个字段上查询多种多样的文本,并且根据一系列的标准来过滤。为了构建类似的高级查询,你需要一种能够将多查询组合成单一查询的查询方法。你可以用 bool
查询来实现你的需求。这种查询将多查询组合在一起,成为用户自己想要的布尔查询。它接收以下参数:
must
must_not
should
filter
bool
查询采用的是“更好匹配”的方法,因此,每个匹配的must
或should
子句的分数将加在一起,以提供每个文档的最终_score。
POST _search
{
"query": {
"bool" : {
"must" : {
"term" : { "user" : "kimchy" }
},
"filter": {
"term" : { "tag" : "tech" }
},
"must_not" : {
"range" : {
"age" : { "gte" : 10, "lte" : 20 }
}
},
"should" : [
{ "term" : { "tag" : "wow" } },
{ "term" : { "tag" : "elasticsearch" } }
]
}
}
}
一种复合查询,分为positive子查询和negitive子查询,两者的查询结构都会返回。
positive
子查询的score保持不变;negetive
子查询的score值将会根据negative_boost
的值做相应程度的降低(score
乘以negative_boost
)。GET /_search
{
"query": {
"boosting" : {
"positive" : {
"term" : {
"text" : "apple"
}
},
"negative" : {
"term" : {
"text" : "pie tart fruit crumble tree"
}
},
"negative_boost" : 0.5
}
}
}
constant_score
检索可以包装一个其它类型的查询,并返回匹配过滤器中的查询条件且具备相同评分的文档。
GET /shakespeare/_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"text_entry": "apple"
}
},
"boost": 1.2
}
}
}
上述查询条件会返回包含apple单词的文档,并且评分都是1.2
使用 dis_max
即分离最大化查询(Disjunction Max Query) 。分离(Disjunction)的意思是 或(or) 。
分离最大化查询(Disjunction Max Query)指的是: 将任何与任一查询匹配的文档作为结果返回,但只将最佳匹配的评分作为查询的评分结果返回 :
PUT /my_index/my_type/1
{
"title": "Quick brown rabbits",
"body": "Brown rabbits are commonly seen."
}
PUT /my_index/my_type/2
{
"title": "Keeping pets healthy",
"body": "My quick brown fox eats rabbits on a regular basis."
}
查询:
{
"query": {
"bool": {
"should": [
{ "match": { "title": "Brown fox" }},
{ "match": { "body": "Brown fox" }}
]
}
}
}
响应:
{
"hits": [
{
"_id": "1",
"_score": 0.14809652,
"_source": {
"title": "Quick brown rabbits",
"body": "Brown rabbits are commonly seen."
}
},
{
"_id": "2",
"_score": 0.09256032,
"_source": {
"title": "Keeping pets healthy",
"body": "My quick brown fox eats rabbits on a regular basis."
}
}
]
}
查询:
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "Brown fox" }},
{ "match": { "body": "Brown fox" }}
]
}
}
}
响应:
{
"hits": [
{
"_id": "2",
"_score": 0.21509302,
"_source": {
"title": "Keeping pets healthy",
"body": "My quick brown fox eats rabbits on a regular basis."
}
},
{
"_id": "1",
"_score": 0.12713557,
"_source": {
"title": "Quick brown rabbits",
"body": "Brown rabbits are commonly seen."
}
}
]
}
function_score 查询 是用来控制评分过程的终极武器,它允许为每个与主查询匹配的文档应用一个函数,以达到改变甚至完全替换原始查询评分 _score 的目的。
全文查询使您可以搜索分析的文本字段,例如电子邮件的正文。 使用在索引期间应用于字段的同一分析器来处理查询字符串。
match_all
查询简单的匹配所有文档。在没有指定查询方式时,它是默认的查询:
{ "match_all": {}}
它经常与 filter
结合使用—例如,检索收件箱里的所有邮件。所有邮件被认为具有相同的相关性,所以都将获得分值为 1 的中性 _score
。
match搜索会对用户给出的关键词进行解析,首先会将其进行分词处理,分词后查询语句中的任意一个词项被匹配,文档就会检索到。其子参数包含:
query
(必填):搜索关键文本analyzer
(可选):分词器,用于将query中的文本转换为一个个词项fuzziness
(可选):使用编辑距离技术进行模糊匹配prefix_length
:模糊匹配的起始单词数operator
(默认为OR):布尔逻辑,用来解释query中的词项,可选的参数有OR、ANDminimum_should_match
(可选):返回的文档需要匹配的最小子串数GET /shakespeare/_search
{
"query": {
"match": {
"text_entry": {
"query": "apple eye",
"operator": "and"
}
}
}
}
查询包含text_entry
字段中包含单词apple和eye的文档。
详情参见:match
multi_match
查询可以在多个字段上执行相同的 match 查询:
GET /shakespeare/_search
{
"query": {
"multi_match": {
"query": "apple",
"fields": ["text_entry", "speaker"]
}
}
}
上述查询语句会在text_entry
和speaker
字段中查询包含单词apple
的文档。
match_phrase搜索会首先把query进行分词处理,分词器可以自定义,同时文档还需满足以下条件才能被匹配:
GET /shakespeare/_search
{
"query": {
"match_phrase": {
"text_entry": {
"query": "apple eye",
"operator": "and"
}
}
}
}
只有eye在apple后面的文档才可以被搜索到。
注:
要执行短语搜索而不是匹配单个术语,请使用match_phrase
而不是match
。 例如,以下请求仅匹配包含短语mill lane
的地址:
GET /bank/_search
{
"query": { "match_phrase": { "address": "mill lane" } }
}
和match_phrase搜索类似,只不过match_phrase_prefix支持词项的前缀匹配,例如:
GET /shakespeare/_search
{
"query": {
"match_phrase_prefix": {
"text_entry": {
"query": "app"
}
}
}
}
那么只会查找出某个词项的前缀为app的文档。
match_bool_prefix
搜索于Elasticsearch 7.0推出,它将输入的文本通过指定的分词器来处理多个term
,然后基于这些term
进行bool quer
y,除了最后一个term
使用前缀查询,其它都是使用term_query
。
GET /shakespeare/_search
{
"query": {
"match_bool_prefix": {
"query": "away as nim",
}
}
}
上述示例相当于查询包含单词away、as,并且有单词以nim开头的文档。
全文搜索在查询前会分析query字符串,而==词项搜索一般用于对存储的词项进行精确搜索,通常用于一些结构化数据,例如数字、枚举类型、日期类型等。
最基本的词项搜索,用于检索出某个text
字段中包含指定单词的文档。例如:
GET /shakespeare/_search
{
"query": {
"term": {
"text_entry": {
"value": "apple",
"boost": 1.0
}
}
}
}
上述请求可以查找出text_entry
字段中包含单词apple的文档。
value
参数为需要查找的词项;
boost
用来减少或增加相关性系数,默认为1.0,大于1.0会增加其相关性,小于1.0会减少其相关性。
terms
可以用来查找text
字段中包含一个或多个指定单词的文档,例如:
GET /shakespeare/_search
{
"query": {
"terms": {
"text_entry": [
"eye","apple"
]
}
}
}
上述请求会查找出text_entry
字段中满足以下要求的文档:
同样,terms
也可以使用boost
来减少或增加相关性系数。
regexp
检索可以查找出包含满足正则表达式单词的文档,例如:
GET /shakespeare/_search
{
"query": {
"regexp": {
"text_entry": "pa.*"
}
}
}
上述请求会查找出text_entry
字段中包含以pa
开头的单词的文档。
需要注意regexp检索不支持包含^
(行的开头)或$
(行的结尾)的正则表达式。
regexp检索还支持以下几个参数:
flags
:启用正则表达式可选运算符max_determinized_states
:查询所需的最大自动机状态数,用于限制过于复杂的正则表达式,默认10000。prefix检索可以查找出包含指定字符串开头的单词的文档,例如:
GET /shakespeare/_search
{
"query": {
"prefix": {
"text_entry": {
"value": "par"
}
}
}
}
上述请求会查找出包含以par开头的单词的文档。
range检索可以用于查询指定字段中在指定范围内的文档,例如:
GET /shakespeare/_search
{
"query": {
"range": {
"line_id": {
"gte": 160,
"lte": 170
}
}
}
}
上述请求可以查找出line_id
字段大于等于160,小于等于170的文档。
被允许的操作符如下:
gt
:大于gte
:大于等于lt
:小于lte
:小于等于wildcard检索用于单词的通配符检索,相当于一个简化版本的regexp检索,?
用于匹配一个任意字符,*
号用于匹配0个或者多个任意字符,相当于正则的.*
。例如:
GET /shakespeare/_search
{
"query": {
"wildcard": {
"text_entry": {
"value": "pre*"
}
}
}
}
上述请求可以查找出text_entry字段中包含以pre开头的单词的文档。此外wildcard也支持boost参数,作用类似。
fuzzy
检索用于词项的近似检索,例如applx可以检索出包含apple单词的文档,两个单词的相似度通过编辑距离算法(Levenshtein)确定。例如:
GET /shakespeare/_search
{
"query": {
"fuzzy": {
"text_entry": "applx"
}
}
}
上述请求可以查找出包含近似applx单词的文档,例如包含apple、apply的文档等。
fuzzy
查询效率不高,需要消耗的资源比较大。
查询可以变得非常的复杂,尤其和不同的分析器与不同的字段映射结合时,理解起来就有点困难了。不过 validate-query
API 可以用来验证查询是否合法。
GET /gb/_validate/query
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
以上 validate 请求的应答告诉我们这个查询是不合法的:
{
"valid" : false
}
为了找出 查询不合法的原因,可以将 explain
参数 加到查询字符串中:
GET /gb/_validate/query?explain
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
explain 参数可以提供更多关于查询不合法的信息。
很明显,我们将查询类型(match)与字段名称 (tweet)搞混了:
{
"valid" : false,
"error" : "org.elasticsearch.common.ParsingException: no [query] registered for [tweet]"
}
对于合法查询,使用 explain
参数将返回可读的描述,这对准确理解 Elasticsearch 是如何解析你的 query
是非常有用的:
ET /_validate/query?explain
{
"query": {
"match" : {
"tweet" : "really powerful"
}
}
}
我们查询的每一个 index
都会返回对应的 explanation
,因为每一个 index
都有自己的映射和分析器:
{
"valid" : true,
"_shards" : { ... },
"explanations" : [ {
"index" : "us",
"valid" : true,
"explanation" : "tweet:really tweet:powerful"
}, {
"index" : "gb",
"valid" : true,
"explanation" : "tweet:realli tweet:power"
} ]
}
从 explanation
中可以看出,匹配 really
powerful
的 match
查询被重写为两个针对 tweet
字段的 single-term 查询,一个single-term查询对应查询字符串分出来的一个term
。
当然,对于索引 us
,这两个 term 分别是 really
和 powerful
,而对于索引 gb
,term 则分别是 realli
和 power
。之所以出现这个情况,是由于我们将索引 gb
中 tweet
字段的分析器修改为 english
分析器。
一个带请求体的 GET 请求?
某些特定语言(特别是 JavaScript)的 HTTP 库是不允许 GET 请求带有请求体的。事实上,一些使用者对于 GET 请求可以带请求体感到非常的吃惊。
而事实是这个RFC文档 RFC 7231— 一个专门负责处理 HTTP 语义和内容的文档 — 并没有规定一个带有请求体的 GET 请求应该如何处理!结果是,一些 HTTP 服务器允许这样子,而有一些 — 特别是一些用于缓存和代理的服务器 — 则不允许。
对于一个查询请求,Elasticsearch 的工程师偏向于使用 GET 方式,因为他们觉得它比 POST 能更好的描述信息检索(retrieving information)的行为。然而,因为带请求体的 GET 请求并不被广泛支持,所以 search API同时支持 POST 请求:
POST /_search
{
"from": 30,
"size": 10
}