elasticsearch入门|后台服务器|码途山海.智隐长卷 -

程序人生|重庆纽新

找回密码
立即注册

QQ登录

只需一步,快速开始

欢迎访问【程序人生-重庆纽新】,本网站为软件开发人员视觉的IT资讯、软件开发中各种问题的解决办法!!
搜索
发新帖


2308

积分

0

好友

259

主题
楼主
发表于 2017-10-13 17:37:15 | 查看: 1608| 回复: 0
elasticsearch 是一个可伸缩,开源的全文搜索引擎。你可以使用它接近实时地储存,搜索,快速分析大规模的数据。它通常被用作底层的引擎或技术,加强一些具有复杂的搜索特性和需求的应用程序。
下面列举一些简单的elasticsearch的使用案列:

  • 你运行一个网店,并允许顾客搜索你售卖的产品。对于这种情况,你可以elasticsearch储存你所有的产品目录和库存,并提供搜索和自动补全关键字的功能。
  • 你想收集日志或者事务数据,然后对其进行趋势分析,数据发掘,统计,汇总和异常分析。对于这种情况,你可以使用Logstashes 栈的一部分)去收集,聚合,解析你的数据。然后使用Logstatsh把这些数据填充到elasticsearch中。一旦数据同步到elasticsearch,你就可以进行查询和聚合操作,以发掘你感兴趣的信息。
  • 你运行一个价格警报平台,它允许顾客指定一条规则:我对某个电子产品感兴趣,如果下个月内,有摊贩报价低于x元,就希望平台可以通知我。这种情况,你可以收集摊贩的售卖价格,然后把这些数据推送到elasticsearch,并利用它的反向搜索能力(过滤器,即词=》文档,而不是文档=》词,Percolator,过滤器),针对顾客的查询,匹配价格的变动,一旦满足顾客的要求,就通知客户。
  • 你有一些商业情报需要快速研究,分析,可视化和在大量(百万或十亿个记录)的数据中查找一个特定的问题。在这个例子,你可以使用elasticsearch去储存你的数据,然后使用 Kibanaes栈的一部分,一个可视化的用于分析数据的web程序)去定制报表,把那些你觉得很重要的数据以可视化的方式展现出来。另外,你还可以使用elasticsearch的聚合函数去执行复杂的商业情报查询。
在本教程的剩余部分,我会通过elasticsearch的启动和运行过程指导你初步认识它,并执行一些基本的操作,例如:索引,搜索,和修改你的数据。在教程的结束后,你将会对:1.elasticsearch是什么,2.它是如何工作的,有一个很清晰的概念。希望你能受到启发,并知道如何使用它去建立一个复杂的搜索程序或者从你的数据中发掘一些情报。
基本概念(Basic Concepts

下面介绍一些elasticsearch的核心概念。从一开始就理解这些概念对你的学习过程十分有帮助。
接近实时(Near Realtime NRT
elasticsearch 是一个接近实时的搜索平台。这意味着从索引一个文档到这个文档可以被搜索的过程,仅有轻微的延迟(正常情况是一秒)
集群(cluster
集群是一个或多个节点(服务器)的集合,它们联合起来保存所有的数据(索引以分片为单位分散到多个节点上保存)并且可以在所有的节点上进行索引和搜索操作。集群通过一个唯一的名字区分,默认的名字是“elasticsearch”。这个名字十分重要,因为一个节点仅仅可以属于一个集群,并根据集群名称加入集群。
确保你不要在不同的环境中使用一样的集群名字,否则你最终有可能加入了错误的集群(例如生产环境的节点错误加入了开发环境的集群)。例如,你可以分别在开发,过渡,生产环境中使用集群名称 logging-dev,logging-stage和logging-prod。
注意,集群也可以仅拥有一个节点。此外,你也可以开启多个独立的集群,他们都有自己唯一的集群名称。
节点(node
节点是一个服务器,也是集群的一部分,它存储你的数据,并参与集群的索引和搜索。和集群一样,节点也是通过唯一的名字去区分,默认名字是一个随机的UUID,当服务器启动的时候就会设置到节点。你也可以自定义节点的名称。名称对管理员来说十分重要,它可以帮助你辨认出集群中的各个服务器和哪个节点相对应。
节点通过配置集群的名称,就可以加入到集群中。默认,节点都加入一个叫elasticsearch的集群,这意味着如果你在网络中启动了大量的节点并且假如他们都能互相通讯的话,那么他们将会被自动的加入一个名字叫elasticsearch的集群。
索引(index
索引是很多文档的集合,这些文档都具备一些相似的特征。例如,你可以分别创建客户,产品目录和其他数据的索引。索引是通过名字(必须是小写的)区分的,当执行索引,搜索,更新和删除操作时,这个名字将会被用来引用实际的索引。
类型(Type
你可以在索引中定义一个或多个类型。类型是索引的一个逻辑分类或划分,它的概念完全取决于你自己如何去理解。通常,类型是为一些具备公共字段的文档定义的。例如,假想你在运行一个博客平台,并且把其全部的数据都存储索引中。你可以在索引中定义一个用于保存用户数据的类型,另一个用于保存博客数据的类型,还有一个用于保存评论数据的类型。
文档(document
文档是可以被索引的基本单位。例如,你可以用一个文档保存单个客户的数据,另一个文档保存单个产品的数据,还有一个文档保存单个订单的数据。文档使用一种互联网广泛存在的数据交换格式保存-JSON。
你可以在索引/类型中存储大量你想存储的数据。值得注意的是,尽管文档本质上是存放在索引中,但实际上是被索引到索引中的一个类型中。
分片和复制(shards &replicas
一个索引存储的数据有可能超过单个节点硬盘容量。例如,索引10亿个的文档可能占用1TB的硬盘空间,单个节点的硬盘有可能不足以存储那么大的数据量,或者在单个节点保存大量的索引会降低服务器处理搜索请求的速度。
为了解决这个问题,elasticsearch 可以让你把索引数据划分到多个分片上。当你创建一个索引,你可以简单的定义你想要的分片数量。每个分片本身就具备索引的全部功能,可以存放在集群中的任何一个节点。
分片的主要是为了以下两个目的:

  • 它允许你水平划分/扩展你的内容
  • 它允许你并行地分发操作到多个节点的分片上,从而可以提升性能或吞吐量。
分片如何分发以及各分片上的文档如何被收集到单个搜索请求中的机制是由elasticsearh完全管理的,这个过程对用户是透明的(用户不需要关心这个机制的过程)
在网络或云环境中,任何时候都可能发生错误。推荐建立容错机制,处理分片或节点宕机或因为某种原因消失在网络上的情况。这样可以非常有效地降低风险。为此,elasticsearch允许你创建一个或者多个分片的副本,我们把这个副本叫做复制分片,简称复制。
复制分片主要是为了以下两个目的:

  • 它提供了高可用性,以防节点/分片不可以用。因此,注意不要把复制分片和主分片放在同一个节点上。
  • 它允许你扩大搜索量和吞吐量,因为不同的搜索请求可以在所有的复制分片中并发执行。
总的来说,索引可以划分在多个分片上。索引也可以被复制0次(也就是说没有复制)或多次。一旦索引被复制,会有一个主分片(被复制的原始分片)和复制分片(主分片的副本)。复制分片的数量可以在创建索引的时候定义。索引被创建后,你可以动态地改变复制分片的数量,但你不能改变原始分片的数量。
默认情况下,每个索引都会被分配5个主分片和1一个复制分片,这意味着如果你的集群中有两个节点,你的索引将会有5个主分片和5个复制分片,总共有10个分片。
每个elasticsaerch分片都是一个Lucene 索引。在单个索引中你最多可以存储2,147,483,519 (= Integer.MAX_VALUE - 128) 个文档。你可以使用 _cat/shards api去监控分片的的大小。
有了这些方法,让我们开始有趣的部分…
安装

elasticsearch最低要求java 8环境。特别在撰写本文的时候,建议你使用orace 1.8.0_121JDK版本,java在不同的平台之间安装有所不同,这里我不会深入介绍他们的细节。oracle 推荐的安装文档在 oracle网站中可以找到。一句话,在你安装elasticsearch之前,请首先运行下面指令检查你的java版本(请根据需要安装或者更新它):
java-version
echo$JAVA_HOME
当java安装好,我们可以下载并运行elasticsearch。所有版本的二进制文件都可以从 www.elastic.co/downloads 下载。你可以选择zip或tar文档或者DEB,RPM包中的任何一个方式安装。为了简化,我们使用tar文件。
使用下面的命令,下载elasticsearch5.0.2tar(windows用户应该下载zip包)
用下面的命令去提取文件
tar -xvfelasticsearch-5.0.2.tar.gz
它会在当前目录创建一系列的文件和文件夹。然后我们进入到bin目录执行:
cdelasticsearch-5.0.2/bin
现在我们已经准备好启动一个具有单个节点的集群
./elasticsearch
如果一切顺利,你将会看到下面的一堆信息:
[2016-09-16T14:17:51,251][INFO][o.e.n.Node               ] []initializing ...
[2016-09-16T14:17:51,329][INFO][o.e.e.NodeEnvironment    ] [6-bjhwl]using [1] data paths, mounts [[/ (/dev/sda1)]], net usable_space [317.7gb], nettotal_space [453.6gb], spins? [no], types [ext4]
[2016-09-16T14:17:51,330][INFO][o.e.e.NodeEnvironment    ] [6-bjhwl]heap size [1.9gb], compressed ordinary object pointers [true]
[2016-09-16T14:17:51,333][INFO][o.e.n.Node               ] [6-bjhwl]node name [6-bjhwl] derived from node ID; set [node.name] to override
[2016-09-16T14:17:51,334][INFO][o.e.n.Node               ] [6-bjhwl]version[5.0.2], pid[21261], build[f5daa16/2016-09-16T09:12:24.346Z],OS[Linux/4.4.0-36-generic/amd64], JVM[Oracle Corporation/Java HotSpot(TM)64-Bit Server VM/1.8.0_60/25.60-b23]
[2016-09-16T14:17:51,967][INFO][o.e.p.PluginsService     ] [6-bjhwl]loaded module [aggs-matrix-stats]
[2016-09-16T14:17:51,967][INFO][o.e.p.PluginsService     ] [6-bjhwl]loaded module [ingest-common]
[2016-09-16T14:17:51,967][INFO][o.e.p.PluginsService     ] [6-bjhwl]loaded module [lang-expression]
[2016-09-16T14:17:51,967][INFO][o.e.p.PluginsService     ] [6-bjhwl]loaded module [lang-groovy]
[2016-09-16T14:17:51,967][INFO][o.e.p.PluginsService     ] [6-bjhwl]loaded module [lang-mustache]
[2016-09-16T14:17:51,967][INFO][o.e.p.PluginsService     ] [6-bjhwl]loaded module [lang-painless]
[2016-09-16T14:17:51,967][INFO][o.e.p.PluginsService     ] [6-bjhwl] loadedmodule [percolator]
[2016-09-16T14:17:51,968][INFO][o.e.p.PluginsService     ] [6-bjhwl]loaded module [reindex]
[2016-09-16T14:17:51,968][INFO][o.e.p.PluginsService     ] [6-bjhwl]loaded module [transport-netty3]
[2016-09-16T14:17:51,968][INFO][o.e.p.PluginsService     ] [6-bjhwl]loaded module [transport-netty4]
[2016-09-16T14:17:51,968][INFO][o.e.p.PluginsService     ] [6-bjhwl]loaded plugin [mapper-murmur3]
[2016-09-16T14:17:53,521][INFO][o.e.n.Node               ] [6-bjhwl]initialized
[2016-09-16T14:17:53,521][INFO][o.e.n.Node               ] [6-bjhwl]starting ...
[2016-09-16T14:17:53,671][INFO][o.e.t.TransportService   ] [6-bjhwl]publish_address {192.168.8.112:9300}, bound_addresses {{192.168.8.112:9300}
[2016-09-16T14:17:53,676][WARN][o.e.b.BootstrapCheck     ] [6-bjhwl]max virtual memory areas vm.max_map_count [65530] likely too low, increase toat least [262144]
[2016-09-16T14:17:56,731][INFO][o.e.h.HttpServer         ] [6-bjhwl]publish_address {192.168.8.112:9200}, bound_addresses {[::1]:9200},{192.168.8.112:9200}
[2016-09-16T14:17:56,732][INFO][o.e.g.GatewayService     ] [6-bjhwl]recovered [0] indices into cluster_state
[2016-09-16T14:17:56,748][INFO][o.e.n.Node               ] [6-bjhwl]started
没有太多的细节,我们看到节点名字是"6-bjhwl"(在你的例子上名字会不一样)的节点已经启动了,并且在单个集群中选取自己作为主节点。这个时候不用知道master是什么意思。重要的是,我们已经在一个集群中启动了一个节点。
对比之前提到的例子,当启动elasticsearch的时候,我们可以通过执行下面的命令,重载集群或者节点的名称。
./elasticsearch-Ecluster.name=my_cluster_name -Enode.name=my_node_name
还要注意一下我们的节点是否能绑定到ip地址(192.168.8.112)和端口(9200)。elasticsearch默认使用端口9200去接入REST API。用户也可以自定义这个端口。
探索你的集群

THE REST API
现在我们已经运行了自己的节点(和集群),下一步就要了解如何和它通讯。很幸运,elasticsearch 提供了一个非常全面并且十分强大的 REST API 让你和集群交互。使用REST API可以完成下面一些事情:

  • 检查你的集群,节点和索引的健康情况,状态和统计
  • 管理你的集群,节点,索引数据和metadata
  • 针对索引执行CURD(创建,读取,更新,删除)和搜索操作
  • 执行高级的搜索操作,例如分页,排序,过滤,聚合,脚本等等。
集群健康(Cluster Health
让我们先从基础的健康检查开始,我们可以使用它去了解我的集群是如何运作的。我们将会使用CURL去做这个实验,不过你也可以使用任何一个支持HTTP/REST的工具调用接口。假如我们继续使用刚刚启动的elasticsearch节点,并打开一个新的命令行窗口。
为了检查集群监控,我们需要使用 _cat API.你可以通过点击下面的"view in console”,在Kibana控制台中运行命令,或者通过点击下面的“copy as curl”并把链接拷贝到你的终端运行。
GET/_cat/health?v
[COPY ASCURL]=curl -XGET 'localhost:9200/_cat/nodes?v&pretty'
[VIEW INCONSOLE]=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_cluster_health/2.json
将会返回结果:
epoch      timestamp cluster       status node.total node.data shards prirelo init unassign pending_tasks max_task_wait_time active_shards_percent
147524770917:01:49  elasticsearch green           1         1     0   0    0   0        0             0                  -                100.0%
可以看到我们集群叫“elasticsearch”,运行状态是green。每当我们查询集群健康情况时,接口可能会返回green,yellow或red状态。green意味着一切良好(集群所有的功能都正常)。yellow意味着所有的数据都是可用的,但是一些复制分片可能没有正确分发(集群的所有功能还是正常的)。red意味着因为某些原因导致有些数据不能使用。注意,即使集群状态是red,它仍然可以运行一部分的功能。(例如,它依然可以从一些可用的分片处理搜索请求)但你应该尽快去修复它,因为这样会使搜索结果丢失一些数据。
参考返回结果,因为没有存储任何数据,我们可以看到集群一共有一个节点和0个分片。注意因为我们使用了默认的集群名称(elasticsearch)并且elasticsearch默认使用单一传播网络去查找同一机器上的其他节点。也就是说,如果你在同一机器上启动超过一个节点,这些节点有可能都加入了同一个集群。在这种情况下,你从返回结果中看到不止一个节点。我们当然也可以通过下面的方法去查询集群的节点数。
GET/_cat/nodes?v
[copy asurl ]=curl -XGET 'localhost:9200/_cat/nodes?v&pretty'
[view inconsole]=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_cluster_health/2.json
其返回结果是:
ip        heap.percent ram.percent cpu load_1mload_5m load_15m node.role master name
127.0.0.1           10           5  5    4.46                        mdi      *     PB2SGZY
可以看到我们的节点叫"PB2SGZY",当前集群只有它一个节点。
列出所有的索引(List All Indices
现在让我们偷偷看一眼我们的索引:
GET/_cat/indices?v
[COPY ASCURL]=curl -XGET 'localhost:9200/_cat/indices?v&pretty'
[VIEW INCONSOLE]=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_list_all_indices/1.json
返回结果:
healthstatus index uuid pri rep docs.count docs.deleted store.size pri.store.size
可以看出当前集群中没有索引。
创建索引(Create an Index
现在让我们创建一个叫“customer”的索引然后再次列出所有的索引:
PUT/customer?pretty
GET/_cat/indices?v
[COPY ASCURLVIEW]=curl -XPUT 'localhost:9200/customer?pretty&pretty'
curl -XGET'localhost:9200/_cat/indices?v&pretty'
[VIEW INCONSOLE]=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_create_an_index/1.json
第一个命令使用PUT方法创建一个叫“customer” 的索引。我们简单地在url添加一个pretty参数,告诉服务器要美化json格式的返回结果。
返回结果:
healthstatus index    uuid                   pri rep docs.countdocs.deleted store.size pri.store.size
yellowopen   customer 95SQ4TSUT7mWBT7VNHH67A   5  1          0            0       260b           260b
第二个命令的结果说明,我们现在有一个叫作“customer”的索引 ,它有5个主分片和一个复制分片,它现在没有储存任何文档。
现在你可能已经注意到customer索引目前的状态是yellow。回忆一下我们之前讨论过的内容,yellow代表有一些复制分片还没有被分发。出现这个问题的原因是elasticsearch默认在索引中创建一个复制分片。因为此时我们仅仅运行一个节点,并且在没有新(后续的)节点加入集群之前,这个复制分片是不可以被分发的(复制分片和主分片不能再同一节点上)。这个复制分片一旦被分配到第二节点中,集群的健康状态将会变成green。
索引和查询文档(Index and Querya Document
现在让我们在customer索引中创建一些文档。回忆之前提过的:为了创建一个文档,我们必须告诉elasticsearch,要把文档保存到索引的哪个类型中。
让我们把一个简单的客户文档索引到customer索引的external类型中,并把文档的id设置为1:
PUT/customer/external/1?pretty
{
"name":"John Doe"
}
[COPY ASCURL]=curl -XPUT 'localhost:9200/customer/external/1?pretty&pretty' -d'
{
"name":"John Doe"
}'
[VIEW INCONSOLE]=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_index_and_query_a_document/1.json
返回结果:
{
    "_index" : "customer",
     "_type" : "external",
     "_id" : "1",
     "_version" : 1,
     "result" : "created",
     "_shards" : {
       "total" : 2,
      "successful" : 1,
       "failed" : 0
    },
     "created" : true
}
从上面的结果可以看出,一个新的客户文档成功被索引到customer索引的extenal类型中,并且我们在索引的时候指定文档的内部id值为1。
值得注意的是elasicsearch并不要求你在索引文档之前,就要显式创建一个索引。在之前的例子,如果customer索引不存在,elasticsearch会自动地创建它。
让我们查询刚刚索引的文档:
GET/customer/external/1?pretty
[COPY ASCURL]=curl -XGET 'localhost:9200/customer/external/1?pretty&pretty'
[VIEW INCONSOLE]=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_index_and_query_a_document/2.json
返回结果:
{
    "_index" : "customer",
    "_type" : "external",
    "_id" : "1",
    "_version" : 1,
    "found" : true,
    "_source" : { "name":"John Doe" }
}
这里比较特殊的是found字段,它说明我们查到了一个id为1的文档,另一特殊的字段_source,保存了在上一个步骤索引的的文档。
删除索引(Delete an Index
现在让我们删除刚刚创建的索引,然后再次列出集群的索引。
DELETE/customer?pretty
GET/_cat/indices?v
COPY ASCURL =curl -XDELETE 'localhost:9200/customer?pretty&pretty'
curl -XGET'localhost:9200/_cat/indices?v&pretty'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_delete_an_index/1.json
返回结果是:
healthstatus index uuid pri rep docs.count docs.deleted store.size pri.store.size
返回结果意味着索引已经被删除了,我们又回到了集群最初时什么都没有的状态。
在继续学习之前,让我们仔细的再看一遍那些已经学会的api命令。
PUT/customer
PUT/customer/external/1
{
  "name": "John Doe"
}
GET/customer/external/1
DELETE/customer
如果我们足够细心的学习,应该会发现数据导入elasticsearch的所使用的模式。这个模式可以总结为:
<RESTVerb> /<Index>/<Type>/<ID>
这种REST 导入模式普遍存在于所有的API命令,如果你可以记住它,那么对于掌握elasticsearch,会是一个很好的开始。
修改你的数据(Modifying YourData

elasticsearch提供接近实时的数据操作和搜索能力。默认情况,从你索引,更新或者删除你的数据到用户可以搜索到新的结果这个过程大概需要1秒(基于refresh 频率)。它们和类似SQL这样的平台不一样,SQL的数据在事务完成后就马上就生效,不会有延迟。
索引/替换文档(Indexing/Replacing Documents
在之前我们已经知道如何创建一个文档。现在我们再次执行那个命令:
PUT /customer/external/1?pretty
{
    "name": "John Doe"
}
COPY ASCURL=curl -XPUT 'localhost:9200/customer/external/1?pretty&pretty' -d'
{
  "name": "John Doe"
}'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_modifying_your_data/1.json
上面的命令将会再次索引这个文档到customer索引的external类型,文档的id值是1。如果我们用不同的文档内容(但ID一样)再次执行上面的命令,elasticsearch将会用一个新的文档取代(即重建索引)旧的文档。
PUT/customer/external/1?pretty
{
  "name": "Jane Doe"
}
COPY AS CURL=curl-XPUT 'localhost:9200/customer/external/1?pretty&pretty' -d'
{
  "name": "Jane Doe"
}'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_modifying_your_data/2.json
上面的操作把id为1的文档的name字段由“john doe”改成“jane doe”。另一方面,如果我使用不同的id执行上述命令,将会创建一个新的文档,旧的文档依然保持不变。
PUT/customer/external/2?pretty
{
  "name": "Jane Doe"
}
COPY ASCURL=curl -XPUT 'localhost:9200/customer/external/2?pretty&pretty' -d'
{
  "name": "Jane Doe"
}'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_modifying_your_data/3.json
以上操作索引了一个新的id为2文档。
索引新文档的时候,id值是可选的。如果没有指定,elasticsearch将会为文档生成一个随机的id。实际生成的id将会保存在调用api接口的返回结果中。
下面的例子展示不指定文档id的时候是如何索引文档的:
POST/customer/external?pretty
{
  "name": "Jane Doe"
}
COPY ASCURL=curl -XPOST 'localhost:9200/customer/external?pretty&pretty' -d'
{
  "name": "Jane Doe"
}'
VIEW INCONSOLE= http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_modifying_your_data/4.json
注意,在上面的例子中,因为没有指定id,我们需要使用POST方法取代之前的PUT方法。
更新文档(UpdatingDocuments
除了索引和替换文档,我们当然也可以更新文档。注意elasticsearch底层实际上并没有更新文档。当我们执行更新操作时,elasticsearch删除旧的文档然后索引新的文档。
这个例子展示如何去更新你的文档,并把name字段改成“Jane doe”:
POST/customer/external/1/_update?pretty
{
  "doc": { "name":"Jane Doe" }
}
COPY ASCURL=curl -XPOST 'localhost:9200/customer/external/1/_update?pretty&pretty'-d'
{
  "doc": { "name":"Jane Doe" }
}'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_updating_documents/1.json
这个例子展示了把name字段改为“Jane Doe”,还添加了一个age字段:
POST/customer/external/1/_update?pretty
{
  "doc": { "name":"Jane Doe", "age": 20 }
}
COPY ASCURL=curl -XPOST 'localhost:9200/customer/external/1/_update?pretty&pretty'-d'
{
  "doc": { "name":"Jane Doe", "age": 20 }
}'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_updating_documents/2.json
也可以使用script执行更新,这个例子使用一个script把age字段增加5:
POST/customer/external/1/_update?pretty
{
  "script" : "ctx._source.age +=5"
}
COPY ASCURL=curl -XPOST 'localhost:9200/customer/external/1/_update?pretty&pretty'-d'
{
  "script" : "ctx._source.age +=5"
}'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_updating_documents/3.json
在上面的例子,ctx.source引用了当前文档。
注意,在写本文档的时候,只能一次更新单个文档。未来,elasticsearh可能会支持通过一个查询条件(就好像SQL的update where那样)同时更新多个文档。
删除文档(DeletingDocuments
删除一个文档是相当简单的。这个例子展示如何删除之前的customer索引的external类型中的一个id为2的文档:
DELETE/customer/external/2?pretty
COPY ASCURL=curl -XDELETE 'localhost:9200/customer/external/2?pretty&pretty'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_deleting_documents/1.json
查阅query api 去删除指定的条件的文档。值得注意的是,删除整个索引比通过query api 删除所有文档更高效。
批处理(Batch Processing
除了可以索引,更新,删除单个文档。elasticsearch 同样可以通过使用 _bulk APi 批量执行以上操作。这个功能十分重要,他会使用一种非常有效的机制去执行多个操作,使其能够尽可能快并且占用减少网络往返次数。
简单举个例子,下面会在一个 bulk操作中索引两个文档:
POST/customer/external/_bulk?pretty
{"index":{"_id":"1"}}
{"name":"John Doe" }
{"index":{"_id":"2"}}
{"name":"Jane Doe" }
COPY ASCURL=curl -XPOST 'localhost:9200/customer/external/_bulk?pretty&pretty' -d'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_batch_processing/1.json
下面的例子会在一个操作内更新第一个文档同时删除第二个文档:
POST/customer/external/_bulk?pretty
{"update":{"_id":"1"}}
{"doc":{ "name": "John Doe becomes Jane Doe" } }
{"delete":{"_id":"2"}}
COPY ASCURL=curl -XPOST 'localhost:9200/customer/external/_bulk?pretty&pretty' -d'
{"update":{"_id":"1"}}
{"doc":{ "name": "John Doe becomes Jane Doe" } }
{"delete":{"_id":"2"}}'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_batch_processing/2.json
注意上面的删除操作没有相应的文档数据,因为删除操作只需要知道文档的id。
批量api会按顺序的执行所有操作。如果某个操作因某些原因执行失败,它会继续执行剩下的操作。api返回结果时,会提供每一个操作的状态(和操作的顺序一致),你可以通过这个状态检查操作是否执行成功。
探索你的数据(Exploring YourData

简单的数据集(Sample Dataset
现在,我们已经学习了一些基础知识。让我们使用更真实的数据做实验。我已经准备好一些虚构的关于客户银行账户信息的json文档。每个文档都有下面的模式:
{
    "account_number": 0,
    "balance": 16623,
    "firstname":"Bradshaw",
    "lastname": "Mckenzie",
    "age": 29,
    "gender": "F",
    "address": "244 ColumbusPlace",
    "employer": "Euron",
    "email":"bradshawmckenzie@euron.com",
    "city": "Hobucken",
    "state": "CO"
}
我从www.json-generator.com上生成这些数据,所以呢,请忽略这些实际的值和数据的语义,因为他们全都是随机生成的。
加载简单的数据集(Loading theSample Dataset
你可以从 这里 下载简单的数据集(accounts.json)。把他复制到当前目录,并使用以下命令加载到集群中:
curl-XPOST 'localhost:9200/bank/account/_bulk?pretty&refresh' --data-binary"@accounts.json"
curl'localhost:9200/_cat/indices?v'
返回结果:
healthstatus index uuid                   prirep docs.count docs.deleted store.size pri.store.size
yellowopen   bank  l7sSYV2cQXmu6_4rJWVIww   5  1       1000            0   128.6kb        128.6kb
这说明了我们刚刚批量索引了1000个文档到bank索引的account类型
搜索apiThe Search API
现在,我们先尝试一些简单的搜索例子。有两种方法执行搜索:第一种是通过REST request Uri发送搜索参数,第二种是通过REST request body。第二种方法允许你用json格式定义更丰富的查询参数。我们将会用第一种方法做一个例子,但之后都会专一地使用第二种方法做实验。
搜索api的接口是_search.这个例子将会返回bank索引的所有文档。
GET/bank/_search?q=*&sort=account_number:asc
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?q=*&sort=account_number:asc&pretty'
VIEW INCONSOLE=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,q=*说明要匹配索引的所有文档,pretty参数要求接口返回格式化的json数据。
返回结果如下:
{
  "took" : 63,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "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":"244ColumbusPlace","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":"880Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}
    }, ...
  }
}
对于返回结果,我们可以看出以下几点:

  • took-elasticsearch执行搜索花费的毫秒数
  • timed_out-告诉我们这次搜索是否超时
  • _shards- 告诉我们搜索了多少个分片,以及搜索成功和失败的分片数
  • hits-搜索返回的文档结果
  • hits.total 一共命中了多少结果
  • sort-搜索排序规则,如果没有该字段,则按相关度排序
  • _scoremax_score 暂时先忽略这个参数(文档得分,反映相关度)
下面用request body方法执行和上面一样的搜索操作:
GET/bank/_search
{
  "query": { "match_all":{} },
  "sort": [
    { "account_number":"asc" }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": { "match_all":{} },
  "sort": [
    { "account_number":"asc" }
}'
VIEW INCONSOLE= 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格式的请求体代替了_search api uri中的q=*参数。我们将会在下一个章节讨论json查询格式。
必须要知道的是:当我们接收到返回结果的时候,elasticsearch已经完全处理了这个请求,不会维护任何的服务器资源或游标到你的返回结果中。这与类似sql这样的平台形成鲜明的对比,sql允许你先取前面的一部分数据,然后连续不断的通过服务器端的游标再去取剩下的数据。
介绍查询语言(Introducing theQuery Language
elasticsearch支持一种json风格的特定领域语言去执行查询。这种查询语言是十分全面和吓人的(在你刚开始接触的时候)。使用一些简单的例子学习,是一种比较好的方法。
回想上一个例子,我们执行过下面的查询:
GET/bank/_search
{
  "query": { "match_all":{} }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": { "match_all":{} }
}'
VIEW INCONSOLE=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是执行查询操作的匹配类型。match_all是匹配所有文档的意思。
除了query参数,我们也可以传递其他参数去影响查询结果。在上一章节的例子我们传递了一个sort参数,这里我们传递一个size参数:
GET/bank/_search
{
  "query": { "match_all":{} },
  "size": 1
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": { "match_all":{} },
  "size": 1
}'
VIEW INCONSOLE=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.
下面这个例子匹配所有文档,并返回第11-20的文档:
GET/bank/_search
{
  "query": { "match_all":{} },
  "from": 10,
  "size": 10
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": { "match_all":{} },
  "from": 10,
  "size": 10
}'
VIEW INCONSOLE=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参数指定从第几个文档开始返回,size参数指定一共返回多少个文档。这个特性是对分页功能是十分重要的。如果from没有指定,默认值是0。
下面的例子按账户余额倒序查询前10个结果(默认size大小):
GET/bank/_search
{
  "query": { "match_all":{} },
  "sort": { "balance": {"order": "desc" } }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": { "match_all":{} },
  "sort": { "balance": {"order": "desc" } }
}'
VIEW INCONSOLE=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
执行查询(ExecutingSearches
现在我已经看见过一些基础的查询参数,让我们挖掘更多关于query DSL 的信息。首先看看返回文档的字段。默认所有的字段都会被返回。文档原始内容被称为源(对应hits中的_source 键)。如果我们不想返回文档的所有字段,也可以仅仅要求接口返回部分字段。
下面的例子展示如何返回 account_number 和balance(在_source里面)这个两个字段:
GET/bank/_search
{
  "query": { "match_all":{} },
  "_source":["account_number", "balance"]
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": { "match_all":{} },
  "_source":["account_number", "balance"]
}'
VIEW INCONSOLE=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字段还是存在的,只是仅返回account_number和balance字段
如果你曾经学过sql,上述概念就和sql select filed list from差不多。
现在,让我们继续学习查询语法。之前,我们已经看到过match_all查询类型使用来匹配所有文档的。现在,我们介绍一种新的查询类型match,他是基于字段搜索的(即,通过匹配一个特定的字段或一组字段执行搜索)
下面的例子返回账号number是20的文档:
GET/bank/_search
{
  "query": { "match": {"account_number": 20 } }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": { "match": {"account_number": 20 } }
}'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_searches/2.json
下面的例子返回所有地址字段包含mill的账户文档
GET/bank/_search
{
  "query": { "match": {"address": "mill" } }
}
COPY ASCURL=GET /bank/_search
{
  "query": { "match": {"address": "mill" } }
}
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_searches/3.json
下面的例子返回地址字段包含“mill”或者“lane”的账户
GET/bank/_search
{
  "query": { "match": {"address": "mill lane" } }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": { "match": {"address": "mill lane" } }
}'
VIEW INCONSOLE=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),它返回所有地址字段包含词组“mill lane”的账户
GET/bank/_search
{
  "query": {"match_phrase": { "address": "mill lane" } }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": {"match_phrase": { "address": "mill lane" } }
}'
VIEW INCONSOLE=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查询允许我们把多个match查询合并到一个查询中。
下面的例子合并了两个match查询并返回所有地址字段同时包含“mill”和“lane”的账户
GET/bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": {"address": "mill" } },
        { "match": {"address": "lane" } }
    }
  }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": {"address": "mill" } },
        { "match": {"address": "lane" } }
    }
  }
}'
VIEW INCONSOLE=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子句指定:当所有的查询都返回true的时候,才认为匹配文档。
与此相反,下面这个这个例子合并两个match查询,并返回所有地址地段包含“mill”或“lane”的账户
GET/bank/_search
{
  "query": {
    "bool": {
      "should": [
        {"match": { "address": "mill" } },
        { "match": {"address": "lane" } }
    }
  }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": {
    "bool": {
      "should": [
        { "match": {"address": "mill" } },
        { "match": {"address": "lane" } }
    }
  }
}'
VIEW INCONSOLE=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查询,并返回所有地址字段既不包含“mill”也不包含“lane”的账户:
GET/bank/_search
{
  "query": {
    "bool": {
      "must_not": [
        { "match": {"address": "mill" } },
        { "match": {"address": "lane" } }
    }
  }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": {
    "bool": {
      "must_not": [
        { "match": {"address": "mill" } },
        { "match": {"address": "lane" } }
    }
  }
}'
VIEW INCONSOLE=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子句指定当所有查询都不满足的时候,就认为匹配文档。
我们也可以把must,should,must_not同时组合到bool子句。此外,我们也可以组合bool 到任何一个bool子句中,实现复杂的多层bool子句嵌套逻辑。
下面的例子返回所有年龄是40岁但不是住在ID州的账户:
GET/bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age":"40" } }
      ],
      "must_not": [
        { "match": {"state": "ID" } }
    }
  }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age":"40" } }
      ],
      "must_not": [
        { "match": {"state": "ID" } }
    }
  }
}'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_searches/9.json
执行过滤(ExecutingFilters
在之前的章节,我们跳过了一个小细节:文档得分(搜索结果的_score字段).文档得分是一个数字值,它代表关键字和文档内容的相关度估值。 文档得分越高说明相关度越高,文档得分越少,说明相关度越少。
但查询不一定都需要产生文档得分,特别在过滤文档集合的时候。为了避免不必要的文档得分计算,Elasticsearch会检查这种情况并自动的优化这种查询。
在前面章节介绍的bool查询也支持filter子句,它允许你使用一个查询语句去过滤其它子句的匹配结果,同时不会改变文档的得分。我们介绍一下range查询,并把它作为例子,它允许我们通过一个范围值去过滤文档。通常用于数字或日期过滤。
这个例子使用bool查询返回所有账户余额在20000-30000之间的文档。换句话说,我们需要查找余额大于20000小于30000的账户:
GET/bank/_search
{
  "query": {
    "bool": {
      "must": {"match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": {
    "bool": {
      "must": {"match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}'
VIEW INCONSOLE=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查询(过滤部分)。我们也可以用任何其它的查询语句代替查询和过滤部分的语句。对于上面的例子,因为所有文档都是指定范围之内的,他们从某种意义上来说是等价的(equally),即他们的相关度都是一样的(filter子句查询,不会改变得分)。
除了 match_all,match,bool,range查询,还有很多种类的查询,但我们不在这里一一介绍。从现在开始,我们对查询已经有一个基础的了解,把学到的知识应用到其他查询类型应该也没什么难度。
执行聚合(ExecutingAggregations
聚合功能能够分组并统计你的数据。最简单的说法就是等价于sql group by 语句和sql的聚合函数。使用elasticsearch,你可以同时在一个请求中返回需要查询的数据以及这些数据的多种聚合运算结果。在单个请求中就可以同时查询数据和进行多次聚合运算是非常有意义的,他可以降低网络请求的次数。
下面的例子把state字段的内容分组,并按照每一组的文档数量倒序排序,返回数量最多的前10(默认值)组数据:
GET/bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      }
    }
  }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field":"state.keyword"
      }
    }
  }
}'
VIEW INCONSOLE=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:
SELECTstate, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC
返回结果(仅展示一部分):
{
  "took": 29,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "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州有27个账户,随后的TX州有27个账户,AL州有25个账户。
注意我们设置size=0是因为我不需要查询文档,只需要查询聚合结果。
基于上述例子,下面的例子除了分组还会计算每个州的账户的平均余额:
GET/bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field":"state.keyword"
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field":"balance"
          }
        }
      }
    }
  }
}
  COPY AS CURL=curl -XGET'localhost:9200/bank/_search?pretty' -d'
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field":"state.keyword"
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field":"balance"
          }
        }
      }
    }
  }
}'
VIEW INCONSOLE=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聚合中。这种模式适合于所有的聚合。你可以按你的需求重复嵌套聚合子句,汇总你的数据。
基于上面的例子,我们加入了按每个州的账户平均余额倒序排序的限制(说明了上一层聚合可以使用下一层聚合的运算结果):
GET/bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field":"state.keyword",
        "order": {
          "average_balance":"desc"
        }
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field":"balance"
          }
        }
      }
    }
  }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field":"state.keyword",
        "order": {
          "average_balance":"desc"
        }
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field":"balance"
          }
        }
      }
    }
  }
}'
VIEW INCONSOL=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),然后按性别分组,最后获取每个组中每个性别的账户平均余额。(例如:年龄段在20-29的女性用户的账户平均余额)
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"
              }
            }
          }
        }
      }
    }
  }
}
COPY ASCURL=curl -XGET 'localhost:9200/bank/_search?pretty' -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"
              }
            }
          }
        }
      }
    }
  }
}'
VIEW INCONSOLE=http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_executing_aggregations/4.json
还有很多聚合特性我们不在这里详细讨论,如果你想做更多的实验,你可以去查看聚合参考指南
结论(Conclusion
elasticsearch是一个既简单又复杂的产品。目前为止,我们已经学了如何使用REST api和elasticsearch的基本概念和特性。我希望这个教程可以让你很好的理解elasticsearch,更重要的是,激励你继续学习后续教程要介绍的强大特性。

收藏回复 只看该作者 道具 举报

高级模式
B Color Image Link Quote Code Smilies



QQ|小黑屋| 码途山海.智隐长卷 渝ICP备15002301号-2   渝公网安备50011202504426

GMT+8, 2025-5-18 07:38 , Processed in 0.046931 second(s), 22 queries .

©Copyright 程序人生!

©2012-2015重庆纽新

快速回复 返回顶部 返回列表