既然咱们已经了解了基础知识,让咱们来尝试操做一些更真实的数据集。我已经预先准备好了一些虚拟的顾客银行帐户信息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/account/_bulk?pretty&refresh' --data-binary "@accounts.json" curl 'localhost:9200/_cat/indices?v'
返回结果为:数据库
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索引中(在account类型下)。json
如今,让咱们从一些简单的搜索指令开始。执行搜索有两种基础的方式,一种是在请求的URL中加入参数来实现,另外一种方式是将请求内容放到请求体中。使用请求体可让你的JSON数据以一种更加可读和更加富有展示力的方式发送。咱们将会在一开始演示一次使用请求URI的方式,而后在本教程剩余的部分,咱们将统一使用请求体的方式发送。api
REST API可使用_search
端点来实现搜索。以下的示例将返回bank索引的全部的文档:数组
HTTP请求:服务器
GET /bank/_search?q=*&sort=account_number:asc&pretty
Curl命令:网络
curl -XGET 'localhost:9200/bank/_search?q=*&sort=account_number:asc&pretty&pretty'
Kibana Console:app
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_the_search_api/1.json
让咱们首先来详细分析一下这个搜索请求。这个请求在bank索引中进行搜索(使用 _search
端点),而后 q=*
参数命令Elasticsearch匹配索引中的所有文档。sort=account_number:asc
参数表示按 account_number
属性升序排列返回的结果。pretty
参数以前已经提到过,就是将返回结果以美观的格式返回。
返回结果为(展现部分):
{ "took" : 63, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 1000, "max_score" : null, "hits" : [ { "_index" : "bank", "_type" : "account", "_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" : "account", "_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"} }, ... ] } }
关于返回结果,咱们看到了以下的部分:
took
- Elasticsearch执行这次搜索所用的时间(单位:毫秒)timed_out
- 告诉咱们这次搜索是否超时_shards
- 告诉咱们搜索了多少分片,还有搜索成功和搜索失败的分片数量hits
- 搜索结果hits.total
- 符合搜索条件的文档数量hits.hits
- 实际返回的搜索结果对象数组(默认只返回前10条)hits.sort
- 返回结果的排序字段值(若是是按score进行排序,则没有)hits._score
和 max_score
- 目前先忽略这两个字段以下是相同效果的另外一种将数据放入请求体的方式:
HTTP请求内容:
GET /bank/_search { "query": { "match_all": {} }, "sort": [ { "account_number": "asc" } ] }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "sort": [ { "account_number": "asc" } ] } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_the_search_api/2.json
这里的不一样点在于咱们使用一个JSON格式的请求体代替了在URI中的 q=*
参数。咱们将在下一节详细讨论这种JSON格式的查询方式。
一旦你获得了返回结果,Elasticsearch就彻底执行结束,不会保持任何的服务器资源或者往你的结果里加入开放的游标,理解这一点是很是重要的。这同狠多其余的平台好比SQL数据库的一些特性造成了鲜明的对比,好比在SQL数据库中你可能在查询时,会首先获得查询结果的一部分,而后你须要经过一些有状态的服务端游标不断地去请求服务端来取得剩余的查询结果。
Elasticsearch提供了一种JSON格式的领域特定语言,你可使用它来执行查询。这个一般叫作Query DSL。这门查询语言至关的全面以致于你第一次看到它时会被它吓住,不过学习它最好的方式就是从一些简单的示例程序开始。
回到咱们上个例子,咱们执行了这个查询:
HTTP请求内容:
GET /bank/_search { "query": { "match_all": {} } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "match_all": {} } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_introducing_the_query_language/1.json
分析以上查询,query
部分告诉咱们咱们的查询定义是什么,match_all
部分简单指定了咱们想去执行的查询类型,意思就是在索引中搜索全部的文档。
除了query
参数,咱们还能够经过其余的参数影响搜索结果。在上一节的示例中咱们使用了sort
来指定搜索结果的顺序,这里咱们指定size
来指定返回的结果数量:
HTTP请求内容:
GET /bank/_search { "query": { "match_all": {} }, "size": 1 }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "size": 1 } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_introducing_the_query_language/2.json
注意若是size
没有指定,它默认为10。
以下的示例使用match_all
并返回了11到20的文档:
HTTP请求内容:
GET /bank/_search { "query": { "match_all": {} }, "from": 10, "size": 10 }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "from": 10, "size": 10 } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_introducing_the_query_language/3.json
from
参数(从0开始)指定了从哪一个文档索引开始,size
参数指定了从from
指定的索引开始返回多少个文档。这个特性在实现分页搜索时颇有用。注意若是from
参数没有指定,它默认为0。
以下示例使用match_all
而且按帐户的balance值进行倒序排列后返回前10条文档:
HTTP请求内容:
GET /bank/_search { "query": { "match_all": {} }, "sort": { "balance": { "order": "desc" } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "sort": { "balance": { "order": "desc" } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_introducing_the_query_language/4.json
既然咱们已经了解了一些基础的搜索参数,那就让咱们来深刻学习一下Query DSL吧。首先,咱们来关注一下返回的文档属性。默认状况下,文档会做为搜索结果的一部分返回全部的属性值。这个文档的JSON内容被称为source(返回结果中的hits的_source属性值)。若是咱们不须要返回全部的source文档属性,咱们能够在请求体中加入咱们须要返回的属性名。
以下的示例演示了如何返回两个属性,account_number
和 balance
(在_source
中):
HTTP请求内容:
GET /bank/_search { "query": { "match_all": {} }, "_source": ["account_number", "balance"] }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "_source": ["account_number", "balance"] } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_searches/1.json
注意上面的例子仅仅只是减小了_source
里的属性。它仍然会返回_source
属性,只不过_source
属性中之包含account_number
和balance
两个属性。
若是你以前学过SQL,上面的示例有点像SQL中的SELECT
FROM
中指定返回的字段列表。
如今,让咱们的视线转到查询部分。以前咱们已经看到如何使用match_all
来匹配全部的文档。如今让咱们介绍一个新的查询叫作match
查询,它能够被认为是基本的属性搜索查询(就是经过特定的一个或多个属性来搜索)。
以下的示例返回account_number为20的文档:
HTTP请求内容:
GET /bank/_search { "query": { "match": { "account_number": 20 } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "match": { "account_number": 20 } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_searches/2.json
以下示例返回全部的address字段中包含“mill”这个单词的帐户文档:
HTTP请求内容:
GET /bank/_search { "query": { "match": { "address": "mill" } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "match": { "address": "mill" } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_searches/3.json
以下示例返回全部的address字段中包含“mill”或者是“lane”的帐户文档:
HTTP请求内容:
GET /bank/_search { "query": { "match": { "address": "mill lane" } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "match": { "address": "mill lane" } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_searches/4.json
以下示例是match
的一种变体(match_phrase
),这个将返回全部address中包含“mill lane”这个短语的帐户文档:
HTTP请求内容:
GET /bank/_search { "query": { "match_phrase": { "address": "mill lane" } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "match_phrase": { "address": "mill lane" } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_searches/5.json
如今让咱们介绍 bool
查询。bool
查询容许咱们使用布尔逻辑将小的查询组成大的查询。
以下的示例组合两个match
查询而且返回全部address属性中包含 “mill” 和 “lane” 的帐户文档:
HTTP请求内容:
GET /bank/_search { "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_searches/6.json
在上述示例中,bool
must
子句指定了全部匹配文档必须知足的条件。
相比之下,以下的示例组合两个match
查询而且返回全部address属性中包含 “mill” 或 “lane” 的帐户文档:
HTTP请求内容:
GET /bank/_search { "query": { "bool": { "should": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "bool": { "should": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_searches/7.json
在上述的例子中,bool
should
子句指定了匹配文档只要知足其中的任何一个条件便可匹配。
以下示例组合两个match
查询而且返回全部address属性中既不包含 “mill” 也不包含 “lane” 的帐户文档:
HTTP请求内容:
GET /bank/_search { "query": { "bool": { "must_not": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "bool": { "must_not": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_searches/8.json
在上述例子中,bool
must_not
子句指定了其中的任何一个条件都不知足时便可匹配。
咱们能够在一个bool
查询中同时指定must
,should
和must_not
子句。此外,咱们也能够在一个bool
子句中组合另外一个bool
来模拟任何复杂的多重布尔逻辑。
以下的示例返回全部age属性为40,而且state属性不为ID的帐户文档:
HTTP请求内容:
GET /bank/_search { "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_searches/9.json
在以前的章节中,咱们跳过了一个叫作文档得分(在搜索结果中的_score
属性)的小细节。这个得分是一个数值,它是一个相对量,用来衡量搜索结果跟咱们指定的关键字的相关程度。分数越高,说明这个文档的相关性越大,分数越低,说明这个文档的相关性越小。
可是一些查询结果并不老是须要产生得分,尤为是当他们仅仅被用来过滤文档集的时候。Elasticsearch会检测这种状况并自动优化查询以避免计算无用的分数。
咱们在前面章节介绍的bool
查询也支持 filter
子句,它容许咱们能够在不改变得分计算逻辑的的状况下限制其余子句匹配的查询结果。为了示例说明,让咱们介绍一下range
查询,它容许咱们经过一个值区间来过滤文档。这个一般用在数值和日期过滤上。
以下的示例使用bool查询返回全部余额在20000到30000之间的帐户(包含边界)。换句话说,咱们想查询帐户余额大于等于20000而且小于等于30000的用户。
HTTP请求内容:
GET /bank/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "balance": { "gte": 20000, "lte": 30000 } } } } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "balance": { "gte": 20000, "lte": 30000 } } } } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_filters/1.json
仔细分析一下上面的例子,bool查询在查询部分使用match_all
,在过滤部分使用range
。咱们可使用任何的查询来代替查询部分和过滤部分。在上面的例子中,range查询让结果更加合乎情理,由于文档在这个区间中必定是符合的,就是说,没有比这些相关性更大的了。
除了match_all
,match
,bool
,和range
查询以外,还有不少其余的查询类型,在这里咱们就不一一介绍了。当咱们对这些基础的理解了以后,再去学习和使用其余的查询类型应该是不会太难了。
聚合提供了功能能够分组并统计你的数据。理解聚合最简单的方式就是能够把它粗略的看作SQL的GROUP BY操做和SQL的聚合函数。在Elasticsearch中,你能够在执行搜索后在一个返回结果中同时返回搜索结果和聚合结果。你可使用简洁的API执行搜索和多个聚合操做,而且能够一次拿到全部的结果,避免网络切换,就此而言,这是一个很是强大和高效功能。
做为开始,以下的例子将帐户按state进行分组,而后按count降序(默认)返回前10组(默认)states。
HTTP请求内容:
GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" } } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" } } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_aggregations/1.json
上面的聚合的例子跟以下的SQL相似:
SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC
返回结果为(展现部分):
{ "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 } ] } } }
咱们能够看到有27个帐户在ID
(爱达荷州),而后27个在TX
(得克萨斯州),还有25个在AL
(亚拉巴马州),等等。
注意咱们设置了size=0
来不显示hits搜索结果,由于咱们这里只关心聚合结果。
以下示例咱们在上一个聚合的基础上构建,这个示例计算每一个state分组的平均帐户余额(仍是使用默认按count倒序返回前10个):
HTTP请求内容:
GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_aggregations/2.json
注意咱们是怎么嵌套average_balance
聚合到group_by_state
聚合中的。
这是一个适用于全部聚合操做的通用模式。你能够任意嵌套聚合,从你的数据中提取你须要的主题汇总。
以下例子依然是在以前的聚合上构建,咱们如今来按平均余额倒序排列:
HTTP请求内容:
GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword", "order": { "average_balance": "desc" } }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword", "order": { "average_balance": "desc" } }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_aggregations/3.json
以下示例演示咱们如何按年龄区间分组(20-29,30-39,40-49),而后按性别,最后获取每一个年龄区间,每一个性别的平均帐户余额:
HTTP请求内容:
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" } } } } } } } }
curl命令:
curl -XGET 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d' { "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" } } } } } } } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_aggregations/4.json
还有不少其它的聚合功能在这里咱们就不去详细介绍了。若是你想了解更多,能够参考聚合参考手册。