REST API

现在,当我们建立和运行ElasticSearch集群和Kibana可视化工具之后,就可以开始学习如何与ElasticSearch进行通信了。幸运的是,ElasticSearch提供了非常全面和强大的REST API,你可以在Kibana的开发工具中使用这些REST API,这样便能和集群进行交互。你可以通过REST API处理以下几个事项:

  • 检查你的集群、节点和索引的健康、状态和统计数据。
  • 管理你的集群、节点、索引数据和元数据。
  • 对你的索引进行CRUD(创建、读取、更新和删除)和搜索操作。
  • 执行高级搜索操作,如分页、排序、筛选、脚本、聚合,以及很多其他操作。

集群健康

 

我们可以从基本的健康检查开始学习,我们可以观察ElasticSearch集群正在做什么。我们将在Kibana的开发工具中进行健康检查,但是你也可以使用任何能够发起HTTP/REST调用的工具。

若要检查集群健康状态,我们可以使用名为_cat的API。我们可以在Kibana的开发工具中运行以下命令:

  1. GET /_cat/health?v

集群的响应如下图所示:

我们可以看到,我们的集群名为“elasticsearch”,健康状态为绿色。

每当我们查询集群的健康状态,我们看到的状态只会是绿色、黄色或红色:

  • 绿色 — 集群正常工作(集群功能完备)。
  • 黄色 — 所有数据都是可用的,但是某些复制尚未分配(集群功能完备)。
  • 红色 — 无论什么原因,某些数据是不可用的(集群部分功能可用)。

注意:当集群的健康状态变为红色时,集群仍然可以通过可用的分片为搜索请求提供服务,但是你最好尽快解决健康问题,因为集群中还有尚未分配的分片。

从上述的响应信息,我们还可以看到1个节点,其中有13个索引分片,它们是Kibana为了监控集群而创建的索引。注意,因为我们使用默认的集群名称(elasticsearch),并且ElasticSearch默认会使用单播网络发现来找到相同主机上的其他节点,所以当你在同一台主机中启动多个节点时,这些节点很有可能意外地加入同一个集群。在这种情况下,你有可能在上述响应信息中看到多个节点。

我们还可以在Kibana的开发工具中运行以下命令,获取节点列表:

  1. GET /_cat/nodes?v

集群的响应如下图所示:

此处,我们可以看到一个名为“TE576pU”的节点,它是我们集群中当前包含的唯一节点。

列出所有索引

现在,可以看看我们的索引:

  1. GET /_cat/indices?v

集群的响应如下图所示:

可以看到,目前集群中有13个索引。

创建索引

现在,我们可以创建一个名为customer的索引,然后再次列出所有的索引:

  1. PUT /customer?pretty
  2. GET /_cat/indices?v

第一个命令会使用PUT指令创建一个名为customer的索引。我们会在命令最后添加pretty关键字,提示控制台优雅地输出JSON响应信息(如果有的话)。

集群的响应如下图所示:

第二个命令的结果会告诉我们,集群现在有一个名为customer的索引,它有5个主分片和1个复制分片(默认配置),这个索引包含0个文档。

你可能还会注意到,customer索引的健康状态是黄色的。正如前文所述,黄色表示某些复制尚未分配。之所以出现这样的问题,是因为ElasticSearch默认会为这个索引创建一个复制分片。由于我们目前只有一个节点正在运行,因此在另一个新节点加入集群之前,我们还不能为这个索引分配复制分片(为了实现高可用功能)。一旦将这个复制分片分配至第二个节点之中,这个索引的健康状态就会变为绿色。

文档的索引和查询

现在我们可以向customer索引添加一些文档。在索引某个文档之前,我们必须告诉ElasticSearch,这个文档应该归属于索引中的何种类型。

我们将一个简单的客户文档索引至customer索引的“external”类型之中,这个文档只有一个等于1的ID:

  1. PUT /customer/external/1?pretty
  2. {
  3. "name": "John Doe"
  4. }

集群的响应如下图所示:

我们从上述的响应信息可以看到,一个新建的客户文档已经成功地添加至customer索引的“external”类型之中了。这个文档还有一个取值为1的内部id,我们在索引文档时指定了这个取值。

需要注意的是,在你可以将文档索引至某个索引之前,ElasticSearch不会要求你显式地创建这个索引。在前面的示例中,如果在索引文档之前,customer索引事先不存在,那么ElasticSearch将会自动创建customer索引。

现在,我们可以获取刚刚索引的文档:

  1. GET /customer/external/1?pretty

集群的响应如下图所示:

响应信息包含两个有特殊意义的字段:

  • found:表示我们找到一个请求ID为1的文档;
  • _source:表示返回完整的JSON文档,这个文档是先前我们索引的客户文档。

删除索引

现在,我们将删除刚刚创建的索引,然后再次列出所有的索引:

  1. DELETE /customer?pretty
  2. GET /_cat/indices?v

集群的响应如下图所示:

我们从响应信息可以看到,先前创建的索引已经成功删除,我们的集群又恢复至先前的状态了。

在我们继续深入学习之前,我们还要再仔细复习本文学到的一些API命令:

  1. PUT /customer
  2. PUT /customer/external/1
  3. {
  4. "name": "John Doe"
  5. }
  6. GET /customer/external/1
  7. DELETE /customer

如果我们仔细学习了上述命令,我们可以发现访问ElasticSearch数据的一个模式。这个模式可以总结如下:

  1. <REST Verb> /<Index>/<Type>/<ID>

这种REST访问模式适用于所有的API命令,如果你能够简单地记住这个模式,那么你在掌握ElasticSearch时就能有一个良好的开端。

 

ElasticSearch能够以接近实时的速度提供数据操作和搜索功能。在默认情况下,从索引/更新/删除数据到出现在搜索结果之间,你可能会感受到有1秒的延迟时间(刷新间隔)。这是与SQL等其他平台的一个重要区别,这些平台在完成事务之后,它们的数据立即可用。

索引/替换文档

先前,我们已经知道如何索引一个单个的文档。我们可以再次回忆这个命令:

  1. PUT /customer/external/1?pretty
  2. {
  3. "name": "John Doe"
  4. }

 

同样,上述命令会将指定的文档索引至customer索引的“external”类型之中,它的ID是1。如果我们再次执行上述命令,但是使用不同的(或相同的)文档,那么ElasticSearch会将ID为1的已有文档替换(重新索引)为一个新的文档。

  1. PUT /customer/external/1?pretty
  2. {
  3. "name": "Jane Doe"
  4. }

上述命令会修改ID为1的文档的name字段,从“John Doe”修改为“Jane Doe”。另一方面,如果我们使用一个不同的ID,那么就会索引一个新的文档,并且索引中已有的文档会保持不变。

  1. PUT /customer/external/2?pretty
  2. {
  3. "name": "Jane Doe"
  4. }

上述命令会索引一个新的文档,它的ID为2。

当进行索引时,ID部分是可选的。如果没有指定,那么ElasticSearch将会生成一个随机的ID,然后用其索引文档。ElasticSearch生成的(或者我们在先前示例中明确指定的)真实ID是调用索引API的返回信息的一部分。

以下示例演示如何在没有显式ID的情况下对文档进行索引:

  1. POST /customer/external?pretty
  2. {
  3. "name": "Jane Doe"
  4. }

注意,在上面的例子中,我们使用的是POST命令,而不是PUT命令,因为我们没有指定任何ID。

更新文档

除了能够索引和替换文档之外,我们还可以更新文档。需要注意的是,ElasticSearch实际上在底层并没有立即执行更新操作。每当我们执行一次更新操作时,ElasticSearch就会删除旧的文档,然后索引一个新的文档,所有的更新内容都包含在这个新文档之中。

以下示例演示如何更新我们先前索引的文档(ID为1),将name字段修改为“Jane Doe”:

  1. POST /customer/external/1/_update?pretty
  2. {
  3. "doc": { "name": "Jane Doe" }
  4. }

以下示例演示如何更新我们先前索引的文档(ID为1),将name字段修改为“Jane Doe”,同时添加一个新的age字段:

  1. POST /customer/external/1/_update?pretty
  2. {
  3. "doc": { "name": "Jane Doe", "age": 20 }
  4. }

你还可以使用简单脚本来执行更新操作。以下示例会使用一个脚本,将age字段增加5:

  1. POST /customer/external/1/_update?pretty
  2. {
  3. "script" : "ctx._source.age += 5"
  4. }

在上述示例中,ctx._source指的就是即将更新的当前源文档。

ElasticSearch还可以通过给定的查询条件更新多个文档(类似于SQL的UPDATE-WHERE语句)。请参考更新查询API

删除文档

删除文档是非常简单的。以下示例演示如何删除我们先前索引的ID为2的客户文档:

  1. DELETE /customer/external/2?pretty

请参考删除查询API,这样便能通过特定的查询删除所有匹配的文档。值得一提的是,删除一个完整的索引,相比起通过删除查询API来删除所有文档,效率要高得多。

批量处理

除了能够索引、更新和删除单个文档之外,ElasticSearch还提供了批量执行上述操作的功能,也就是名为<a href="https://www.elastic.co/guide/en/elasticsearch/reference/5.6/docs-bulk.html" title="_bulk“>_bulk的API命令。这个功能非常重要,因为它能够提供一种非常高效的机制,可以尽量快地完成多个操作,同时尽量少地耗费网络往返的开销。

举个简单的例子,以下命令会在一个批量操作中索引两个文档(ID 1:John Doe和ID 2:Jane Doe):

  1. POST /customer/external/_bulk?pretty
  2. {"index":{"_id":"1"}}
  3. {"name": "John Doe" }
  4. {"index":{"_id":"2"}}
  5. {"name": "Jane Doe" }

以下示例会在一个批量操作中更新第一个文档(ID为1),然后删除第二个文档(ID为2):

  1. POST /customer/external/_bulk?pretty
  2. {"update":{"_id":"1"}}
  3. {"doc": { "name": "John Doe becomes Jane Doe" } }
  4. {"delete":{"_id":"2"}}

注意,对于删除操作,在它之后没有相应的源文档,因为删除操作只需要待删除文档的ID就可以了。

批量操作的API命令不会由于其中某个动作失败而执行失败。如果某个动作由于某种原因而执行失败,API将继续处理后面的剩余动作。当批量操作的API命令返回时,它会显示每个操作的执行结果(和动作的发送顺序相同),这样你便能检查某个特定操作是否执行失败。

 

数据集样本

现在,我们已经了解了ElasticSearch的基础知识,接下来我们将会研究一个更加具有现实意义的数据集。我已经准备了一个虚拟的客户银行账户信息的JSON文档示例。每个文档都具有以下模式:

  1. {
  2. "account_number": 0,
  3. "balance": 16623,
  4. "firstname": "Bradshaw",
  5. "lastname": "Mckenzie",
  6. "age": 29,
  7. "gender": "F",
  8. "address": "244 Columbus Place",
  9. "employer": "Euron",
  10. "email": "bradshawmckenzie@euron.com",
  11. "city": "Hobucken",
  12. "state": "CO"
  13. }

 

为了方便起见,这些数据是通过www.json-generator.com/生成的。因此,请忽略这些数据的实际值和语义,它们全都是随机生成的。

加载数据集样本

你可以从此处下载数据集样本。将其解压至我们的当前目录,然后将这些数据加载至我们的集群之中,如下所示:

  1. curl -H "Content-Type: application/json" -XPOST 'localhost:9200/bank/account/_bulk?pretty&refresh' --data-binary "@accounts.json"
  2. curl 'localhost:9200/_cat/indices?v'

集群的响应如下图所示:

上述的响应信息表示我们刚刚成功地将1000个文档批量索引至bank索引之中(归属于account类型)。

搜索API

现在,我们可以从一些简单的搜索示例开始。有两种运行搜索示例的基本方法:一种是通过REST请求的URI来发送搜索参数,另一种是通过REST请求体来发送搜索参数。使用请求体的方法使得你能够更加清晰地表达搜索请求,你还能够以一种更具可读性的JSON格式来定义搜索请求。我们将通过以下示例来尝试使用请求URI的方法,但是在本文的其余部分中,我们将专门使用请求体的方法。

你可以通过_search端点来访问用于搜索的REST API命令。以下示例会返回bank索引中的所有文档:

  1. GET /bank/_search?q=*&sort=account_number:asc&pretty

首先,我们需要仔细分析搜索请求的调用。我们在bank索引中进行搜索(通过_search端点),q=*参数会告诉ElasticSearch匹配索引中的所有文档。sort=account_number:asc参数会使用每个文档的account_number字段,对搜索结果进行升序排序。再一次地,pretty参数会告诉ElasticSearch以一种良好的JSON格式输出搜索结果。

集群的响应如下图所示(只显示一部分结果):

对于上述的响应信息,我们可以看到以下几个部分:

  • took — ElasticSearch执行搜索操作耗费的时间,以毫秒为单位。
  • timed_out — 表示搜索操作是否超时
  • _shards — 表示搜索的分片数量,以及搜索成功/失败的分片数量。
  • hits — 搜索结果
  • hits.total — 匹配搜索条件的文档总数
  • hits.hits — 搜索结果的实际数组(默认为前10个文档)
  • hits.sort — 用于结果排序的关键字(如果通过得分来排序,则不会有这项信息)
  • hits._score和max_score — 暂时忽略这些字段

以下示例会使用请求体方法进行搜索,运行结果和上述示例完全相同:

  1. GET /bank/_search
  2. {
  3. "query": { "match_all": {} },
  4. "sort": [
  5. { "account_number": "asc" }
  6. ]
  7. }

此处的区别是,我们向名为_search的API提交了一个JSON风格的查询请求体,而不是在URI中传递q=*参数。我们将在下一节中讨论这个JSON查询。

一旦ElasticSearch向你返回了搜索结果,这就表示搜索请求已经处理完成了,ElasticSearch不会维护任何类型的服务端资源,也不会在搜索结果中打开游标,理解这一点非常重要。这种行为和许多其他的平台(如SQL)形成了鲜明的对比,在这些平台中,你可能会首先获得查询结果的一部分子集,然后如果你想要使用某种有状态的服务端游标来获取(或翻页浏览)剩余的结果,那么你必须不断地返回服务端。

查询语言简介

ElasticSearch提供了一种JSON风格的领域特定语言(DSL),你可以使用它执行查询操作。这种语言称为查询DSL。查询语言是非常全面的,咋看起来可能令人望而生畏,但是学习它的最好的方法实际上需要从几个基本示例开始。

以下示例会搜索bank索引中的所有文档:

  1. GET /bank/_search
  2. {
  3. "query": { "match_all": {} }
  4. }

仔细分析上述示例,query部分表示查询的具体定义,match_all部分表示我们想要运行的查询类型。match_all查询会在指定的索引中简单地搜索所有的文档。

除了query参数之外,我们还可以传递其他参数,这些参数可能会影响搜索结果。在上一节的示例中,我们传递sort参数,此处我们传递size参数:

  1. GET /bank/_search
  2. {
  3. "query": { "match_all": {} },
  4. "size": 1
  5. }

注意,如果没有指定size参数,那么默认值是10。

以下示例会执行一次match_all查询,然后返回第11个至第20个文档。

  1. GET /bank/_search
  2. {
  3. "query": { "match_all": {} },
  4. "from": 10,
  5. "size": 10
  6. }

from参数指定从哪个文档下标(首个下标为0)开始返回结果;size参数指定从from参数指定的位置,返回多少个结果。此功能在实现搜索结果分页查询时非常有用。注意,如果没有指定from参数,那么默认值是0。

以下示例会执行一次match_all查询,然后根据账号的balance字段进行降序排序,最后返回前10个(默认大小)结果文档。

  1. GET /bank/_search
  2. {
  3. "query": { "match_all": {} },
  4. "sort": { "balance": { "order": "desc" } }
  5. }

执行搜索命令

现在,我们已经了解了一些基本的搜索参数,我们接下来将会进一步深入学习查询DSL。首先,我们要看看返回的文档字段。在默认情况下,完整的JSON文档会作为所有搜索的一部分结果返回。这些结果被称为源文档(也就是搜索结果中的_source字段)。如果我们不希望返回整个文档,那么我们可以只请求返回的源文档内的一些指定的字段。

以下示例演示如何返回两个字段(_source中的account_number和balance字段)作为搜索结果:

  1. GET /bank/_search
  2. {
  3. "query": { "match_all": {} },
  4. "_source": ["account_number", "balance"]
  5. }

注意,上述示例只是简单地减少了_source字段的内容。它仍然会返回一个名为_source的字段,但是这个字段只包含account_number和balance字段。

如果你具有SQL的背景知识,那么上述示例在概念上有点类似于SQL的SELECT FROM字段列表。

现在,我们应该关注查询部分了。此前,我们已经知道如何使用match_all查询来匹配所有的文档。现在,我们将要介绍一种新的查询方式,叫做<a href="https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-match-query.html" title="match“>match查询,它是一种基本的按字节搜索和查询的方法(例如,对指定的一个字段或字段集合进行搜索)。

以下示例会返回账号等于20的文档:

  1. GET /bank/_search
  2. {
  3. "query": { "match": { "account_number": 20 } }
  4. }

以下示例会返回在地址中包含词语“mill”的所有文档:

  1. GET /bank/_search
  2. {
  3. "query": { "match": { "address": "mill" } }
  4. }

以下示例会返回在地址中包含词语“mill”或“lane”的所有文档:

  1. GET /bank/_search
  2. {
  3. "query": { "match": { "address": "mill lane" } }
  4. }

以下示例是match(match_phrase)查询的一个变体,它会返回在地址中包含短语“mill lane”的所有账号:

  1. GET /bank/_search
  2. {
  3. "query": { "match_phrase": { "address": "mill lane" } }
  4. }

现在,我们将介绍<a href="https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-bool-query.html" title="bool“>bool查询。bool查询使得我们能够使用布尔逻辑,将较小的查询组合为较大的查询。

以下示例会组合两个match查询,然后返回在地址中包含“mill”和“lane”的所有账号:

  1. GET /bank/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. { "match": { "address": "mill" } },
  7. { "match": { "address": "lane" } }
  8. ]
  9. }
  10. }
  11. }

在上述示例中,bool must子句指定了所有结果必须为真的查询,文档必须同时匹配这两个条件才能作为查询结果。

相反,以下示例也会组合两个match查询,但是返回在地址中包含“mill”或“lane”的所有账号:

  1. GET /bank/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "should": [
  6. { "match": { "address": "mill" } },
  7. { "match": { "address": "lane" } }
  8. ]
  9. }
  10. }
  11. }

在上述示例中,bool should子句指定了结果为真的查询列表,只要文档匹配其中任意一个条件就可以作为查询结果。

以下示例会组合两个match查询,然后返回在地址中既不包含“mill”,也不包含“lane”的所有账号:

  1. GET /bank/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must_not": [
  6. { "match": { "address": "mill" } },
  7. { "match": { "address": "lane" } }
  8. ]
  9. }
  10. }
  11. }

在上述示例中,bool must_not子句指定了结果为假的查询列表,文档必须同时不匹配这两个条件才能作为查询结果。

我们可以在一个bool查询中同时组合使用must、should和must_not子句。此外,我们还可以在bool查询中组合任意多个bool子句,这样便能模拟任何复杂的多层级布尔逻辑。

以下示例会返回年龄为40岁,并且不居住在爱达荷州(ID)的储户的账号:

  1. GET /bank/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. { "match": { "age": "40" } }
  7. ],
  8. "must_not": [
  9. { "match": { "state": "ID" } }
  10. ]
  11. }
  12. }
  13. }

执行筛选命令

在上一节中,我们跳过了一个叫做文档评分的小细节(也就是搜索结果中的_score字段)。评分是一个数值,它是一种用于判断文档和我们指定的搜索查询之间的匹配程度的相对度量。若评分越高,则文档的相关度就越高;若评分越低,则文档的相关度就越低。

但是,查询并不总是需要生成评分,特别是当查询仅仅用于“筛选”文档集合时。为了避免计算无用的评分,ElasticSearch会发现这些情况,然后自动优化查询的执行过程。

我们在上一节中介绍的<a href="https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-bool-query.html" title="bool“>bool查询也支持filter子句,这个子句使得我们能够在不改变评分计算方法的前提下,只使用一次查询就可以限制和其他子句相匹配的结果文档。在以下示例中,我们会引入<a href="https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-range-query.html" title="range“>range查询,它使得我们能够通过某个范围的值来筛选结果文档。这种查询通常用于数字或日期的筛选。

以下示例会执行一次布尔查询,返回账户余额在20000至30000之间(闭区间)的所有账号。换句话说,我们希望找到账户余额大于等于20000,并且小于等于30000的账号。

  1. GET /bank/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": { "match_all": {} },
  6. "filter": {
  7. "range": {
  8. "balance": {
  9. "gte": 20000,
  10. "lte": 30000
  11. }
  12. }
  13. }
  14. }
  15. }
  16. }

仔细分析上述的示例,我们发现这个布尔查询包含一个match_all查询(查询部分)和一个range查询(筛选部分)。我们可以将任何其他的查询替换至上述的查询部分和筛选部分。在上述的示例中,范围查询的执行结果是完全正确的,因为落在范围内的文档全部都是“相等”匹配的,也就是说,没有一个文档的相关度比另一个文档更高。

除了match_all、match、bool和range查询之外,ElasticSearch还有很多其他的查询类型,我们不会在本文中深入学习这些查询类型。由于我们已经基本了解ElasticSearch的查询操作是如何工作的,所以要将这些知识应用到其他查询类型的学习和实验中并不会太难。

执行聚合命令

聚合操作可以将你的数据进行分组,还可以从你的数据中提取统计数据。你可以将聚合操作大致想象为SQL的GROUP BY操作,以及SQL的聚合函数。在ElasticSearch中,你能够在执行搜索操作之后获得返回的命中结果,同时还能够获得返回的聚合结果,这两种结果都在同一个响应消息之中。这是一项非常强大和高效的功能,因为只要通过一次请求,你就可以运行多个查询和聚合操作,然后一次性获得这些操作返回的所有结果。通过这个简单明了的API命令,便可以避免不必要的网络往返开销。

首先,以下示例会根据账号所属的州,对所有账号进行分组,然后返回前10个(默认)州的数据,按照账号数量降序排列(也是默认):

  1. GET /bank/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "group_by_state": {
  6. "terms": {
  7. "field": "state.keyword"
  8. }
  9. }
  10. }
  11. }

如果使用SQL语言,那么上述的聚合操作可以类似地写为:

  1. SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC

集群的响应如下图所示(只显示一部分):

我们可以看到,在ID(爱达荷州)中有27个账号,随后的TX(德克萨斯州)中有27个账号,再随后的AL(阿拉巴马州)中有25个账号,以此类推。

注意,我们设置了size=0,这样便不会显示搜索命中的结果,因为我们只想在响应消息中看到聚合结果。

在前面的聚合示例的基础上,以下示例会计算每个州的平均账户余额(再一次地,只会显示前10个州,按照账号数量降序排列):

  1. GET /bank/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "group_by_state": {
  6. "terms": {
  7. "field": "state.keyword"
  8. },
  9. "aggs": {
  10. "average_balance": {
  11. "avg": {
  12. "field": "balance"
  13. }
  14. }
  15. }
  16. }
  17. }
  18. }

注意,我们是如何将average_balance聚合操作内嵌至group_by_state聚合操作中的。这是所有聚合操作的常见模式。你可以将聚合操作内嵌至其他聚合操作,这样便能从你的数据中任意地提取关键的概要数据。

在前面的聚合示例的基础上,以下示例会降序排列每个州的平均账户余额:

  1. GET /bank/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "group_by_state": {
  6. "terms": {
  7. "field": "state.keyword",
  8. "order": {
  9. "average_balance": "desc"
  10. }
  11. },
  12. "aggs": {
  13. "average_balance": {
  14. "avg": {
  15. "field": "balance"
  16. }
  17. }
  18. }
  19. }
  20. }
  21. }

以下示例说明了我们如何针对年龄组(20-29、30-39和40-49)进行分组,然后再针对性别进行分组,最后获取每个年龄组、每种性别的平均账户余额:

  1. GET /bank/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "group_by_age": {
  6. "range": {
  7. "field": "age",
  8. "ranges": [
  9. {
  10. "from": 20,
  11. "to": 30
  12. },
  13. {
  14. "from": 30,
  15. "to": 40
  16. },
  17. {
  18. "from": 40,
  19. "to": 50
  20. }
  21. ]
  22. },
  23. "aggs": {
  24. "group_by_gender": {
  25. "terms": {
  26. "field": "gender.keyword"
  27. },
  28. "aggs": {
  29. "average_balance": {
  30. "avg": {
  31. "field": "balance"
  32. }
  33. }
  34. }
  35. }
  36. }
  37. }
  38. }
  39. }

ElasticSearch还有很多其他的聚合操作,我们不会在本文中深入学习这些聚合操作。如果你想要深入体验聚合操作,那么聚合操作参考指南将会是一个很好的切入点。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐