如今咱们已经了解了基础知识,让咱们尝试更真实的数据集,我准备了一份关于客户银行帐户信息的虚构JSON文档样本,每一个文档都有如下模式:html
{ "account_number": 0, "balance": 16623, "firstname": "Bradshaw", "lastname": "Mckenzie", "age": 29, "gender": "F", "address": "244 Columbus Place", "employer": "Euron", "email": "bradshawmckenzie@euron.com", "city": "Hobucken", "state": "CO" }
奇怪的是,这些数据是使用www.json-generator.com/生成的,所以请忽略数据的实际值和语义,由于这些都是随机生成的。git
你能够从此处下载示例数据集(accounts.json),将它解压缩到咱们当前的目录,而后将它们加载到咱们的集群中,以下所示:github
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json" curl "localhost:9200/_cat/indices?v"
响应以下:json
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open bank l7sSYV2cQXmu6_4rJWVIww 5 1 1000 0 128.6kb 128.6kb
这意味着咱们只是成功地将1000个文档批量索引到bank
索引(在_doc
类型下)。segmentfault
如今让咱们从一些简单的搜索开始吧,运行搜索有两种基本方法:一种是经过REST请求URI发送搜索参数,另外一种是经过REST请求体发送它们。请求体方法容许你更具表现力,并以更易读的JSON格式定义搜索,咱们将尝试一个请求URI方法的示例,可是对于本教程的其他部分,咱们将专门使用请求体方法。数组
能够从_search
端点访问用于搜索的REST API,此示例返回bank索引中的全部文档:服务器
GET /bank/_search?q=*&sort=account_number:asc&pretty
让咱们首先剖析搜索调用,咱们在bank索引中搜索(_search
端点),q=*
参数指示Elasticsearch匹配索引中的全部文档。sort=account_number:asc
参数指示使用每一个文档的account_number
字段升序对结果进行排序,pretty
参数再次告诉Elasticsearch返回漂亮的JSON结果。网络
响应(部分显示):app
{ "took" : 63, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 1000, "max_score" : null, "hits" : [ { "_index" : "bank", "_type" : "_doc", "_id" : "0", "sort": [0], "_score" : null, "_source" : {"account_number":0,"balance":16623,"firstname":"Bradshaw","lastname":"Mckenzie","age":29,"gender":"F","address":"244 Columbus Place","employer":"Euron","email":"bradshawmckenzie@euron.com","city":"Hobucken","state":"CO"} }, { "_index" : "bank", "_type" : "_doc", "_id" : "1", "sort": [1], "_score" : null, "_source" : {"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"} }, ... ] } }
至于响应,咱们看如下部分:curl
took
— Elasticsearch执行搜索的时间(以毫秒为单位)timed_out
— 告诉咱们搜索是否超时_shards
— 告诉咱们搜索了多少个碎片,以及搜索成功/失败碎片的计数hits
— 搜索结果hits.total
— 符合咱们搜索条件的文档总数hits.hits
— 实际的搜索结果数组(默认为前10个文档)hits.sort
— 对结果进行排序(若是按分数排序则丢失)hits._score
和max_score
— 暂时忽略这些字段如下是使用替代请求体方法的上述彻底相同的搜索:
GET /bank/_search { "query": { "match_all": {} }, "sort": [ { "account_number": "asc" } ] }
这里的区别在于,咱们不是在URI中传递q=*
,而是向_search
API提供JSON样式的查询请求体,咱们将在下一节讨论这个JSON查询。
重要的是要理解,一旦你得到了搜索结果,Elasticsearch就彻底完成了请求,而且不会在结果中维护任何类型的服务器端资源或打开游标,这与SQL等许多其余平台造成鲜明对比,其中你可能最初预先得到查询结果的部分子集,而后若是要获取(或翻页)其他的则必须连续返回服务器使用某种有状态服务器端游标的结果。
Elasticsearch提供了一种JSON样式的特定于域的语言,可用于执行查询,这被称为Query DSL,查询语言很是全面,乍一看可能使人生畏,但实际学习它的最佳方法是从一些基本示例开始。
回到上一个例子,咱们执行了这个查询:
GET /bank/_search { "query": { "match_all": {} } }
解析上面的内容,query
部分告诉咱们查询定义是什么,match_all
部分只是咱们想要运行的查询类型,match_all
查询只是搜索指定索引中的全部文档。
除了query
参数,咱们还能够传递其余参数来影响搜索结果,在上面的部分示例中,咱们传递了sort
,这里咱们传入size
:
GET /bank/_search { "query": { "match_all": {} }, "size": 1 }
请注意,若是未指定size
,则默认为10。
此示例执行match_all
并返回文档10到19:
GET /bank/_search { "query": { "match_all": {} }, "from": 10, "size": 10 }
from
参数(从0开始)指定从哪一个文档索引开始,size
参数指定从from
参数开始返回的文档数,在实现搜索结果的分页时,此功能很是有用,请注意,若是未指定from
,则默认为0。
此示例执行match_all
并按账户balance降序对结果进行排序,并返回前10个(默认大小)文档。
GET /bank/_search { "query": { "match_all": {} }, "sort": { "balance": { "order": "desc" } } }
如今咱们已经看到了一些基本的搜索参数,让咱们再深刻研究一下Query DSL,咱们先来看一下返回的文档字段。默认状况下,完整的JSON文档做为全部搜索的一部分返回,这被称为源(搜索命中中的_source
字段),若是咱们不但愿返回整个源文档,咱们能够只请求返回源中的几个字段。
此示例显示如何从搜索中返回两个字段,account_number
和balance
(在_source
内部):
GET /bank/_search { "query": { "match_all": {} }, "_source": ["account_number", "balance"] }
请注意,上面的示例只是简化了_source
字段,它仍然只返回一个名为_source
的字段,但在其中只包含字段account_number
和balance
。
若是你有来自SQL背景,则上述内容在概念上与SQL SELECT FROM
字段列表有些类似。
如今让咱们转到查询部分,之前,咱们已经看到match_all
查询如何用于匹配全部文档。如今让咱们介绍一种称为match query的新查询,它能够被认为是基本的字段搜索查询(即针对特定字段或字段集进行的搜索)。
此示例返回帐户数为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" } } }
此示例是match
(match_phrase
)的变体,它返回地址中包含短语“mill lane”的全部账户:
GET /bank/_search { "query": { "match_phrase": { "address": "mill lane" } } }
咱们如今介绍bool query,bool
查询容许咱们使用布尔逻辑将较小的查询组成更大的查询。
此示例组成两个match
查询,并返回地址中包含“mill”和“lane”的全部账户:
GET /bank/_search { "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
在上面的示例中,bool must
子句指定全部必须为true的查询才能将文档视为匹配项。
相反,此示例组成两个match
查询并返回地址中包含“mill”或“lane”的全部账户:
GET /bank/_search { "query": { "bool": { "should": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
在上面的示例中,bool should
子句指定了一个查询列表,其中的任何一个为true则将文档视为匹配项。
此示例组成两个匹配查询,并返回地址中既不包含“mill”也不包含“lane”的全部账户:
GET /bank/_search { "query": { "bool": { "must_not": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
在上面的示例中,bool must_not
子句指定了一个查询列表,对于文档而言,这些查询都不能为true。
咱们能够在bool
查询中同时组合must
,should
和must_not
子句,此外,咱们能够在任何这些bool
子句中组合bool
查询来模仿任何复杂的多级布尔逻辑。
此示例返回任何40岁但不住在ID(aho)的人的全部账户:
GET /bank/_search { "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } } }
在上一节中,咱们跳过了一个称为文档分数的小细节(搜索结果中的_score
字段),分数是一个数值,它是文档与咱们指定的搜索查询匹配程度的相对度量,分数越高,文档越相关,分数越低,文档的相关性越低。
可是查询并不老是须要产生分数,特别是当它们仅用于“过滤”文档集时,Elasticsearch会检测这些状况并自动优化查询执行,以便不计算无用的分数。
咱们在上一节中介绍的bool query还支持filter
子句,这些子句容许使用查询来限制将与其余子句匹配的文档,而不会更改计算得分的方式。做为一个例子,让咱们介绍range query,它容许咱们按一系列值过滤文档,这一般用于数字或日期过滤。
此示例使用bool查询返回全部余额介于20000和30000之间的账户,换句话说,咱们但愿找到余额大于或等于20000且小于或等于30000的账户。
GET /bank/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "balance": { "gte": 20000, "lte": 30000 } } } } } }
解析上面的内容,bool查询包含match_all
查询(查询部分)和range
查询(过滤部分),咱们能够将任何其余查询替换为查询和过滤部分,在上述状况下,范围查询很是有意义,由于落入范围的文档都“相同”匹配,即,没有文档比另外一文档更相关。
除了match_all
,match
,bool
和range
查询以外,还有不少其余可用的查询类型,咱们不会在这里讨论它们,因为咱们已经基本了解它们的工做原理,所以将这些知识应用于学习和试验其余查询类型应该不会太困难。
聚合提供了从数据中分组和提取统计信息的能力,考虑聚合的最简单方法是将其大体等同于SQL GROUP BY和SQL聚合函数。在Elasticsearch中,你能够执行返回匹配的搜索,同时在一个响应中返回与命中相关的聚合结果。这是很是强大和高效的,由于你能够运行查询和多个聚合,并一次性获取两个(或任一)操做的结果,避免使用简洁和简化的API进行网络往返。
首先,此示例按state对全部账户进行分组,而后返回前10个(默认)state,按计数降序排序(也是默认值):
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 LIMIT 10;
响应(部分显示):
{ "took": 29, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped" : 0, "failed": 0 }, "hits" : { "total" : 1000, "max_score" : 0.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 }, { "key" : "TN", "doc_count" : 23 }, { "key" : "MA", "doc_count" : 21 }, { "key" : "NC", "doc_count" : 21 }, { "key" : "ND", "doc_count" : 21 }, { "key" : "ME", "doc_count" : 20 }, { "key" : "MO", "doc_count" : 20 } ] } } }
咱们能够看到ID
(Idaho)有27个帐户,其次是TX
(Texas)的27个帐户,其次是AL
(Alabama)的25个帐户,依此类推。
请注意,咱们将size=0
设置为不显示搜索命中,由于咱们只想在响应中看到聚合结果。
在前一个聚合的基础上,此示例按state计算平均账户余额(一样仅针对按降序排序的前10个state):
GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } }
请注意咱们如何嵌套group_by_state
聚合中的average_balance
聚合,这是全部聚合的常见模式,你能够在聚合中任意嵌套聚合,以从数据中提取所需的轮转摘要。
在前一个聚合的基础上,咱们如今按降序排列平均余额:
GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword", "order": { "average_balance": "desc" } }, "aggs": { "average_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" } } } } } } } }
还有许多其余聚合功能,咱们在此再也不详述,若是你想进行进一步的实验,聚合参考指南是一个很好的起点。