Elasticsearch 是一个分布式的 RESTful 风格的搜索和数据分析引擎。html
Elasticsearch是一个高度可伸缩的开源全文搜索和分析引擎。它容许您快速和接近实时地存储、搜索和分析大量数据。node
这里有一些使用Elasticsearch的用例:git
Near Realtime (NRT)github
Elasticsearch是一个近乎实时的搜索平台。这意味着从索引文档到能够搜索的时间只有轻微的延迟(一般是1秒)。json
Cluster数组
集群是一个或多个节点(服务器)的集合,它们共同保存你的整个数据,并提供跨全部节点的联合索引和搜索功能。一个集群由一个惟一的名称标识,默认这个惟一标识的名称是"elasticsearch"。这个名称很重要,由于若是节点被设置为按其名称加入集群,那么节点只能是集群的一部分。服务器
确保不要在不一样的环境中用相同的集群名称,不然可能致使节点加入到错误的集群中。例如,你可使用"logging-dev", "logging-test", "logging-prod"分别用于开发、测试和正式集群的名字。网络
Nodeapp
节点是一个单独的服务器,它是集群的一部分,存储数据,并参与集群的索引和搜索功能。就像集群同样,节点由一个名称来标识,默认状况下,该名称是在启动时分配给节点的随机通用惟一标识符(UUID)。若是不想用默认的节点名,能够定义任何想要的节点名。这个名称对于管理来讲很重要,由于你但愿识别网络中的哪些服务器对应于你的Elasticsearch集群中的哪些节点。curl
一个节点能够经过配置集群名称来加入到一个特定的集群中。默认状况下,每一个节点都被设置加入到一个名字叫"elasticsearch"的集群中,这就意味着若是你启动了不少个节点,而且假设它们彼此能够互相发现,那么它们将自动造成并加入到一个名为"elasticsearch"的集群中。
一个集群能够有任意数量的节点。此外,若是在你的网络上当前没有运行任何节点,那么此时启动一个节点将默认造成一个单节点的名字叫"elasticsearch"的集群。
Index
索引是具备某种类似特征的文档的集合。例如,你能够有一个顾客数据索引,产品目录索引和订单数据索引。索引有一个名称(必须是小写的)标识,该名称用于在对其中的文档执行索引、搜索、更新和删除操做时引用索引。
Document
文档是能够被索引的基本信息单元。文档用JSON表示。
Shards & Replicas
一个索引可能存储大量数据,这些数据能够超过单个节点的硬件限制。例如,一个包含10亿条文档占用1TB磁盘空间的索引可能不适合在单个节点上,或者可能太慢而不能单独处理来自单个节点的搜索请求。
为了解决这个问题,Elasticsearch提供了将你的索引细分为多个碎片(或者叫分片)的能力。在建立索引时,能够简单地定义所需的分片数量。每一个分片自己就是一个功能彻底独立的“索引”,能够驻留在集群中的任何节点上。
分片之因此重要,主要有两个缘由:
在一个网络/云环境中随时都有可能出现故障,强烈推荐你有一个容灾机制。Elasticsearch容许你将一个或者多个索引分片复制到其它地方,这被称之为副本。
复制之因此重要,有两个主要缘由:
总而言之,每一个索引均可以分割成多个分片。索引也能够被复制零(意味着没有副本)或更屡次。一旦被复制,每一个索引都将具备主分片(被复制的原始分片)和副本分片(主分片的副本)。在建立索引时,能够为每一个索引定义分片和副本的数量。建立索引后,您能够随时动态地更改副本的数量,但不能更改过后分片的数量。
在默认状况下,Elasticsearch中的每一个索引都分配了5个主分片和1个副本,这意味着若是集群中至少有两个节点,那么索引将有5个主分片和另外5个副本分片(PS:这5个副本分片组成1个完整副本),每一个索引总共有10个分片。
(画外音:副本是针对索引而言的,同时须要注意索引和节点数量没有关系,咱们说2个副本指的是索引被复制了2次,而1个索引可能由5个分片组成,那么在这种状况下,集群中的分片数应该是 5 × (1 + 2) = 15 )
tar -zxf elasticsearch-6.3.2.tar.gz cd elasticsearch-6.3.2/bin ./elasticsearch
注意:不能以root用户运行elasticsearch
By default, Elasticsearch uses port 9200 to provide access to its REST API. This port is configurable if necessary.
检查Elasticsearch是否正在运行:
curl http://localhost:9200/
请求:
curl -X GET "localhost:9200/_cat/health?v"
响应:
epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent 1533625274 15:01:14 elasticsearch green 1 1 0 0 0 0 0 0 - 100.0%
咱们能够看到,咱们命名为“elasticsearch”的集群如今是green状态。
不管什么时候咱们请求集群健康时,咱们会获得green, yellow, 或者 red 这三种状态。
从上面的响应中咱们能够看到,集群"elasticsearch"总共有1个节点,0个分片由于尚未数据。
下面看一下集群的节点列表:
请求:
curl -X GET "localhost:9200/_cat/nodes?v"
响应:
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name 127.0.0.1 15 53 0 0.03 0.03 0.05 mdi * Px524Ts
能够看到集群中只有一个节点,它的名字是“Px524Ts”
请求:
curl -X GET "localhost:9200/_cat/indices?v"
响应:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
上面的输出意味着:咱们在集群中没有索引
如今,咱们建立一个名字叫“customer”的索引,而后查看索引:
请求:
curl -X PUT "localhost:9200/customer?pretty"
(画外音:pretty的意思是响应(若是有的话)以JSON格式返回)
响应:
{ "acknowledged" : true, "shards_acknowledged" : true, "index" : "customer" }
请求:
curl -X GET "localhost:9200/_cat/indices?v"
响应:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open customer rG5fxdruTNmD-bdYIF5zOg 5 1 0 0 1.1kb 1.1kb
结果的第二行告诉咱们,咱们如今有叫"customer"的索引,而且他有5个主分片和1个副本(默认是1个副本),有0个文档。
可能你已经注意到这个"customer"索引的健康状态是yellow。回想一下咱们以前的讨论,yellow意味着一些副本(还没有)被分配。
之因此会出现这种状况,是由于Elasticsearch默认状况下为这个索引建立了一个副本。因为目前咱们只有一个节点在运行,因此直到稍后另外一个节点加入集群时,才会分配一个副本(对于高可用性)。一旦该副本分配到第二个节点上,该索引的健康状态将变为green。
如今,让咱们put一些数据到咱们的"customer"索引:
请求:
curl -X PUT "localhost:9200/customer/_doc/1?pretty" -H 'Content-Type: application/json' -d'{"name": "John Doe"}'
响应:
{ "_index" : "customer", "_type" : "_doc", "_id" : "1", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 0, "_primary_term" : 1 }
从上面的响应能够看到,咱们在"customer"索引下成功建立了一个文档。这个文档还有一个内部id为1,这是咱们在建立的时候指定的。
须要注意的是,Elasticsearch并不要求你在索引文档以前就先建立索引,而后才能将文档编入索引。在前面的示例中,若是事先不存在"customer"索引,Elasticsearch将自动建立"customer"索引。
(画外音:也就是说,在新建文档的时候若是指定的索引不存在则会自动建立相应的索引)
如今,让我从新检索这个文档:
请求:
curl -X GET "localhost:9200/customer/_doc/1?pretty"
响应:
{ "_index" : "customer", "_type" : "_doc", "_id" : "1", "_version" : 1, "found" : true, "_source" : { "name" : "John Doe" } }
能够看到除了"found"字段外没什么不一样,"_source"字段返回了一个完整的JSON文档。
如今,让咱们删除前面建立的索引,而后查看所有索引
请求:
curl -X DELETE "localhost:9200/customer?pretty"
响应:
{ "acknowledged" : true }
接下来,查看一下
curl -X GET "localhost:9200/_cat/indices?v"
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
到如今为止,咱们已经学习了建立/删除索引、索引/查询文档这四个命令
curl -X PUT "localhost:9200/customer" curl -X PUT "localhost:9200/customer/_doc/1" -H 'Content-Type: application/json' -d'{"name": "John Doe"}' curl -X GET "localhost:9200/customer/_doc/1" curl -X DELETE "localhost:9200/customer"
若是咱们仔细研究上面的命令,咱们实际上能够看到如何在Elasticsearch中访问数据的模式。这种模式能够归纳以下:
<REST Verb> /<Index>/<Type>/<ID>
事实上,每当咱们执行更新时,Elasticsearch就会删除旧文档,而后索引一个新的文档。
下面这个例子展现了如何更新一个文档(ID为1),改变name字段为"Jane Doe",同时添加一个age字段:
请求:
curl -X POST "localhost:9200/customer/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d' { "doc": { "name": "Jane Doe", "age": 20 } } '
响应:
{ "_index" : "customer", "_type" : "_doc", "_id" : "1", "_version" : 2, "result" : "updated", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 1, "_primary_term" : 1 }
下面这个例子用脚原本将age增长5
请求:
curl -X POST "localhost:9200/customer/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d' { "script" : "ctx._source.age += 5" } '
在上面例子中,ctx._source引用的是当前源文档
响应:
{ "_index" : "customer", "_type" : "_doc", "_id" : "1", "_version" : 3, "result" : "updated", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 2, "_primary_term" : 1 }
删除文档至关简单。这个例子展现了如何从"customer"索引中删除ID为2的文档:
请求:
curl -X DELETE "localhost:9200/customer/_doc/2?pretty"
响应:
{ "_index" : "customer", "_type" : "_doc", "_id" : "2", "_version" : 1, "result" : "not_found", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 0, "_primary_term" : 1 }
除了可以索引、更新和删除单个文档以外,Elasticsearch还可使用_bulk API批量执行上述任何操做。
这个功能很是重要,由于它提供了一种很是有效的机制,能够在尽量少的网络往返的状况下尽量快地执行多个操做。
下面的例子,索引两个文档(ID 1 - John Doe 和 ID 2 - Jane Doe)
请求:
curl -X POST "localhost:9200/customer/_doc/_bulk?pretty" -H 'Content-Type: application/json' -d' {"index":{"_id":"1"}} {"name": "John Doe" } {"index":{"_id":"2"}} {"name": "Jane Doe" } '
响应:
{ "took" : 5, "errors" : false, "items" : [ { "index" : { "_index" : "customer", "_type" : "_doc", "_id" : "1", "_version" : 4, "result" : "updated", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 3, "_primary_term" : 1, "status" : 200 } }, { "index" : { "_index" : "customer", "_type" : "_doc", "_id" : "2", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 1, "_primary_term" : 1, "status" : 201 } } ] }
接下来的例子展现了,更新第一个文档(ID为1),删除第二个文档(ID为2):
请求:
curl -X POST "localhost:9200/customer/_doc/_bulk?pretty" -H 'Content-Type: application/json' -d' {"update":{"_id":"1"}} {"doc": { "name": "John Doe becomes Jane Doe" } } {"delete":{"_id":"2"}} '
响应:
{ "took" : 8, "errors" : false, "items" : [ { "update" : { "_index" : "customer", "_type" : "_doc", "_id" : "1", "_version" : 5, "result" : "updated", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 4, "_primary_term" : 1, "status" : 200 } }, { "delete" : { "_index" : "customer", "_type" : "_doc", "_id" : "2", "_version" : 2, "result" : "deleted", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 2, "_primary_term" : 1, "status" : 200 } } ] }
如今,咱们来从新查看一下索引文档
curl -X GET "localhost:9200/customer/_doc/1?pretty"
如今咱们已经了解了基础知识,让咱们尝试处理一个更真实的数据集。我准备了一个关于客户银行帐户信息的虚构JSON文档示例。每一个文档都有如下格式:
{ "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" }
你能够从这里下载示例数据
提取它到咱们的当前目录,而且加载到咱们的集群中:
新建一个文件accounts.json,而后将数据复制粘贴到该文件中,保存退出
在这个accounts.json文件所在目录下执行以下命令:
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json"
此时,accounts.json中的文档数据便被索引到"bank"索引下
让咱们查看一下索引:
请求:
curl "localhost:9200/_cat/indices?v"
响应:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open customer DoM-O7QmRk-6f3Iuls7X6Q 5 1 1 0 4.5kb 4.5kb yellow open bank 59jD3B4FR8iifWWjrdMzUg 5 1 1000 0 474.7kb 474.7kb
能够看到,如今咱们的集群中有两个索引,分别是"customer"和"bank"
"customer"索引,1个文档,"bank"索引有1000个文档
如今让咱们从一些简单的搜索开始。运行搜索有两种基本方法:一种是经过REST请求URI发送检索参数,另外一种是经过REST请求体发送检索参数。
(画外音:一种是把检索参数放在URL后面,另外一种是放在请求体里面。至关于HTTP的GET和POST请求)
请求体方法容许你更有表现力,也能够用更可读的JSON格式定义搜索。
用于搜索的REST API可从_search端点访问。下面的例子返回"bank"索引中的全部文档:
curl -X GET "localhost:9200/bank/_search?q=*&sort=account_number:asc&pretty"
让咱们来剖析一下上面的请求。
咱们在"bank"索引中检索,q=*参数表示匹配全部文档;sort=account_number:asc表示每一个文档的account_number字段升序排序;pretty参数表示返回漂亮打印的JSON结果。
响应结果看起来是这样的:
{ "took" : 96, "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", "_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" }, "sort" : [ 0 ] }, { "_index" : "bank", "_type" : "_doc", "_id" : "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" }, "sort" : [ 1 ] }, { "_index" : "bank", "_type" : "_doc", "_id" : "2", "_score" : null, "_source" : { "account_number" : 2, "balance" : 28838, "firstname" : "Roberta", "lastname" : "Bender", "age" : 22, "gender" : "F", "address" : "560 Kingsway Place", "employer" : "Chillium", "email" : "robertabender@chillium.com", "city" : "Bennett", "state" : "LA" }, "sort" : [ 2 ] }, ...... ] }
能够看到,响应由下列几部分组成:
下面是一个和上面相同,可是用请求体的例子:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "sort": [ { "account_number": "asc" } ] } '
区别在于,咱们没有在URI中传递q=*,而是向_search API提供json风格的查询请求体
很重要的一点是,一旦返回搜索结果,Elasticsearch就彻底完成了对请求的处理,不会在结果中维护任何类型的服务器端资源或打开游标。这是许多其余平台如SQL造成鲜明对比。
Elasticsearch提供了一种JSON风格的语言,您可使用这种语言执行查询。这被成为查询DSL。
查询语言很是全面,乍一看可能有些吓人,但实际上最好的学习方法是从几个基本示例开始。
回到咱们上一个例子,咱们执行这样的查询:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "match_all": {} } } '
查询部分告诉咱们查询定义是什么,match_all部分只是咱们想要运行的查询类型。这里match_all查询只是在指定索引中搜索全部文档。
除了查询参数外,咱们还能够传递其余参数来影响搜索结果。在上面部分的例子中,咱们传的是sort参数,这里咱们传size:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "size": 1 } '
注意:若是size没有指定,则默认是10
下面的例子执行match_all,并返回第10到19条文档:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "from": 10, "size": 10 } '
from参数(从0开始)指定从哪一个文档索引开始,而且size参数指定从from开始返回多少条。这个特性在分页查询时很是有用。
注意:若是没有指定from,则默认从0开始
这个示例执行match_all,并按照账户余额降序对结果进行排序,并返回前10个(默认大小)文档。
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "sort": { "balance": { "order": "desc" } } } '
继续学习查询DSL。首先,让咱们看一下返回的文档字段。默认状况下,会返回完整的JSON文档(PS:也就是返回全部字段)。这被成为source(hits._source)
若是咱们不但愿返回整个源文档,咱们能够从源文档中只请求几个字段来返回。
下面的例子展现了只返回文档中的两个字段:account_number 和 balance字段
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "_source": ["account_number", "balance"] } '
(画外音:至关于SELECT account_number, balance FROM bank)
如今让咱们继续查询部分。之前,咱们已经看到了如何使用match_all查询匹配全部文档。如今让咱们引入一个名为match query的新查询,它能够被看做是基本的字段搜索查询(即针对特定字段或字段集进行的搜索)。
下面的例子返回account_number为20的文档
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "match": { "account_number": 20 } } } '
(画外音:至关于SELECT * FROM bank WHERE account_number = 20)
下面的例子返回address中包含"mill"的帐户:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "match": { "address": "mill" } } } '
(画外音:至关于SELECT * FROM bank WHERE address LIKE '%mill%')
下面的例子返回address中包含"mill"或者"lane"的帐户:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "match": { "address": "mill lane" } } } '
(画外音:至关于SELECT * FROM bank WHERE address LIKE '%mill' OR address LIKE '%lane%')
让咱们来引入bool查询,bool查询容许咱们使用布尔逻辑将较小的查询组合成较大的查询。
下面的例子将两个match查询组合在一块儿,返回address中包含"mill"和"lane"的帐户:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } } '
(画外音:至关于SELECT * FROM bank WHERE address LIKE '%mill%lane%')
上面是bool must查询,下面这个是bool shoud查询:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "bool": { "should": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } } '
(画外音:must至关于and,shoud至关于or,must_not至关于!)
(画外音:逻辑运算符:与/或/非,and/or/not,在这里就是must/should/must_not)
咱们能够在bool查询中同时组合must、should和must_not子句。此外,咱们能够在任何bool子句中编写bool查询,以模拟任何复杂的多级布尔逻辑。
下面的例子是一个综合应用:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } } } '
(画外音:至关于SELECT * FROM bank WHERE age LIKE '%40%' AND state NOT LIKE '%ID%')
分数是一个数值,它是文档与咱们指定的搜索查询匹配程度的相对度量(PS:类似度)。分数越高,文档越相关,分数越低,文档越不相关。
可是查询并不老是须要产生分数,特别是当它们仅用于“过滤”文档集时。Elasticsearch检测到这些状况并自动优化查询执行,以便不计算无用的分数。
咱们在前一节中介绍的bool查询还支持filter子句,该子句容许使用查询来限制将由其余子句匹配的文档,而不改变计算分数的方式。
做为一个例子,让咱们引入range查询,它容许咱们经过一系列值筛选文档。这一般用于数字或日期过滤。
下面这个例子用一个布尔查询返回全部余额在20000到30000之间(包括30000,BETWEEN...AND...是一个闭区间)的帐户。换句话说,咱们想要找到余额大于等于20000而且小于等等30000的帐户。
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "balance": { "gte": 20000, "lte": 30000 } } } } } } '
(画外音:至关于SQL中的汇集函数,好比分组、求和、求平均数之类的)
首先,这个示例按state对全部账户进行分组,而后按照count数降序(默认)返回前10条(默认):
(画外音:至关于按state分组,而后count(*),每一个组中按照COUNT(*)数取 top 10)
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "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":50, "timed_out":false, "_shards":{ "total":5, "successful":5, "skipped":0, "failed":0 }, "hits":{ "total":1000, "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 }, { "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 } ] } } }
注意,咱们将size=0设置为不显示搜索结果,由于咱们只想看到响应中的聚合结果。
接下来的例子跟上一个相似,按照state分组,而后取balance的平均值
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } } '
在SQL中,至关于:
SELECT state, COUNT(*), AVG(balance) FROM bank GROUP BY state ORDER BY COUNT(*) DESC LIMIT 10;
下面这个例子展现了咱们如何根据年龄段(20-29岁,30-39岁,40-49岁)来分组,而后根据性别分组,最后获得平均帐户余额,每一个年龄等级,每一个性别:
curl -X GET "localhost:9200/bank/_search" -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" } } } } } } } } '
先写到这里吧!!!
https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html
发现有一本书叫《Elasticsearch: 权威指南》