Elasticsearch是一个高度可伸缩的开源全文搜索和分析引擎。它容许你以近实时的方式快速存储、搜索和分析大量的数据。它一般被用做基础的技术来赋予应用程序复杂的搜索特性和需求。html
这里列举了几个Elasticsearch能够用来作的功能例子java
在剩余的本教程中,你将会被引导去把Elasticsearch安装好并运行起来,而且对它有一个简单的了解。使用基础的操做好比索引,搜索以及修改你的数据。当你完成这个教程的时候,你应该对Elasticsearch有了一个不错的了解,它是怎么工做的,而且咱们但愿你可以使用它构建出更加复杂精致的搜索应用来挖掘你的数据里的价值。node
这里有一些Elasticsearch的核心概念。在一开始理解这些概念将会极大的使你的学习过程变得更加轻松。git
Elasticsearch是一个近实时的搜索平台。这意味着当你导入一个文档并把它变成可搜索的时间仅会有轻微的延时。github
一个集群是由一个或多个节点(服务器)组成的,经过全部的节点一块儿保存你的所有数据而且提供联合索引和搜索功能的节点集合。每一个集群有一个惟一的名称标识,默认是“elasticsearch”。这个名称很是重要,由于一个节点(Node)只有设置了这个名称才能加入集群,成为集群的一部分。shell
确保你没有在不一样的环境下重用相同的名称,不然你最终可能会将节点加入错误的集群。例如你可使用logging-dev,logging-stage和logging-prod来分别给开发,展现和生产集群命名。数据库
注意,一个集群中只有一个节点是有效的而且是很是好的。因此这样的话,你可能须要部署多个集群而且每一个集群有它们惟一的集群名称。express
一个节点是一个单一的服务器,是你的集群的一部分,存储数据,而且参与集群的索引和搜索功能。跟集群同样,节点在启动时也会被分配一个惟一的标识名称,这个名称默认是一个随机的UUID(Universally Unique IDentifier)。若是你不想用默认的名称,你能够本身定义节点的名称。这个名称对于管理集群节点,识别哪台服务器对应集群中的哪一个节点有重要的做用。json
一个节点能够经过配置特定的集群名称来加入特定的集群。默认状况下,每一个节点被设定加入一个名称为“elasticsearch”的集群,这意味着若是你在你的网络中启动了一些节点,而且假设它们能相互发现,它们将会自动组织并加入一个名称是“elasticsearch”的集群。windows
在一个集群中,你想启动多少节点就能够启动多少节点。此外,若是没有其它节点运行在当前网络中,只启动一个节点将默认造成一个新的名称为“elasticsearch”单节点集群。
一个索引就是含有某些类似特性的文档的集合。例如,你能够有一个用户数据的索引,一个产品目录的索引,还有其余的有规则数据的索引。一个索引被一个名称(必须都是小写)惟一标识,而且这个名称被用于索引经过文档去执行索引,搜索,更新和删除操做。
在一个集群中,你能够根据本身的需求定义任意多的索引。
警告!Type在6.0.0版本中已经不同意使用
一个类型是你的索引中的一个分类或者说是一个分区,它可让你在同一索引中存储不一样类型的文档,例如,为用户建一个类型,为博客文章建另外一个类型。如今已不可能在同一个索引中建立多个类型,而且整个类型的概念将会在将来的版本中移除。查看“映射类型的移除[https://www.elastic.co/guide/...]”了解更多。
一个文档是一个可被索引的数据的基础单元。例如,你能够给一个单独的用户建立一个文档,给单个产品建立一个文档,以及其余的单独的规则。这个文档用JSON格式表现,JSON是一种广泛的网络数据交换格式。
在一个索引或类型中,你能够根据本身的需求存储任意多的文档。注意,虽然一个文档在物理存储上属于一个索引,可是文档实际上必须指定一个在索引中的类型。
咱们在一个索引里存储的数据,潜在的状况下可能会超过单节点硬件的存储限制。例如,单个索引有上千万个文档须要占用1TB的硬盘存储空间,可是一台机器的硬盘可能没有这么大,或者是即使有这么大,可是单个节点在提供搜索服务时会响应缓慢。
为了解决这个问题,Elasticsearch提供了分片的能力,它能够将你的索引细分红多个部分。当你建立一个索引的时候,你能够简单的定义你想要的分片的数量。每一个分片自己是一个全功能的彻底独立的“索引”,它能够部署在集群中的任何节点上。
分片对于如下两个主要缘由很重要:
一个分片是如何被分配以及文档又是如何被汇集起来以应对搜索请求的,它的实现技术由Elasticsearch彻底管理,而且对用户是透明的。
在一个网络环境下或者是云环境下,故障可能会随时发生,有一个故障恢复机制是很是有用而且是高度推荐的,以防一个分片或节点不明缘由下线,或者由于一些缘由去除没有了。为了达到这个目的,Elasticsearch容许你制做分片的一个或多个拷贝放入一个叫作复制分片或短暂复制品中。
复制对于如下两个主要缘由很重要:
总结一下,每一个索引能够被切分红多个分片,一个索引能够被复制零次(就是没有复制)或屡次。一旦被复制,每一个索引将会有一些主分片(就是那些最原始不是被复制出来的分片),还有一些复制分片(就是那些经过复制主分片获得的分片)。
主分片和复制分片的数量能够在索引被建立时指定。索引被建立后,你能够随时动态修改复制分片的数量,可是不能修改主分片的数量。
默认状况下,在Elasticsearch中的每一个索引被分配5个主分片和一份拷贝,这意味着假设你的集群中至少有两个节点,你的索引将会有5个主分片和5个复制分片(每一个主分片对应一个复制分片,5个复制分片组成一个完整拷贝),总共每一个索引有10个分片。
每一个Elasticsearch分片是一个Lucene索引。在一个Lucene索引中有一个文档数量的最大值。截至LUCENE-5843,这个限制是2,147,483,519 (= Integer.MAX_VALUE - 128)个文档。你可使用_cat/shards API监控分片大小。
如今熟悉了概念以后,让咱们开始有趣的部分吧...
Elasticsearch须要至少Java 8。明确的说,截至本文写做时,推荐使用Oracle JDK 1.8.0_131版本。Java的安装在不一样的平台下是不同,因此在这里就再也不详细介绍。你能够在Oracle官网找到官方推荐的安装文档。因此说,当你在安装Elasticsearch以前,请先经过如下命令检查你的Java版本(而后根据须要安装或升级)。
java -version
echo $JAVA_HOME
一旦Java准备就绪,而后咱们就能够下载并运行Elasticsearch。咱们能够从这个页面http://www.elastic.co/downloads 获取全部发行版本的二进制安装包。每个版本都对应有zip和tar压缩包,还有deb和rpm安装包,还有Windows下用的msi安装包。
为了简单,让咱们使用tar包来安装。
使用以下命令下载Elasticsearch 6.1.1的tar包:
curl -L -O https://artifacts.elastic.co/...
使用以下命令解压:
tar -xvf elasticsearch-6.1.1.tar.gz
上述操做将会在你的当前目录下建立不少文件和文件夹。而后咱们经过以下命令进入bin目录:
cd elasticsearch-6.1.1/bin
接下来咱们就能够启动咱们的单节点集群:
./elasticsearch
在macOS上,咱们能够经过Homebrew来安装Elasticsearch:
brew install elasticsearch
对于Windows用户,咱们推荐使用MSI安装包进行安装。这个安装包使用图形用户界面来引导你进行安装。
首先,从这里https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.1.1.msi下载Elasticsearch 6.1.1的MSI安装包。
而后双击下载好的安装包文件启动图形化安装程序,在第一个界面,选择安装目录:
而后选择是否将Elasticsearch安装为一个系统服务,为了和用tar包安装示例保持一致,咱们选择不安装为系统服务,根据本身须要手动启动:
而后到了配置页面,这里就简单的使用默认的配置值:
进入插件安装页面,一样为了跟tar包安装示例保持一致,将全部的选择去掉,不安装任何插件:
而后点击安装按钮,Elasticsearch将会被安装:
默认状况下,Elasticsearch将会被安装在%PROGRAMFILES%\Elastic\Elasticsearch
。进入这个目录而且切换到bin目录下:
使用命令提示符:
cd %PROGRAMFILES%ElasticElasticsearchbin
使用PowerShell:
cd $env:PROGRAMFILESElasticElasticsearchbin
接下来咱们就能够启动咱们的单节点集群了:
.elasticsearch.exe
若是安装过程顺利的话,你应该会看到以下的信息输出:
[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], net total_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[6.1.1], 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-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] loaded module [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 to at 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)。不用担忧此时的master是什么意思。这里咱们主要关心的重点是咱们启动了一个单节点的集群。
在前面咱们提到过,咱们能够覆盖集群或者是节点的名称。这个操做能够经过以下方式启动Elasticsearch完成。
./elasticsearch -Ecluster.name=my_cluster_name -Enode.name=my_node_name
还有就是经过上面启动时的输出信息咱们能够看到,咱们能够经过IP地址(192.168.8.112
)和端口号(9200
)来访问咱们的节点。默认状况下,Elasticsearch使用9200
端口提供REST API访问。这个端口能够根据须要自定义。
注意!为安全起见,Elasticsearch被设置为不容许使用root用户运行。因此运行以前首先须要建立新用户并赋予权限。
既然咱们的节点(集群)已经安装成功而且已经启动运行,那么下一步就是去了解如何去操做它。幸运的是,Elasticsearch提供了很是全面和强大的REST API,咱们能够经过它去跟集群交互。经过API咱们能够完成以下的功能:
让咱们从一个简单的健康检查开始,经过这个咱们能够了解咱们集群的运行状况。咱们将使用curl工具来作这个测试,固然你可使用任何能够发送HTTP/REST请求的工具。让咱们假设咱们依然在以前已启动的Elasticsearch节点上而且打开了另外一个shell窗口。
咱们将使用 _cat API
去检查集群健康状态。HTTP请求内容为:
GET /_cat/health?v
你能够经过点击VIEW IN Console在Kibana Console中运行命令,或者直接执行以下curl命令:
curl -XGET 'localhost:9200/_cat/health?v&pretty'
响应结果为:
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”的集群正在运行,状态标识为green
。
不管什么时候查看集群健康状态,咱们会获得green
、yellow
、red
中的任何一个。
注意:当一个集群处于red状态时,它会经过可用的分片继续提供搜索服务,可是当有未分配的分片时,你须要尽快的修复它。
另外,从上面的返回结果中咱们能够看到,当咱们里面没有数据时,总共有1个节点,0个分片。注意当咱们使用默认的集群名称(elasticsearch)而且当Elasticsearch默认使用单播网络发如今同一台机器上的其它节点时,极可能你会在你电脑上不当心启动不止一个节点而且他们都加入了一个集群。在这种状况下,你可能会从上面的返回结果中看到不止一个节点。
咱们也能够经过以下请求获取集群中的节点列表:
Http请求体
GET /_cat/nodes?v
Curl命令
curl -XGET 'localhost:9200/_cat/nodes?v&pretty'
Kibana Console
http://localhost:5601/app/kibana#/dev_tools/console?load_from= https://www.elastic.co/guide/...
返回结果为:
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 mdi * PB2SGZY
这里,咱们能够看到咱们的一个节点名称叫作“PB2SGZY”,它是目前咱们集群中的惟一的节点。
如今让咱们来大概看一看咱们的索引:
Http请求内容:
GET /_cat/indices?v
Curl命令
curl -XGET 'localhost:9200/_cat/indices?v&pretty'
Kibana Console
http://localhost:5601/app/kibana#/dev_tools/console?load_from= https://www.elastic.co/guide/...
获得的返回结果为:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
这个返回结果只是一个表头,简单说就是咱们的集群中尚未任何索引。
如今让咱们建立一个索引,名称为“customer”,而后再一次列出全部的索引:
Http请求内容:
PUT /customer?prettyGET /_cat/indices?v
Curl命令
curl -XPUT 'localhost:9200/customer?pretty&pretty'curl -XGET 'localhost:9200/_cat/indices?v&pretty'
Kibana Console
http://localhost:5601/app/kibana#/dev_tools/console?load_from= https://www.elastic.co/guide/...
第一个命令使用PUT
方法建立了一个名为“customer”的索引。咱们简单的在请求后面追加pretty
参数来使返回值以格式化过美观的JSON输出(若是返回值是JSON格式的话)。
而后它的返回结果为:
第一个命令: { "acknowledged" : true, "shards_acknowledged" : true, "index" : "customer" } 第二个命令: health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open customer 95SQ4TSUT7mWBT7VNHH67A 5 1 0 0 260b 260b
第二个命令的返回结果告诉咱们,咱们如今有1个名称为“customer”的索引,而且有5个主分片和1个拷贝(默认状况),而且里面包含0个文档。
你可能也注意到,这个customer索引的健康状态是yellow,回忆咱们以前讨论过的,yellow的意思是有一些拷贝尚未被分配。索引起生这种状况的缘由是Elasticsearch默认为当前索引建立一个拷贝。可是当前咱们只启动了一个节点,这个拷贝直到一段时间后有另外一个节点加入集群以前,不会被分配(为了高可用,拷贝不会与索引分配到同一个节点上)。一旦拷贝在第二个节点上得到分配,这个索引的健康状态就会变成green。
如今让咱们往customer索引中放点东西。以下请求将一个简单的顾客文档放入customer索引中,这个文档有一个ID为1:
Http请求内容:
PUT /customer/doc/1?pretty { "name": "John Doe" }
Curl命令
curl -XPUT 'localhost:9200/customer/doc/1?pretty&pretty' -H 'Content-Type: application/json' -d '{"name": "John Doe"}'
Kibana Console
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" : "doc", "_id" : "1", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 0, "_primary_term" : 1 }
从上面咱们能够看到,一个新的顾客文档已经在customer索引中成功建立。同时这个文档有一个本身的id,这个id就是咱们在将文档加入索引时指定的。
这里有一个重要的注意点,你不须要在将一个文档加入一个索引前明确的将这个索引预先建立好。在上面咱们建立文档的例子中,若是这个customer索引事先不存在,Elasticsearch会自动建立customer索引。
如今让咱们获取刚刚加入索引的文档:
Http请求体:
GET /customer/doc/1?pretty
Curl命令
curl -XGET 'localhost:9200/customer/doc/1?pretty&pretty'
Kibana Console
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" : "doc", "_id" : "1", "_version" : 1, "found" : true, "_source" : { "name": "John Doe" } }
这里没有什么不寻常的,除了一个属性found
,这个found属性表示咱们经过请求ID为1发现了一个文档,还有另外一个属性_source
,_source属性返回咱们在上一步中加入索引的完整JSON文档内容。
如今让咱们删除刚刚建立的索引而且再次列出全部的索引:
Http请求内容:
DELETE /customer?pretty GET /_cat/indices?v
Curl命令
curl -XDELETE 'localhost:9200/customer?pretty&pretty' curl -XGET 'localhost:9200/_cat/indices?v&pretty'
Kibana Console
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
第一个命令的返回结果为:
{ "acknowledged" : true }
第二个命令的返回结果为:
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中访问数据的模式。这个模式能够总结为如下形式:
<REST Verb> /<Index>/<Type>/<ID>
这种REST访问模式遍及全部的API命令,若是简单的记住它,你将会在掌握Elasticsearch的过程当中有一个很好的开端。
以下是我在上述章节实际作的操做:
[root@bogon elasticsearch-6.1.1]# curl -XGET 'localhost:9200/_cat/health?v&pretty' epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent 1514269983 14:33:03 elasticsearch green 1 1 0 0 0 0 0 0 - 100.0% [root@bogon elasticsearch-6.1.1]# curl -XGET 'localhost:9200/_cat/health?v&pretty' epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent 1514270109 14:35:09 elasticsearch green 1 1 0 0 0 0 0 0 - 100.0% [root@bogon elasticsearch-6.1.1]# curl -XGET 'localhost:9200/_cat/nodes?v&pretty' ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name 127.0.0.1 7 93 0 0.00 0.01 0.05 mdi * sEicoNR [root@bogon elasticsearch-6.1.1]# curl -XGET 'localhost:9200/_cat/indices?v&pretty' health status index uuid pri rep docs.count docs.deleted store.size pri.store.size [root@bogon elasticsearch-6.1.1]# curl -XPUT 'localhost:9200/customer?pretty&pretty' { "acknowledged" : true, "shards_acknowledged" : true, "index" : "customer" } [root@bogon elasticsearch-6.1.1]# curl -XGET 'localhost:9200/_cat/indices?v&pretty' health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open customer Azxs-a4FQnGKgAj0zdWXxQ 5 1 0 0 1.1kb 1.1kb [root@bogon elasticsearch-6.1.1]# curl -XPUT 'localhost:9200/customer/doc/1?pretty&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 } [root@bogon elasticsearch-6.1.1]# curl -XGET 'localhost:9200/customer/doc/1?pretty&pretty' { "_index" : "customer", "_type" : "doc", "_id" : "1", "_version" : 1, "found" : true, "_source" : { "name" : "John Doe" } } [root@bogon elasticsearch-6.1.1]# curl -XDELETE 'localhost:9200/customer?pretty&pretty' { "acknowledged" : true } [root@bogon elasticsearch-6.1.1]# curl -XGET 'localhost:9200/_cat/indices?v&pretty' health status index uuid pri rep docs.count docs.deleted store.size pri.store.size [root@bogon elasticsearch-6.1.1]#
Elasticsearch提供了近实时的数据操做和搜索能力。默认状况下,从你开始索引/更新/删除你的数据到出现搜索结果的时间会有一秒的延时(刷新间隔)。这个与其它的SQL数据库平台在一个事务完成后当即获取到数据这一点上有很大的优点。
咱们以前已经看过如何将一个文档放入索引中,让咱们再次回忆一下那个命令:
Http请求:
PUT /customer/doc/1?pretty { "name": "John Doe" }
curl命令:
curl -XPUT 'localhost:9200/customer/doc/1?pretty&pretty' -H 'Content-Type: application/json' -d' { "name": "John Doe" } '
Kibana Console:
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
再次补充说明一下,上面的请求将会将一个ID为1的文档加入customer索引。若是咱们再次执行上面的请求,以相同的文档内容或者是不一样的,Elasticsearch将会用这个新文档替换以前的文档(就是以相同的ID从新
加入索引)。
Http请求内容:
PUT /customer/doc/1?pretty { "name": "Jane Doe" }
curl命令:
curl -XPUT 'localhost:9200/customer/doc/1?pretty&pretty' -H 'Content-Type: application/json' -d' { "name": "Jane Doe" } '
Kibana Console:
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,这样的话将会建立一个新的文档,而以前的文档仍是保持原样。
Http请求:
PUT /customer/doc/2?pretty { "name": "Jane Doe" }
curl命令:
curl -XPUT 'localhost:9200/customer/doc/2?pretty&pretty' -H 'Content-Type: application/json' -d' { "name": "Jane Doe" } '
Kibana Console:
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,而后使用它去索引文档。实际Elasticsearch生成的ID(或者是咱们明确指定的)将会在API调用成功后返回。
以下这个例子演示如何使用隐式的ID将一个文档加入索引:
Http请求:
POST /customer/doc?pretty { "name": "Jane Doe" }
curl命令:
curl -XPOST 'localhost:9200/customer/doc?pretty&pretty' -H 'Content-Type: application/json' -d' { "name": "Jane Doe" } '
Kibana Console:
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
来发送请求。
除了可以新增和替换文档,咱们也能够更新文档。注意虽然Elasticsearch在底层并无真正更新文档,而是当咱们更新文档时,Elasticsearch首先去删除旧的文档,而后加入新的文档。
以下的例子演示如何去更新咱们的以前ID为1的文档,在这里将name属性改成“Jane Doe”:
Http请求内容:
POST /customer/doc/1/_update?pretty { "doc": { "name": "Jane Doe" } }
curl命令:
curl -XPOST 'localhost:9200/customer/doc/1/_update?pretty&pretty' -H 'Content-Type: application/json' -d' { "doc": { "name": "Jane Doe" } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_updating_documents/1.json
以下示例演示如何更新咱们以前ID为1的文档,修改name属性为“Jane Doe”,并同时添加新的age属性:
Http请求内容:
POST /customer/doc/1/_update?pretty { "doc": { "name": "Jane Doe", "age": 20 } }
curl命令:
curl -XPOST 'localhost:9200/customer/doc/1/_update?pretty&pretty' -H 'Content-Type: application/json' -d' { "doc": { "name": "Jane Doe", "age": 20 } } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_updating_documents/2.json
更新操做也可使用简单的脚原本执行。以下的示例使用一个脚本将age增长了5:
Http请求:
POST /customer/doc/1/_update?pretty { "script" : "ctx._source.age += 5" }
curl命令:
curl -XPOST 'localhost:9200/customer/doc/1/_update?pretty&pretty' -H 'Content-Type: application/json' -d' { "script" : "ctx._source.age += 5" } '
Kibana Console:
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
指代的是当前须要被更新的source文档。
Elasticsearch提供了一次更新多个文档的功能,经过使用查询条件(好比SQL的UPDATE-WHERE语句)。详情查看docs-update-by-query API
删除一个文档操做至关的直截了当。以下的示例演示了如何删除咱们以前ID为2的文档:
Http请求内容:
DELETE /customer/doc/2?pretty
curl命令:
curl -XDELETE 'localhost:9200/customer/doc/2?pretty&pretty'
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_deleting_documents/1.json
查看_delete_by_query API去删除匹配特定条件的全部的文档。有一个值得注意的的地方是,直接删除整个索引比经过Query API删除索引中的全部文档更高效。
除了在单个文档上执行索引,更新和删除操做外,Elasticsearch还提供了批操做的功能,经过使用 _bulk
API完成。这个功能很是重要,由于它提供了一种很是高效的机制去经过更少的网络切换尽量快的执行多个操做。
做为一个快速入门示例,以下请求在一个批操做中建立了两个文档:
Http请求内容:
POST /customer/doc/_bulk?pretty {"index":{"_id":"1"}} {"name": "John Doe" } {"index":{"_id":"2"}} {"name": "Jane Doe" }
curl命令:
curl -XPOST 'localhost:9200/customer/doc/_bulk?pretty&pretty' -H 'Content-Type: application/json' -d' {"index":{"_id":"1"}} {"name": "John Doe" } {"index":{"_id":"2"}} {"name": "Jane Doe" } '
Kibana Console:
http://localhost:5601/app/kibana#/dev_tools/console?load_from=https://www.elastic.co/guide/en/elasticsearch/reference/current/snippets/_batch_processing/1.json
以下的示例在一个批操做中首先更新ID为1的文档,而后删除ID为2的文档:
Http请求内容:
POST /customer/doc/_bulk?pretty {"update":{"_id":"1"}} {"doc": { "name": "John Doe becomes Jane Doe" } } {"delete":{"_id":"2"}}
curl命令:
curl -XPOST 'localhost:9200/customer/doc/_bulk?pretty&pretty' -H 'Content-Type: application/json' -d' {"update":{"_id":"1"}} {"doc": { "name": "John Doe becomes Jane Doe" } } {"delete":{"_id":"2"}} '
Kibana Console:
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便可,不须要指定对应的source内容。
批处理API不会由于单条指令失败而所有失败。若是里面有单条指令由于一些缘由失败了,那么整个批处理还会继续执行它后面剩余的指令。当批处理API返回时,它会提供每条指令的执行状态(以发送时的顺序),以便你能够检查一个特定的指令是否失败。
既然咱们已经了解了基础知识,让咱们来尝试操做一些更真实的数据集。我已经预先准备好了一些虚拟的顾客银行帐户信息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生成的。全部请忽略这些数据值的实际意义,由于它们都是随机生成的。
你能够从这里下载样本数据集(accounts.json)。把它放到咱们当前的目录下,而后使用以下的命令把它加载到咱们得集群中:
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类型下)。
如今,让咱们从一些简单的搜索指令开始。执行搜索有两种基础的方式,一种是在请求的URL中加入参数来实现,另外一种方式是将请求内容放到请求体中。使用请求体可让你的JSON数据以一种更加可读和更加富有展示力的方式发送。咱们将会在一开始演示一次使用请求URI的方式,而后在本教程剩余的部分,咱们将统一使用请求体的方式发送。
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:
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
还有不少其它的聚合功能在这里咱们就不去详细介绍了。若是你想了解更多,能够参考聚合参考手册。
Elasticsearch是一个既简单又复杂的产品。咱们到目前为止已经学习了基础的知识,知道了它是什么,它内部的实现原理,以及如何使用REST API去操做它。但愿此教程能帮助你理解Elasticsearch以及更重要的东西,鼓励你去实践它剩余的更多的特性!