官方文档翻译 -- 开始 Elasticsearch 入门

Getting started with Elasticsearch

想快速入门,来看官方文档吧!!!
个人翻译版 Github地址,英语水平有限,有错误或遗漏的欢迎留言指正。

准备好如何使用 Elasticsearch 来测试驱动使用 REST APIs 来存储、查询、分析数据了吗?
遵循如下开始教程:

  1. 获取一个 Elasticsearch 实例并启动运行
  2. 添加索引实例文档
  3. 使用 Elasticsearch 查询语法来查询文档
  4. 使用桶(Bucket) 和 指标(metrics) 来聚合分析返回结果

需要更多内容?

查看Elasticsearch 介绍来学习和理解 Elasticsearch 基础的工作原理。如果你已经熟悉 Elasticsearch 且希望知道ELK 如何工作,请跳转Elastic Stack 教程来了解如何使用 Elasticsearch, Kibana, Beats 和 Logstash 来设置系统监控的解决方案。

安装(Installation)

这个略过(...)

探索集群(Exploring Your Cluster)

关于 REST API

现在我们有了可以启动并允许的节点(或集群),下一步是理解如何使用好 Elasticsearch。幸运的是Elasticsearch 提供了非常全面且有力的 REST API 来让我们交互,可以使用 API 完成如下的功能:

  1. 检查集群、节点和索引的健康状态和统计
  2. 管理集群、节点和索引数据和元数据
  3. 对索引完整的增删改查和查询
  4. 运行高级的查询操作,如分页、排序、过滤、脚本、聚合和其他操作

集群健康状态(Cluster Health)

让我们开始基础的健康状态检查,我们可以使用它来我们集群执行情况。我们将使用curl来做,但你也可以用其他允许你发起"HTTP/REST"请求的工具。让我们假定使用相同的节点,当我们启动Elasticsearch,还有打开另一个命令行窗口。

为了检查集群健康状态,我们将会使用_catAPI。你可以通过点击"VIEW IN CONSOLE"使用Kibana的命令行或者使用点击"COPY AS CURL"使用curl命令复制粘贴到命令行终端。

GET /_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
1475247709 17:01:49  elasticsearch green           1         1      0   0    0    0        0             0                  -                100.0%

我们可以看到集群名 "elasticsearch",是启动的绿色状态。
任何时候我们访问集群状态,返回都是绿色、黄色或红色。

  • 绿色 - 所有服务都正常(集群全可用)
  • 黄色 - 所有的数据是有效,但一些复制还没分配(集群全可用)
  • 红色 - 部分数据异常(集群部分用)

注意:当一个集群是红色状态,她会保持从有效的分片来响应查询请求的服务,但自从很多分片未被分配,你将可能需要去修复她。

同样的从上面的响应结果中,我们可以看到总共有1个节点,而且当我们没有数据时,我们有0个分片。注意从我们使用默认集群名"elasticsearch"时,或者还有从单一网络发现去默认查找其他在这个计算机上的节点。这个可能在你的计算机上意外的启动多个节点,并且他们都加入同一个集群。在这种情节中,你可能会从响应结果中看到超过1个节点。

我们可以通过如下命令获取一个集群中的节点列表:

GET /_cat/nodes?v

响应结果:

ip        heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
127.0.0.1           10           5   5    4.46                        dim      *      PB2SGZY

这里,我们可以看到一个节点名"PB2SGZY", 这是我们集群中当前的一个单节点。

列出索引目录(List All Indices)

现在让我们看一眼目录:

GET /_cat/indices?v

响应结果:

health status index uuid pri rep docs.count docs.deleted store.size pri.store.size

简单的表示我们集群中还没没有目录。

创建索引(Create an Index)

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

PUT /customer?pretty
GET /_cat/indices?v

第一个使用 PUT 命令创建了索引并名为"customer"。我们简单地在请求后面附上pretty,让她启动pretty-print模块来打印JSON返回结果。

响应结果:

health status index    uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   customer 95SQ4TSUT7mWBT7VNHH67A   1   1          0            0       260b           260b

第二条命令返回的这些结果告诉我们,我们已经拥有一条索引名为"customer", 而且他有一个独立分片和一个复制(默认)和索引里面包含0个文档。

你也许会注意到"customer"索引有一个黄色状态标记。我们前面的论述中提到黄色意味着有些复制还未分配。这个索引出现这个原因是,Elasticsearch默认值创建一个复制。从我们只有一个节点启动那一刻起,那个复制还不能很好分配(为了高可用)直到稍后的时间点,有另一个节点加入集群。一旦那个复制被分配到第二个节点时,健康状态会变成绿色。

创建索引和查询文档(Index and Query a Document)

现在让我们放一些东西到我们的"customer"索引中。我们将创建一个简单的"custoemr"文档到索引中,且ID=1,如下:

PUT /customer/_doc/1?pretty
{
  "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不需要你去明确创建一个索引,在你创建索引文档进去之前。在前面的例子中,如果索引不存在是,Elasticsearch会自动创建"customer"文档的索引。

让我们重新检索这个刚刚创建的文档:

GET /customer/_doc/1?pretty

响应结果:

{
  "_index" : "customer",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 25,
  "_primary_term" : 1,
  "found" : true,
  "_source" : { "name": "John Doe" }
}

这里没有什么特别的除了 found 字段,着显示状态我们找到一个文档且ID为1,和另一个字段:_source,将会返回我们上一步创建索引时的完整的JSON结果的文档信息。

删除索引(Delete an Index)

接下来我们来删除我们刚刚创建的索引然后列出索引的索引:

DELETE /customer?pretty
GET /_cat/indices?v

响应结果:

health status index uuid pri rep docs.count docs.deleted store.size pri.store.size

这个意味着索引已经被删除成功且我们回到刚启动并且无数据在集群中。

在我们继续学习前,让我们回顾一遍这些我们刚刚学过的API命令:

PUT /customer
PUT /customer/_doc/1
{
  "name": "John Doe"
}
GET /customer/_doc/1
DELETE /customer

如果我们仔细的学习了上面的内容。我们可以实际的体验到一个模式来如何在Elasticsearch中存取数据。这个模式可以归纳为如下:

<HTTP Verb> /<Index>/<Endpoint>/<ID>

这个REST存取模式可以适用所有的API命令,如果你直接记住,你掌握 Elasticsearch 将会是一个很好的开始。

修改数据(Modifying Your Data)

Elasticsearch提供数据操作和查询的能力近乎实时。按默认设置,你可以认为从你添加、更新、删除数据(index/update/delte)到你的结果显示有1s延迟(刷新间隔)。这是与其他平台相比很重要的区别,比如SQL,当事务完成后,数据是直接有效。

创建重建文档(Indexing/Replacing Document)

我们先提前看如何添加一个简单文档的索引。再一次重复执行命令:

PUT /customer/_doc/1?pretty
{
  "name": "John Doe"
}

再一次的,上面的例子会创建一个指定的索引,即ID为1。如果我们用上面的命令执行不同的文档内容时,Elasticsearch 将会替换(或重建)为新的文档来代替原ID为1的已存在文档。

PUT /customer/_doc/1?pretty
{
  "name": "Jane Doe"
}

上面改变了同一个索引为1的文档对应的名字"John Doe" 为 "Jane Doe"。另外一种情况,我们使用不同的ID创建索引新的文档,而已经在索引中的已存在文档则保持不被影响。

PUT /customer/_doc/2?pretty
{
  "name": "Jane Doe"
}

上面使用ID等于2来创建了新索引文档。

当创建索引时,ID是可选的。 如果不指定,Elasticsearch 将会生成随机的ID并用这个ID来索引文档。实际的ID 生成后(或者像我们上一个例子那样明确指定ID),会作为结果的一半分返回。

这个例子告诉我们如何添加一个索引时不需要明确的ID:

POST /customer/_doc?pretty
{
  "name": "Jane Doe"
}

注意上面的例子,当我们不需要指定的ID时,我们使用 POST 代替 PUT 方法。

更新文档(Updating Documents)

为了进一步的可以创建索引和替换文档,我们可以更新文档。注意通过这种方式,实际上 Elasticsearch 不能直接更新。当我们做一次更新时,Elasticsearch 删除旧文档然后再创建了新的文档索引。

这个例子示范了如何通过更改name 字段来更新前文ID为1的文档:

POST /customer/_update/1?pretty
{
  "doc": { "name": "Jane Doe" }
}

这个例子示范如何通过更改name 字段的同事添加一个新的age字段来更新前文ID为1的文档:

POST /customer/_update/1?pretty
{
  "doc": { "name": "Jane Doe", "age": 20 }
}

更新同样可以完美的使用简单的脚本。这个例子使用脚本来为age 字段增加5。

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

在上面的例子中,tx._source 引用了当前的源文档,将会被更新。

Elasticsearch 提供给定表达式来更新多个文档(比如SQL UPDATE-WHERE表达式), 查看docs-update-by-query API

删除文档(Deleting Documents)

删除文档是相当简单的。这个例子示范如何删除ID为2的文档:

DELETE /customer/_doc/2?pretty

查看delete_by_query API, 来了解删除所有匹配的指定查询。值得注意的是这是一种非常高效的方式来删除整个索引代替来删除所有的文档。

合并处理(Batch Processing)

为了进一步的创建、更新、删除个别文档,Elasticsearch 也提供了更为有效的操作来批量使用_blukAPI。这个功能提供非常高效的原理来尽可能快地处理多个操作但尽可能更少的网络请求响应。

作为一个快速的例子,下面实例索引了两个文档(ID 1 - John Doe 与 ID 2 - Jane Doe)在一个合并操作中:

POST /customer/_bulk?pretty
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }

这个例子更新了第一个文档(ID=1),并且删除了第二文档(ID=2)在一个合并操作中:

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

注意上面的删除动作,其中没有相应的源文档,所以删除只需要待删除文档的ID。

合并API 不会失败因为一个动作失败而执行失败。如果一个动作不管啥原因失败,合并操作会继续处理剩下的下一个动作。当这个合并返回时,会为每个动作(用相同的排序顺序)提供一个状态,让你可以检查指定的动作是成功还是失败。

探索数据(Exploring Your Data)

样本数据集(Sample Dataset)

我们已经看了基础部分,让我们来尝试下更真实的数据集。我们已经为"customer"准备了一个虚构的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"
}

有趣的是,这个数据是用www.json-generator.com生成的,所以请忽略实际值和数据的语义都是随机生成的。

加载样本数据集(Loading the Sample Dataset)

你可以从这里下载样本数据集。解压到我们的当前执行命令的目录,用下列命令行执行加载。

curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_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"索引中。

查询API(The Search API)

现在让我们从简单的查询开始。有两种基础的查询方式:一是通过REST请求地址URI中发送查询参数,另一种是把参数放在请求体body中。请求体body允许你加载更多表达式和通过JSON格式定义查询。我们将用一个请求地址发送参数的例子,但在剩余的这个教程中,我们都会使用请求体body的方式。

REST API 的查询通过"_search"作为末端参数。这个例子会返回所有的在"bank"索引中的文档。

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

首先我们仔细分析查询请求。我们查询(_search 末端)"bank"索引,q=*参数命令Elasticsearch 去匹配所有在索引中的文档。sort=account_number:asc参数命令排序结果并用account_number字段进行正序排序。pretty参数告诉Elasticsearch 使用"pretty-printed"来输出JSON结果。

响应结果(部分显示):

{
  "took" : 63,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
        "value": 1000,
        "relation": "eq"
    },
    "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"}
    }, ...
    ]
  }
}

在响应结果中,我们可以看到下面部分:

  • took - Elasticsearch查询所花费的时间毫秒数
  • timed_out - 告诉我们查询时间是否超时
  • _shards - 告诉我们查询时有多事个分片。还有成功或失败的数量统计
  • hits - 查询结果
  • hits.total - 一个对象包含了文档匹配查询条件的总数量
    • hits.total.value - 命中查询的总量统计(必须通过hits.total.relation来)
    • hits.total.relation - hits.total.value是否是准确的命中查询值,取决于该参数是否为"eq" 或低于实际命中统计值(超过或相等),或是该参数等于"gte"
  • hits.hits - 查询结果中实际的数组(默认是前10个文档)
  • hits.sort - 排序值(按分值"score"排序时不生效)
  • hits._score 和 max_score - 暂时忽略这些字段

准确的hits.total值是用另外一个请求参数track_total_hits控制,当这个值设置为true时,请求会跟踪命中查询的准确("relation": "eq")。默认设置为10,000 意味着只会命中10,000个文档。你可以通过设置track_total_hits为true 来禁止。 查看请求体了解更多。

这里另外一种通过请求体查询的方式:

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ]
}

这种方法不同点在于使用请求体代替q=*的请求参数。Elasticsearch提供了JSON风格的查询体来使用_searchAPI。我们将在下一章节讨论这种JSON查询。

明白获取查询返回的结果是很重要的,Elasticsearch是用请求完全完成,而且不需要大部分任何的服务端资源或者打开游标到返回结果中。这是强烈的对比于其他不同平台比如SQL,在这些中平台,最初可能从查询结果中预先获取一个部分子集,然后不得不继续返回服务器,如果你想使用一些服务端游标状态来获取(或者分页)剩下的返回结果。(译注:不是很明白这段话,意思是ES可以一次获取大量数据不需要分页? 还是说统计等多种信息和数据集都在一次查询返回中不需要多次查询。)

介绍查询语言(Introducing the Query Language)

Elasticsearch 提供了JSON风格的领域特殊语言让我们可以施工用执行查询。关于Query DSL。这种查询语言相当综合而且乍一看很吓人,但是是很好的方式通过实际来学习,让我们开始一个简单的例子。

回到我们上一个例子,我们执行下面的查询:

GET /bank/_search
{
  "query": { "match_all": {} }
}

仔细分析上面的例子,query部分告诉我们什么是查询的定义, match_all部分是简单的查询类型是我们要执行的。match_all查询是在制定的索引("bank")中,简单的查询全部文档。

另外的查询参数,我们可以通过其他参数来影响查询结果。在前面的例子中我们传入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且使用"account"字段倒序排序了结果,并返回了前10个(默认大小)结果。

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": { "balance": { "order": "desc" } }
}

执行查询(Executing Searches)

现在我们已经看过基础的查询参数,让我们深挖更多的Query DSL。首先让我们看一眼返回文档字段。默认的,全部JSON文档作为返回结果的一部分。(_source)。如果我们不想要整个源文档返回,我们有能力只请求返回少量包含在源文档中的字段。

这个例子示范了如何只返回两个字段,"account_number"和"balance"(包含在 _source 里面)的的查询:

GET /bank/_search
{
  "query": { "match_all": {} },
  "_source": ["account_number", "balance"]
}

注意上面列子简单减少_source字段,她也是一样只返回一个_source字段,但内部只包含有一个account_numberbalance

如果你来自SQL后端,上面多少有些跟SQL 的SELECT FROM 的字段列表概念相似。

现在让我们移到查询部分。在前面,我们看到如何match_all查询是用户匹配所有文档。现在让我们介绍一种新的查询叫match查询,可以作为基础的查询字段(比如:一个查询完成指定一个字段或者一个数据集的字段)。

这个例子返回account_number 值为20的:

GET /bank/_search
{
  "query": { "match": { "account_number": 20 } }
}

这个例子返回所有账户包含匹配"mill"值在address中:

GET /bank/_search
{
  "query": { "match": { "address": "mill" } }
}

这个例子返回所有账户包含匹配"mill"或是"lane"值在address中:

GET /bank/_search
{
  "query": { "match": { "address": "mill lane" } }
}

这个例子是不同的match(match_phrase)会返回所有账户中包含了短语"mill lane"值在address中:

GET /bank/_search
{
  "query": { "match_phrase": { "address": "mill lane" } }
}

现在是时候介绍bool查询。bool查询允许我们使用布尔逻辑构成小查询到大查询。

这个例子构成两个match查询且返回所有账户包含了"mill"且"lane"值在address中:

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

在上面的例子中,boolmust子句指定所有查询必须都为true,对于任一个文档都必须被匹配。

在对比下,这个例子构成两个match查询且返回所有账户包含了"mill"或"lane"值在address中:

GET /bank/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

在上面的例子中,boolshould子句指定查询列表至少有一个为true,对于任一个文档都必须被匹配。

这个例子构成两个match查询且返回所有账户都不包含了"mill"或"lane"值在address中:

GET /bank/_search
{
  "query": {
    "bool": {
      "must_not": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

在上面的例子中,boolmust_not子句指定查询列表任一个都不为true,对于任一个文档都必须被匹配。

我们可以结合must,should和``must_not子句同时在一个bool查询中。此外,我们可以构成bool查询进入任一个bool`子句去模仿任何一个复杂的多级布尔逻辑。

这个例子返回所有账号中age等于40,但是不住在ID(aho):

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}

执行过滤(Executing Filters)

在前面的小节中,我们跳过一些详细的文档评分(查询结果中的_score字段)。这个分数是数值,是文档匹配查询指定值的相关性如何。在更高的分数,则文档相关更大;更低的评分,则文档相关性更小。

但查询不会一直需要生成评分,在个别只需要过滤的文档集中。Elasticsearch 发现这些情景和自动优化查询执行在排序不会去计算无用的评分。

在我们前面介绍的bool查询中一样支持filter子句,她允许我们使用查询去重新约束文档,会被其他子句匹配,但已经计算完成不改变如何排序。在例子中我们介绍range查询,可以运行我们过滤掉文档中某些范围的值。这个适用于数值和日期过滤。

这个例子使用bool查询去返回账号中"balances"在20000到30000之间的,包含在内。换而言之,我们想查账户中"balance"字段大于或等于20000且低于等于30000。

GET /bank/_search
{
  "query": {
    "bool": {
      "must": { "match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}

仔细分析上面的例子,bool查询包含一个match_all查询(查询部分)和一个range范围查询(过滤部分)。我们可以代替其他的查询语句到查询部分和过滤部。在上面的例子中,范围查询非常的有意义,在范围中的所有的文档都匹配“相等”,比如,没有文档比其他的更有相关性。

另外对于match_all,match,boolrange查询,这里有很多其他查询非常有效,我们咱不会在这里深入。从我们已经基本理解她如何工作,就不会太难去应用这些已经学习的知识和尝试其他的查询类型。

执行聚合(Executing Aggregations)

聚合提供从数据中分组能力和提取统计。更早方式思考聚合是大概的类比等于SQL 的GROUP BY 语句和SQL的聚类函数。在Elasticsearch中,你有能力执行查询返回命中结果,同时返回聚合结果单独从命中所有都在一个响应结果中。这个非常强大高效的,你可以运行查询和多种聚合,还有使用简洁又简单的API来同时(或其中一个)操作获取结果,在一次请求中避免网络多次响应。

首先,这个例子用"state"分组了所有的账号, 然后返回前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 LIMIT 10;

响应结果(部分显示):

{
  "took": 29,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped" : 0,
    "failed": 0
  },
  "hits" : {
     "total" : {
        "value": 1000,
        "relation": "eq"
     },
    "max_score" : null,
    "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(Idaho),接着27个账号在TX(Texas),接着25个账号在AL(Alabama)等等。

注意我们设置site = 0将不显示查询命中,因为我们只在响应结果中想看到聚合的结果。

构建前面的聚合,这个例子通过state计算平均账户的"balance"(再一次只为前10个根据统计值倒序排序):

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}

注意我们嵌套了average_balancegroup_by_state聚合中。这是对于所有的聚合来说是一个普通的模式。你可以嵌套聚合到任意的聚合来提取提供统计来满足从数据获取的请求。

构建一个前面的聚合。让我们现排序"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),然后按性别,最终获得平均的账户"balance",年龄段百分百,性别百分比:

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"
              }
            }
          }
        }
      }
    }
  }
}

有很多其他的聚合我们暂不详细展开,
aggregations reference guide 是非常好的起点,如果你想尝试更多。

总结(Conclusion)

Elasticsearch 是即简单又复杂的产品。我们到目前为止学习了关于 Elasticsearch 的基础,及如何查看和使用 REST APIs。希望这个教程能给你更好的理解 Elasticsearch 是什么,且更重要的是激发你未来去探索 Elastissearch 更丰富的功能。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343