请求体查询
简易 查询 —query-string search— 对于用命令行进行即席查询(ad-hoc)是非常有用的。 然而,为了充分利用查询的强大功能,你应该使用 请求体 search
API, 之所以称之为请求体查询(Full-Body Search),因为大部分参数是通过 Http 请求体而非查询字符串来传递的。
请求体查询 —下文简称 查询—不仅可以处理自身的查询请求,还允许你对结果进行片段强调(高亮)、对所有或部分结果进行聚合分析,同时还可以给出 你是不是想找 的建议,这些建议可以引导使用者快速找到他想要的结果。
空查询
让我们以 最简单的 search
API 的形式开启我们的旅程,空查询将返回所有索引库(indices)中的所有文档:
GET /_search
{}
这是一个空的请求体。
只用一个查询字符串,你就可以在一个、多个或者 _all
索引库(indices)和一个、多个或者所有types中查询:
GET /index_2014*/type1,type2/_search
{}
同时你可以使用 from
和 size
参数来分页:
GET /_search
{
"from": 30,
"size": 10
}
一个带请求体的 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
}类似的规则可以应用于任何需要带请求体的
GET
API。
我们将在聚合 聚合 章节深入介绍聚合(aggregations),而现在,我们将聚焦在查询。
相对于使用晦涩难懂的查询字符串的方式,一个带请求体的查询允许我们使用 查询领域特定语言(query domain-specific language) 或者 Query DSL 来写查询语句。
查询表达式
查询表达式(Query DSL)是一种非常灵活又富有表现力的 查询语言。 Elasticsearch 使用它可以以简单的 JSON 接口来展现 Lucene 功能的绝大部分。在你的应用中,你应该用它来编写你的查询语句。它可以使你的查询语句更灵活、更精确、易读和易调试。
要使用这种查询表达式,只需将查询语句传递给 query
参数:
GET /_search
{
"query": YOUR_QUERY_HERE
}
空查询(empty search) —{}
— 在功能上等价于使用 match_all
查询, 正如其名字一样,匹配所有文档:
GET /_search
{
"query": {
"match_all": {}
}
}
查询语句的结构
一个查询语句 的典型结构:
{
QUERY_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
如果是针对某个字段,那么它的结构如下:
{
QUERY_NAME: {
FIELD_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
}
举个例子,你可以使用 match
查询语句 来查询 tweet
字段中包含 elasticsearch
的 tweet:
{
"match": {
"tweet": "elasticsearch"
}
}或
{
"match": {
"tweet": {
"query": "elasticsearch"
}
}
}
完整的查询请求如下:
GET /_search
{
"query": {
"match": {
"tweet": "elasticsearch"
}
}
}或
{
"query": {
"match": {
"tweet": {
"query": "elasticsearch"
}
}
}
}
合并查询语句
查询语句(Query clauses) 就像一些简单的组合块 ,这些组合块可以彼此之间合并组成更复杂的查询。这些语句可以是如下形式:
-
叶子语句(Leaf clauses) (就像
match
语句) 被用于将查询字符串和一个字段(或者多个字段)对比。 -
复合(Compound) 语句 主要用于 合并其它查询语句。 比如,一个
bool
语句 允许在你需要的时候组合其它语句,无论是must
匹配、must_not
匹配还是should
匹配,同时它可以包含不评分的过滤器(filters):
{
"bool": {
"must": { "match": { "tweet": "elasticsearch" }},
"must_not": { "match": { "name": "mary" }},
"should": { "match": { "tweet": "full text" }},
"filter": { "range": { "age" : { "gt" : 30 }} }
}
}
一条复合语句可以合并 任何 其它查询语句,包括复合语句,了解这一点是很重要的。这就意味着,复合语句之间可以互相嵌套,可以表达非常复杂的逻辑。
例如,以下查询是为了找出信件正文包含 business opportunity
的星标邮件,或者在收件箱正文包含business opportunity
的非垃圾邮件:
{
"bool": {
"must": { "match": { "email": "business opportunity" }},
"should": [
{ "match": { "starred": true }},
{ "bool": {
"must": { "match": { "folder": "inbox" }},
"must_not": { "match": { "spam": true }}
}}
],
"minimum_should_match": 1
}
}
到目前为止,你不必太在意这个例子的细节,我们会在后面详细解释。最重要的是你要理解到,一条复合语句可以将多条语句 — 叶子语句和其它复合语句 — 合并成一个单一的查询语句。
查询与过滤
Elasticsearch 使用的查询语言(DSL) 拥有一套查询组件,这些组件可以以无限组合的方式进行搭配。这套组件可以在以下两种情况下使用:过滤情况(filtering context)和查询情况(query context)。
当使用于 过滤情况 时,查询被设置成一个“不评分”或者“过滤”查询。即,这个查询只是简单的问一个问题:“这篇文档是否匹配?”。回答也是非常的简单,yes 或者 no ,二者必居其一。
-
created
时间是否在2013
与2014
这个区间? -
status
字段是否包含published
这个单词? -
lat_lon
字段表示的位置是否在指定点的10km
范围内?
当使用于 查询情况 时,查询就变成了一个“评分”的查询。和不评分的查询类似,也要去判断这个文档是否匹配,同时它还需要判断这个文档匹配的有 _多好_(匹配程度如何)。 此查询的典型用法是用于查找以下文档:
- 查找与
full text search
这个词语最佳匹配的文档 - 包含
run
这个词,也能匹配runs
、running
、jog
或者sprint
- 包含
quick
、brown
和fox
这几个词 — 词之间离的越近,文档相关性越高 - 标有
lucene
、search
或者java
标签 — 标签越多,相关性越高
一个评分查询计算每一个文档与此查询的 _相关程度_,同时将这个相关程度分配给表示相关性的字段 `_score`,并且按照相关性对匹配到的文档进行排序。这种相关性的概念是非常适合全文搜索的情况,因为全文搜索几乎没有完全 “正确” 的答案。
自 Elasticsearch 问世以来,查询与过滤(queries and filters)就独自成为 Elasticsearch 的组件。但从 Elasticsearch 2.0 开始,过滤(filters)已经从技术上被排除了,同时所有的查询(queries)拥有变成不评分查询的能力。
然而,为了明确和简单,我们用 "filter" 这个词表示不评分、只过滤情况下的查询。你可以把 "filter" 、 "filtering query" 和 "non-scoring query" 这几个词视为相同的。
相似的,如果单独地不加任何修饰词地使用 "query" 这个词,我们指的是 "scoring query" 。
性能差异
过滤查询(Filtering queries)只是简单的检查包含或者排除,这就使得计算起来非常快。考虑到至少有一个过滤查询(filtering query)的结果是 “稀少的”(很少匹配的文档),并且经常使用不评分查询(non-scoring queries),结果会被缓存到内存中以便快速读取,所以有各种各样的手段来优化查询结果。
相反,评分查询(scoring queries)不仅仅要找出 匹配的文档,还要计算每个匹配文档的相关性,计算相关性使得它们比不评分查询费力的多。同时,查询结果并不缓存。
多亏倒排索引(inverted index),一个简单的评分查询在匹配少量文档时可能与一个涵盖百万文档的filter表现的一样好,甚至会更好。但是在一般情况下,一个filter 会比一个评分的query性能更优异,并且每次都表现的很稳定。
过滤(filtering)的目标是减少那些需要通过评分查询(scoring queries)进行检查的文档。
如何选择查询与过滤
通常的规则是,使用 查询(query)语句来进行 全文 搜索或者其它任何需要影响 相关性得分 的搜索。除此以外的情况都使用过滤(filters)。
最重要的查询
虽然 Elasticsearch 自带了很多的查询,但经常用到的也就那么几个。我们将在 深入搜索 章节详细讨论那些查询的细节,接下来我们对最重要的几个查询进行简单介绍。
match_all 查询
match_all
查询简单的 匹配所有文档。在没有指定查询方式时,它是默认的查询:
{ "match_all": {}}
它经常与 filter 结合使用--例如,检索收件箱里的所有邮件。所有邮件被认为具有相同的相关性,所以都将获得分值为 1
的中性 `_score`。
match 查询
无论你在任何字段上进行的是全文搜索还是精确查询,match
查询是你可用的标准查询。
如果你在一个全文字段上使用 match
查询,在执行查询前,它将用正确的分析器去分析查询字符串:
{ "match": { "tweet": "About Search" }}
如果在一个精确值的字段上使用它, 例如数字、日期、布尔或者一个 not_analyzed
字符串字段,那么它将会精确匹配给定的值:
{ "match": { "age": 26 }}
{ "match": { "date": "2014-09-01" }}
{ "match": { "public": true }}
{ "match": { "tag": "full_text" }}
对于精确值的查询,你可能需要使用 filter 语句来取代 query,因为 filter 将会被缓存。接下来,我们将看到一些关于 filter 的例子。
不像我们在 轻量 搜索 章节介绍的字符串查询(query-string search), match
查询不使用类似 +user_id:2 +tweet:search
的查询语法。它只是去查找给定的单词。这就意味着将查询字段暴露给你的用户是安全的;你需要控制那些允许被查询字段,不易于抛出语法异常。
multi_match 查询
multi_match
查询可以在多个字段上执行相同的 match
查询:
{
"multi_match": {
"query": "full text search",
"fields": [ "title", "body" ]
}
}
range 查询
range
查询找出那些落在指定区间内的数字或者时间:
{
"range": {
"age": {
"gte": 20,
"lt": 30
}
}
}
被允许的操作符如下:
gt
大于
gte
大于等于
lt
小于
lte
小于等于
term 查询
term
查询被用于精确值 匹配,这些精确值可能是数字、时间、布尔或者那些 not_analyzed
的字符串:
{ "term": { "age": 26 }}
{ "term": { "date": "2014-09-01" }}
{ "term": { "public": true }}
{ "term": { "tag": "full_text" }}
term
查询对于输入的文本不 分析 ,所以它将给定的值进行精确查询。
terms 查询
terms
查询和 term
查询一样,但它允许你指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件:
{ "terms": { "tag": [ "search", "full_text", "nosql" ] }}
和 term
查询一样,terms
查询对于输入的文本不分析。它查询那些精确匹配的值(包括在大小写、重音、空格等方面的差异)。
exists 查询和 missing 查询
exists
查询和 missing
查询被用于查找那些指定字段中有值 (exists
) 或无值 (missing
) 的文档。这与SQL中的 IS_NULL
(missing
) 和 NOT IS_NULL
(exists
) 在本质上具有共性:
{
"exists": {
"field": "title"
}
}
这些查询经常用于某个字段有值的情况和某个字段缺值的情况。
组合多查询
现实的查询需求从来都没有那么简单;它们需要在多个字段上查询多种多样的文本,并且根据一系列的标准来过滤。为了构建类似的高级查询,你需要一种能够将多查询组合成单一查询的查询方法。
你可以用 bool
查询来实现你的需求。这种查询将多查询组合在一起,成为用户自己想要的布尔查询。它接收以下参数:
must
文档 必须 匹配这些条件才能被包含进来。
must_not
文档 必须不 匹配这些条件才能被包含进来。
should
如果满足这些语句中的任意语句,将增加 _score
,否则,无任何影响。它们主要用于修正每个文档的相关性得分。
filter
必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。
由于这是我们看到的第一个包含多个查询的查询,所以有必要讨论一下相关性得分是如何组合的。每一个子查询都独自地计算文档的相关性得分。一旦他们的得分被计算出来, bool
查询就将这些得分进行合并并且返回一个代表整个布尔操作的得分。
下面的查询用于查找 title
字段匹配 how to make millions
并且不被标识为 spam
的文档。那些被标识为 starred
或在2014之后的文档,将比另外那些文档拥有更高的排名。如果 _两者_ 都满足,那么它排名将更高:
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }},
{ "range": { "date": { "gte": "2014-01-01" }}}
]
}
}
如果没有
must
语句,那么至少需要能够匹配其中的一条should
语句。但,如果存在至少一条must
语句,则对should
语句的匹配没有要求。
增加带过滤器(filtering)的查询
如果我们不想因为文档的时间而影响得分,可以用 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 查询移到 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" }}
]
}
}
}
}
将
bool
查询包裹在filter
语句中,我们可以在过滤标准中增加布尔逻辑
通过混合布尔查询,我们可以在我们的查询请求中灵活地编写 scoring 和 filtering 查询逻辑。
constant_score 查询
尽管没有 bool
查询使用这么频繁,constant_score
查询也是你工具箱里有用的查询工具。它将一个不变的常量评分应用于所有匹配的文档。它被经常用于你只需要执行一个 filter 而没有其它查询(例如,评分查询)的情况下。
可以使用它来取代只有 filter 语句的 bool
查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助。
{
"constant_score": {
"filter": {
"term": { "category": "ebooks" }
}
}
}
term
查询被放置在 constant_score
中,转成不评分的 filter。这种方式可以用来取代只有 filter 语句的 bool
查询。
验证查询
查询可以变得非常的复杂,尤其 和不同的分析器与不同的字段映射结合时,理解起来就有点困难了。不过 validate-query
API 可以用来验证查询是否合法。
GET /gb/tweet/_validate/query
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
以上 validate
请求的应答告诉我们这个查询是不合法的:
{
"valid" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
}
}
理解错误信息
为了找出 查询不合法的原因,可以将 explain
参数 加到查询字符串中:
GET /gb/tweet/_validate/query?explain
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
explain
参数可以提供更多关于查询不合法的信息。
很明显,我们将查询类型(match
)与字段名称 (tweet
)搞混了:
{
"valid" : false,
"_shards" : { ... },
"explanations" : [ {
"index" : "gb",
"valid" : false,
"error" : "org.elasticsearch.index.query.QueryParsingException:
[gb] No query registered for [tweet]"
} ]
}
理解查询语句
对于合法查询,使用 explain
参数将返回可读的描述,这对准确理解 Elasticsearch 是如何解析你的 query 是非常有用的:
GET /_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
分析器。
elasticsearch 基础 —— 请求体查询的更多相关文章
-
第2部分 Elasticsearch查询-请求体查询、排序
一.请求体查询 请求体 search API, 之所以称之为请求体查询(Full-Body Search),因为大部分参数是通过http请求体而非查询字符串来传递的. 请求体查询:不仅可以处理自身的查 ...
-
elasticsearch(5) 请求体搜索
上一篇提到的轻量搜索非常简单便捷,但是通过请求体查询可以更充分的利用查询的强大功能.因为_search api中大部分参数是通过HTTP请求体而非查询字符串来传递的. 一 空查询 对于空查询来说,最简 ...
-
elasticsearch 请求体查询方式整理
空查询(empty search) —{}— 在功能上等价于使用 match_all 查询, 正如其名字一样,匹配所有文档: GET /_search { "query": { & ...
-
ElasticSearch(6)-结构化查询
引用:ElasticSearch权威指南 一.请求体查询 请求体查询 简单查询语句(lite)是一种有效的命令行_adhoc_查询.但是,如果你想要善用搜索,你必须使用请求体查询(request bo ...
-
第1部分 Elasticsearch基础
一.安装 es端口:9200 kibana端口:5601 brew install elasticsearch brew install elasticsearch brew services sta ...
-
Flask中获取参数(路径,查询,请求体,请求头)
上一篇中已经讲述了:HTTP协议向服务器传参有几种途径{ 链接 } 在Flask中同样通过这4中传参途径进行归纳: 1. URL中路径参数的获取: 拓展: # 路由参数/路径参数:http://127 ...
-
Django中获取参数(路径,查询,请求头,请求体)
一.通常HTTP协议向服务器传参有几种途径 : 提取URL的特定部分,如/weather/shanghai/2018,可以在服务器端的路由中用正则表达式截取: 查询字符串(query string), ...
-
最完整的Elasticsearch 基础教程
翻译:潘飞(tinylambda@gmail.com) 基础概念 Elasticsearch有几个核心概念.从一开始理解这些概念会对整个学习过程有莫大的帮助. 接近实时(NRT) Ela ...
-
Elasticsearch基础教程
Reference: http://blog.csdn.net/cnweike/article/details/33736429 基础概念 Elasticsearch有几个核心概念.从一开始理解这些概 ...
随机推荐
-
Mysql 学习之基础操作
一.表复制 1.复制表结构 将表hello的结构复制一份为表hello3 2.复制数据 a.如果两张表的结构一样且你要复制所有列的数据 mysql> insert into hello3 ...
-
Unity arm64
ERROR ITMS-90086 ERROR ITMS-90086:"missing 64-bit support. beginning on february 1, 2015, new i ...
-
oracle sql developer数据表注释为乱码问题---解决
参考网址:http://blog.163.com/jackie_howe/blog/static/19949134720121126879265/ 内容: 在windows中创建一个名为“NLS_LA ...
-
Python实现DBScan
Python实现DBScan 运行环境 Pyhton3 numpy(科学计算包) matplotlib(画图所需,不画图可不必) 计算过程 st=>start: 开始 e=>end: 结束 ...
-
[React Native] Passing data when changing routes
The way you make HTTP requests in React Native is with the Fetch API. In this video we'll talk about ...
-
sed简单实例练习
sedfile内容如下: Steve Blenheim:238-923-7366:95 Latham Lane, Easton, PA 83755:11/12/56:20300 Betty Boop: ...
-
第4章 令牌端点(Token Endpoint) - IdentityModel 中文文档(v1.0.0)
令牌端点的客户端库(OAuth 2.0和OpenID Connect)作为HttpClient一组扩展方法提供.这允许HttpClient以您喜欢的方式创建和管理生命周期- 例如静态或通过像Micro ...
-
自定义JDBC链接池
上篇简单介绍了jdbc链接数据库: 本篇就说一下自定义连接池以及增删改查的测试: 自定义连接池 自定义链接池的原因 JDBC连接中用到Connection 在每次对数据进行增删查改 都要 开启 ...
-
学习Java的进度
这周我们通过老师的讲解带着我们回到了第八周的知识点.lambda表达式也是一种简化程序的好方法,通过回调程序的测试可以对比出lambda 表达式少的不是一两行代码,可以少了类中方法的定义,直接使用.内 ...
-
webservice 项目中遇到的问题
redshift database 连接异常 解决方案 url 修改添加参数如下 jdbc:redshift://hostname:5439/dbname?ssl=true&sslfactor ...