入门(6)检索你的数据
测试数据
我们已经掌握了一些基础知识,现在我们来操作一些更真实的数据-客户银行账户数据。点击这里获取json,并利用前面的知识插入到es中!
POST bank/account/_bulk?pretty
{"index":{"_id":"1"}}
{"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}
{"index":{"_id":"6"}}
{"account_number":6,"balance":5686,"firstname":"Hattie","lastname":"Bond","age":36,"gender":"M","address":"671 Bristol Street","employer":"Netagy","email":"hattiebond@netagy.com","city":"Dante","state":"TN"}
...
Search API
搜索有两种基本的方法:
- 一种是通过REST请求URI发送搜索参数,。
- 一种是通过REST请求体发送参数。请求体方法更具表现力,并且还可以以更可读的JSON格式定义搜索。 我们举一个请求URI方法的示例,但是在本教程的其余部分中,我们使用请求体方法。
**用于搜索的REST API以_search开头。此示例返回银行索引中的10条文档: **
GET bank/_search?q=*&sort=account_number:asc&pretty
我们来看一下搜索语句。我们在bank index中搜索(_search ):
- q = *参数表示Elasticsearch匹配索引中的所有文档。
- sort = account_number:asc参数表示使用每个文档的account_number字段按升序对结果进行排序。 漂亮的参数再次告诉Elasticsearch返回漂亮打印的JSON结果。 之所以返回10条不是所有是因为有个size参数,在不指定的情况下默认为10。 如果指定的话,语句这样写
GET bank/_search?q=*&sort=account_number:asc&size=1000&pretty
响应: ![](http:// orma7vo6f.bkt.clouddn.com/wiki/ElasticSearch5.4官方文档-中文翻译/_image/2017-06-16-17-22-45.jpg)
我们来看一下上面响应中的参数:
took
-Elasticsearch执行搜索的时间(毫秒)timed_out
- 搜索是否超时_shards
-搜索了多少个分片,以及成功/失败的搜索分片数hits
- 搜索结果hits.total
- 符合搜索条件的文件总数hits.hits
- 搜索结果的实际数组(默认为前10个文档)hits.sort
- 结果的排序键(如果按分数排序,则丢失)hits._score
和max_score
- 现在忽略这些字段
下面是使用请求体方法做与上面效果相同的搜索:
GET bank/_search
{
"query": { "match_all": {} },
"size": 1000,
"sort": [
{ "account_number": "asc" }
]
}
这里的不同之处在于,我们不会在URI中传递q = *,而是向_search API发布一个JSON样式的查询请求体。我们将在下一节中讨论这个JSON查询。
重要的是要明白,一旦你获得了搜索结果,Elasticsearch就完成了这个请求,并且不会保留任何类型的服务器端资源或打开游标到你的结果中。这与许多其他平台(如SQL)形成鲜明对比,您可能最初会先获得查询结果的部分子集,然后如果要获取(或浏览)其余部分,则必须不断返回到服务器的结果使用某种有状态的服务器端游标。 上面这句话尽量理解吧!
介绍查询语言(Introducing the Query Language)
Elasticsearch提供了一种可用于执行查询的JSON风格的的语言
。这被称为查询DSL
。查询语言是相当全面的,乍看起来可能很吓人,我们先来看几个例子!
回到我们的最后一个例子,我们执行了这个查询:
GET bank/_search
{
"query": { "match_all": {} },
"size": 1000,
"sort": [
{ "account_number": "asc" }
]
}
解析上面的内容: query
部分告诉我们,我们的查询定义是什么。 match_all
部分只是我们要运行的查询类型。 match_all查询只是搜索指定索引中的所有文档。 除查询参数外,我们还可以传递其他参数来影响搜索结果。在上面的例子中我们通过排序
(sort),大小
(size)!
再次提醒,如果未指定大小,则默认为10。
此示例执行一个match_all并返回文档11到20:
GET bank/_search
{
"query":{
"match_all": {}
},
"from": 0,
"size": 10
}
from参数(基于0)指定要从哪个文档索引开始, size参数指定从from参数开始返回多少个文档。执行搜索结果分页时,此功能非常有用。请注意,如果未指定from,则默认为0。
此示例执行match_all,并按结果按降序排列帐户余额,并返回前10名(默认大小)文档。
GET bank/_search
{
"query":{
"match_all": {}
},
"sort": {
"balance":{"order":"desc"}
},
"from": 0,
"size": 10
}
执行搜索(Executing Searches)
现在我们已经看到了一些基本的搜索参数,我们来进一步探讨Query DSL。我们先来看看返回的文档字段。默认情况下,完整的JSON文档作为搜索结果
的一部分返回。这被称为源
(搜索结果中的_source字段)。如果我们不希望返回整个源文档,我们可以指定返回的字段。
此示例显示如何从指定搜索字段:
GET bank/_search
{
"query":{"match_all": {}},
"_source":["account_number","balance"]
}
如果您有SQL背景,上述内容在概念上与SQL SELECT FROM字段列表有些相似。
现在看查询部分。前面我们已经看到了如何使用match_all查询来匹配所有文档。 现在我们来介绍一个称为匹配查询
的新查询,它是基本的字段搜索查询(即针对特定字段或一组字段进行搜索)。
此示例返回的帐号为20:
GET bank/_search
{
"query": { "match": { "account_number": 20 } }
}
下面示例返回在地址中包含“mill”的所有帐户:
GET bank/_search
{
"query": {
"match": {
"address": "mill"
}
}
}
此示例返回在地址中包含术语“mill”或“lane”的所有帐户:
GET bank/_search
{
"query": {
"match": {
"address": "mill lane"
}
}
}
此示例返回所有包含地址中包含“mill lane”短语的帐户:
GET /bank/_search
{
"query": { "match_phrase": { "address": "mill lane" } }
}
bool查询
现在介绍一下bool(ean)
查询。 bool查询允许我们使用布尔逻辑将更小的查询组合成较大的查询。(其实就是条件表达式)
** 查询地址
中包含“mill”和
“lane”的所有帐户: **
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address": "mill"
}
},
{
"match": {
"address": "lane"
}
}
]
}
}
}
查询地址中包含“mill”或
“lane”的所有帐户:
GET /bank/_search
{
"query": {
"bool": {
"should": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
查询地址中既不包含“mill”也不包含“lane”的所有帐户:
GET /bank/_search
{
"query": {
"bool": {
"must_not": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
我们可以在一个bool查询中,同时结合使用must,should和must_not子句。此外,我们可以在任何这些bool子句中组合bool查询,以实现任何复杂的多级boolean逻辑。
查询所有age为40
但是state不为id
的帐户:
GET /bank/_search
{
"query": {
"bool": {
"must": [
{ "match": { "age": "40" } }
],
"must_not": [
{ "match": { "state": "ID" } }
]
}
}
}
执行过滤(Filter)
在上一节中,我们跳过了一些名为文档分数的细节(搜索结果中的_score字段)。分数是一个数字值,它是文档
与我们指定的搜索查询匹配
的相对度量
。分数越高,文档越相关,分数越低,文档的相关性越低。
但查询并不总是需要生成分数,特别是当它们仅用于“过滤”文档集时。Elasticsearch检测这些情况和自动优化查询执行为了不计算无用的分数。
bool查询也支持filter
条件,它不改变计算分数。例如,我们来介绍一下范围查询,它允许我们通过一系列值过滤文档。通常用于数字或日期过滤。
查询余额(balance)在20000和30000之间的所有账户。换句话说,我们希望找到大于等于20000,小于等于30000的账户。
GET /bank/_search
{
"query": {
"bool": {
"must": { "match_all": {} },
"filter": {
"range": {
"balance": {
"gte": 20000,
"lte": 30000
}
}
}
}
}
}
在上面的例子中,布尔(bool)查询包含match_all查询(查询的一部分)和范围(range)查询(过滤[filter]部分)。在上面的例子中,使用范围查询是完全合理的,因为没有一个文档比另一个文档相关性更高。
除了match_all
,match
,bool
和range
查询之外,还有很多其他可用的查询类型,我们不会在这里查看。既然我们已经对它们有了基本的了解,将这些知识应用于学习和实验其他查询类型并不难。
执行聚合(Executing Aggregations)
聚合(Aggregations )
提供:对数据中分组
和提取统计信息
的功能。 理解聚合的最简单的方法是将它大致看作SQL GROUP BY和SQL聚合函数。在Elasticsearch中,你可以在一次查询中执行多个聚合,他们都会分别在返回结果中展示,这样的效率是比较高的。
此示例按状态(state)分组,然后返回按count降序排序的前10个(默认):
GET bank/_search
{
"size": 0
, "aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword"
}
}
}
}
在SQL中,上述聚合在概念上类似于:
SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC
响应(部分显示):
{
"took": 281,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 999,
"max_score": 0,
"hits": []
},
"aggregations": {
"group_by_state": {
"doc_count_error_upper_bound": 20,
"sum_other_doc_count": 770,
"buckets": [
{
"key": "ID",
"doc_count": 27
},
{
"key": "TX",
"doc_count": 27
},
{
"key": "AL",
"doc_count": 25
},
{
"key": "MD",
"doc_count": 25
}
]
}
}
}
我们可以看到,ID(爱达荷州州)有27个帐户,其次是TX(德克萨斯州)的27个帐户,其次是AL(阿拉巴马州)25个帐户,等等。
请注意,我们将size = 0,设置为不显示搜索结果(只显示聚合结果),因为我们只想在响应中看到聚合结果。
在前面的聚合基础上,本例通过状态计算平均帐户余额(仅用于按降序排序的前10个状态):
GET bank/_search
{
"size": 0
, "aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword"
}
, "aggs": {
"avg_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
注意我们如何嵌套group_by_state聚合中的average_balance聚合。这是所有聚合的常见模式。您可以任意嵌套聚合内的聚合。
基于上一个汇总,我们现在按照降序对平均余额进行排序:
GET bank/_search
{
"size": 0
, "aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword",
"order": {
"avg_balance": "desc"
}
}
, "aggs": {
"avg_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
这个例子演示了先按年龄段(20-29岁,30-39岁和40-49岁)分组,再按按性别分组,最终得到每个年龄段的平均账户余额:
GET /bank/_search
{
"size": 0,
"aggs": {
"group_by_age": {
"range": {
"field": "age",
"ranges": [
{
"from": 20,
"to": 30
},
{
"from": 30,
"to": 40
},
{
"from": 40,
"to": 50
}
]
},
"aggs": {
"group_by_gender": {
"terms": {
"field": "gender.keyword"
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
}
}
还有许多其他聚合功能,我们将不再详细介绍。如果您想进一步探索,点击:聚合参考指南