elasticsearch中的请求体查询详解
空查询
- 空查询将会返回所有索引库(indices)中的所有文档:
GET /_search - 只用一个查询字符串你就可以在一个或者多个索引库中进行查询
GET /test,drivers/_search - 同时可以使用from和size参数来分页
GET /_search
{
"from": 1,
"size": 1
}
相对于使用晦涩难懂的查询字符串的方式,一个带请求体的查询允许我们使用
查询领域特定语言Query DSL来写查询语句
查询表达式
- 查询表达式Query DSL是一种非常灵活且富有表现力的查询语言,es可以使用json接口来展现lucene的强大功能
在应用中,应该用查询表达式Query DSL来编写查询语句,他可以使你的查询语句更灵活、更精确、易读和易调试
要使用这样查询表达式,只需要将查询语句传递给query参数 - 空查询在功能上等价于match_all查询
- 空查询
GET /_search - match_all查询
GET /search
{
"query": {
"match_all": {}
}
}
- 查询语句的结构
可以使用match查询语句来查询name字段包含zhang的文档
GET /_search
{
"query": {
"match": {
"name": "zhang"
}
}
}
- 合并查询请求
- 筛选date域在大于2014-09-20,小于2014-09-24的文档
{
"query": {
"bool": {
"must": [
{
"range": {
"date": {
"gt": "2014-09-20"
}
}
},
{
"range": {
"date": {
"lt": "2014-09-24"
}
}
}
]
}
}
}
查询语句query clauses就像一些简单的组合块,这些组合块可以彼此之间合并形成更复杂的查询
语句形式如下;
叶子语句(leaf clauses), (就像match语句),被用于将查询字符串和一个字段(或多个字段)对比
复合(compound)语句主要用于合并其它查询语句,比如一个bool语句,允许你在需要的时候组合其它语句
无论是must匹配,mast_not匹配还是should匹配,还有filter匹配
案例:查询us索引库中date域大于2014-09-14
,tweet域包含elasticsearch
,date域不等于2014-09-16
,
date域等于2014-09-22
{
"query": {
"bool": {
"must": [
{
"range": {
"date": {
"gt": "2014-09-14"
}
}
},
{
"match": {
"tweet": "elasticsearch"
}
}
],
"must_not": {
"term": {
"date": "2014-09-16"
}
},
"filter": {
"term": {
"date": "2014-09-22"
}
}
}
}
}
一条复合语句可以合并任何其它查询语句,包括复合语句,了解这一点是很重要的
这就意味着,复合语句之间可以互相嵌套,可以表达非常复杂的逻辑
- should可以进行or查询
查询tweet域包含@mary
或者date域大于2014-09-22
{
"query": {
"bool": {
"should": [
{
"match": {
"tweet": "@mary"
}
},
{
"range": {
"date": {
"gt": "2014-09-22"
}
}
}
]
}
}
}
- should可以进行or查询,在加上must联合查询呢
查询(tweet域包含"@mary"或者date域大于"2014-09-22") 并且date域等于"2014-09-20"
GET /us/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"tweet": "@mary"
}
},
{
"range": {
"date": {
"gt": "2014-09-22"
}
}
}
],
"must": {
"term": {
"date": "2014-09-20"
}
}
}
}
}
查询结果,只查询到了date域等于"2014-09-20"的文档,也就是说must查询表达式起作用了,should没有起作用
其实should在于must或者filter同级时,默认是不需要满足should中的任何条件的,此时我们可以加上minimum_should_match参数
来达到我们的目的,代码改为
{
"query": {
"bool": {
"should": [
{
"match": {
"tweet": "@mary"
}
},
{
"range": {
"date": {
"gt": "2014-09-20"
}
}
}
],
"must": {
"terms": {
"date": ["2014-09-24", "2014-09-22"]
}
},
"minimum_should_match": 1
}
}
}
上述代码表示必须满足must中的所有条件,并且至少满足should中的一个条件,这样就得到了预期的结果,
当然我们也可以使用另外一种方式实现,即将should放到must中的一个bool条件中,即用分层的方式让should和must不同时出现
{
"query": {
"bool": {
"must": [
{
"terms": {
"date": ["2014-09-22", "2014-09-24"]
}
},
{
"bool": {
"should": [
{
"match": {
"tweet": "@mary"
}
},
{
"range": {
"date": {
"gt": "2014-09-22"
}
}
}
]
}
}
]
}
}
}
上述两种方法都可以解决should与must或者filter共存的问题,
- 核心需要知道的是:
- 一条复合语句可以将多条语句,叶子语句和其它复合语句,合并成一个单一的查询语句
查询与过滤
- es使用的查询语言DSL拥有一套查询组件,这些组件可以无限组合的方式进行搭配
这套组件可以在一下两种情况使用过滤情况filtering context, 查询情况query context - 当使用过滤情况时,查询被设置成不评分或者过滤查询,
- 当使用查询情况下,查询就被设置成了评分查询,和不评分的查询类似,也要去看这个文档是否匹配
同时也要看这个文档匹配的有多好(匹配程度如何),查询的典型用法是查找一下文档
- 查找与full text context这个词语最佳匹配的文档
- 包含 run 这个词,也能匹配 runs 、 running 、 jog 或者 sprint
- 包含 quick 、 brown 和 fox 这几个词 — 词之间离的越近,文档相关性越高
- 标有 lucene 、 search 或者 java 标签 — 标签越多,相关性越高
- 一个评分查询计算每一个文档与此查询的相关程度,同时将这个相关程度分配给表示相关性的字段_score
并且按照相关性对匹配的文档进行排序,这种相关性的概念非常适合全文搜索情况,因为全文搜索几乎没有
"完全"正确的答案 - es自问世以来,查询与过滤(queries filters)就独自成为了es的组件,但从2.0开始,过滤已经从技术上被排除了,
同时所有的查询(queries)拥有了变成不评分查询的能力,然而为了明确和简单,我们用filter这个词表示不评分,
只过滤情况下的查询,filter和should联合使用案例,需要配合minimum_should_match: 1
{
"query": {
"bool": {
"filter": [
{
"match": {
"tweet": "is"
}
}
],
"should": [
{
"match": {
"name": "jone"
}
},
{
"match": {
"tweet": "elasticsearch"
}
}
],
"minimum_should_match": 1
}
}
}
filter只查询不评分,而must和should即查询又评分
当只使用filter查询时你会发现,_score字段是0.0,因为filter过滤查询不评分
6. 性能差异
过滤查询(filter queries)只是简单的查询包含或者排除,这就使得计算起来非常快,同时结果会被缓存起来
评分查询(scoring queries)不仅要找出匹配的文档,还要计算每个匹配文档的相关性,计算相关性使得它们比不评分查询费力的多
同时,查询结果并不缓存
多亏倒排索引(inverted index),一个简单的评分查询在匹配少量的文档时可能与一个涵盖百万文档的filter表现的一样好
甚至会更好,但是一般情况下, filter会比评分的query性能更好,并且每次的表现都很稳定
- 过滤filter的目标是减少那些需要通过评分查询进行检查的文档
- 如何选择查询与过滤
通常的规则是,使用查询语句来进行全文搜索,或者其它任何需要影响相关性得分的搜索
除此之外的情况都需要考虑用filter
最重要的查询
- match_all查询
简单的匹配所有文档,在没有指定查询方式时,它是默认的查询
GET /us/_search
{
"query": {
"match_all": {}
}
}
- match查询
无论你在任何字段上进行的全文搜索还是精确查询,match查询是你可用的标准查询
如果你在一个全文字段上使用match查询,在执行查询前,它将用正确的分析器去分析查询字符串,例如:
GET /us/_search
{
"query": {
"match": {
"tweet": "@mary API"
}
}
}
分析器会先分析查询参数"@mary API"成两个token词条mary api,然后再去索引匹配
- 如果在一个精确值的字段上使用match, 例如数字、日期、布尔或者一个keyword字符串字段,
那么它会精确匹配给定的值
{ "match": { "age": 26 }}
{ "match": { "date": "2014-09-01" }}
{ "match": { "public": true }}
{ "match": { "tag": "full_text" }}
对于精确值的查询,我们需要使用filter过滤查询,因为filter将会被缓存
- multi_match查询
multi_match查询可以在多个字段上执行相同的match查询
GET /us/_search
{
"query": {
"multi_match": {
"query": "mayanan API",
"fields": ["name", "tweet"]
}
}
}
- range查询
range查询找出那些落在指定区间内的数字或日期
GET /us/_search
{
"query": {
"range": {
"date": {
"gt": "2014-09-16",
"lt": "2014-09-20"
}
}
}
}
- 被允许的操作符如下:gt gte lt lte
- term查询
term查询用于精确值匹配,这些精确值可能是数字、日期、布尔或者keyword类型的字符串
{
"query": {
"term": {
"date": "2014-09-16"
}
}
}
term查询对于输入的文本不分析,所以它将给定的值进行精确查询
- terms查询
terms查询和term查询一样,但它允许你指定多值进行匹配,如果指定字段中包含了指定值的任何一个值
那么这个文档就满足条件
GET /us/_search
{
"query": {
"terms": {
"date": ["2014-09-16", "2014-09-22"]
}
}
}
- 和term查询一样,terms查询对于输入的文本也不分析,它查询那些精确匹配的值(包括在大小写,重音,空格等方面的差异)
- exists查询
用于查找那些指定字段有值的文档,这与sql中的not is_null有共性
GET /us/_search
{
"query": {
"exists": {
"field": "username"
}
}
}
上面的查询将会查找username字段有值的文档
该查询经常用于查某个字段有值的情况
组合多查询
实际应用中,我们需要在多个字段中查询多种多样的文本,并且根据一系列的标准来过滤,
为了构建类似的高级查询,你需要一种将多查询组合成单一查询的查询方法
- 你可以使用bool查询来实现查询需求,这种查询将多查询组合在一起,成为用户自己想要的布尔查询
它接收一下参数:
- must: 文档必须匹配这些条件才能被包含进来,相当于mysql中的and
- must_not: 文档必须不匹配这些条件才能被包含进来, 相当于mysql中的not and
- should: 文档只要匹配这些条件中的任意一个,将会被包含进来, 相当于mysql中的or
- filter: 必须匹配,但它不以评分模式,而是以过滤模式来进行,这些语句对评分没有贡献
只是根据过滤标准来包含或者排除文档
filter案例, 查找date字段等于2014-09-22的文档
{
"query": {
"bool": {
"filter": {
"term": {
"date": "2014-09-22"
}
}
}
}
}
- 由于这是第一个我们看到包含多个查询的查询,所以必须要讨论一下相关性得分是如何组合的,
每一个子查询都独自计算文档的相关性得分,一旦它们的得分被计算出来,bool查询就将这些得分进行合并
并且返回一个代表整个布尔操作的得分,经典案例:
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }},
{ "range": { "date": { "gte": "2014-01-01" }}}
]
}
}
用于查找title字段匹配how to make millions
, 并且tag字段不为spam的文档,
如果tag字段等于starred或者date>=2014-01-01,将比另外那些文档拥有更高的排名,
如果should中两者都满足,他么它的排名将更高
注意:如果没有must语句,那么至少要匹配其中的一条should语句,但是如果存在至少一条must语句
则对should语句的匹配没有要求
- 还有一个注意事项,如果must_not和should都使用,那么两条查询语句都需要匹配
GET /us/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"tweet": "elasticsearch"
}
}
],
"should": [
{
"term": {
"date": "2014-09-24"
}
}
]
}
}
}
- 增加带过滤器filtering的查询
如果我们不想因为文档的date时间而影响得分,可以使用filter语句来重写前面的例子
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }}
],
"filter": {
"range": { "date": { "gte": "2014-01-01"}}
}
}
}
range查询已经从should语句中移动到filter语句中
通过将range查询从should语句中移动到了filter语句中,我们将它转成了不评分的查询,
将不在影响文档的相关性排名,由于它是一个不评分的查询,可以使用各种filter查询有效的优化手段来提升性能
所有的查询都可以借鉴这种方式,将查询移动到bool的filter语句中,那么他就自动转成了不评分的filter了
如果你需要通过多个不同的标准来过滤文档,bool查询本身亦可以用作不评分的查询,简单的将它放到filter语句内部
构建布尔逻辑
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }}
],
"filter": {
"bool": {
"must": [
{ "range": { "date": { "gte": "2014-01-01" }}},
{ "range": { "price": { "lte": 29.99 }}}
],
"must_not": [
{ "term": { "category": "ebooks" }}
]
}
}
}
}
将布尔查询包裹在filter语句中,我们可以在过滤标准中增加布尔逻辑
- 通过混合布尔查询,我们可以在我们的查询请求中灵活的编写scoring和filtering查询逻辑
案例:
GET /us/_search
查询tweet字段等于elasticsearch的文档(参与评分和匹配),
而且查询date字段不等2014-09-20的文档而且date字段大于I=2014-09-16的文档(参与匹配,不参与评分)
{
"query": {
"bool": {
"must": [
{
"match": {
"tweet": "elasticsearch"
}
}
],
"filter": {
"bool": {
"must_not": {
"term": {
"date": "2014-09-20"
}
},
"must": {
"range": {
"date": {
"gt": "2014-09-16"
}
}
}
}
}
}
}
}
- constant_score查询
它被经常用于你只执行一个filter而没有其它查询,可以使用它来取代只有filter语句的bool查询,
在性能上是完全相同的,但是对于查询简洁性和清晰度有很大帮助
{
"query": {
"constant_score": {
"filter": {
"terms": {
"date": ["2014-09-16", "2014-09-22"]
}
}
}
}
}
- constant_score可以代替只有filter的bool查询
验证查询
- 查询可以变得非常复杂,尤其和不同的分析器与不同的字段映射结合时,理解起来就有点困难了
不过validate-query API可以用来验证查询是否合法
GET /us/_validate/query
{
"query": {
"constant_score": {
"filter": {
"terms": {
"date": ["2014-09-16", "2014-09-22"]
}
}
}
}
}
如果合法,valid返回true, 否则返回false
- 理解错误信息
为了找出不合法的原因,可以将explain参数加到查询字符串中
GET /us/_validate/query?explain
{
"query": {
"constant_score": {
"filter": {
"must": {
"date": ["2014-09-16", "2014-09-22"]
}
}
}
}
}
返回错误原因,因为must应该写成match
{
"valid": false,
"error": "org.elasticsearch.common.ParsingException: Failed to parse; java.lang.IllegalStateException: Can't get text on a START_ARRAY at 6:29"
}
explain参数可以提供更多关于查询不合法的原因
- 理解查询语句
对于合法查询,explain将会返回可读的描述,这对准确理解es是如何解析你的query是非常有用的