Elasticsearch 5.4.0 用戶指南

博客原文一
博客原文二

翻譯作品,水平有限,如有錯(cuò)誤,煩請留言指正。原文請見 官網(wǎng)英文文檔

Elasticsearch

起步

Elasticsearch 是一個(gè)大規(guī)模開源的全文搜索和分析引擎,你可以用它來快速(接近實(shí)時(shí))存儲、搜索和分析大量的數(shù)據(jù)。通常被用作基礎(chǔ)的引擎,或者有復(fù)雜的搜索功能和要求的應(yīng)用的技術(shù)支撐。

這兒是一些Elasticsearch簡單的使用場景:

  • 在線網(wǎng)站的數(shù)據(jù)存儲,允許你的客戶對你售賣的產(chǎn)品進(jìn)行搜索的場景。這時(shí),你可以使用Elasticsearch存儲你的全部產(chǎn)品信息分類和庫存,然后提供搜索和自動填充建議。

  • 你想做日志和事務(wù)數(shù)據(jù)的收集,然后對它們進(jìn)行分析和挖掘,期待找到某種趨勢,或者進(jìn)行統(tǒng)計(jì)、歸納和異常分析。在這種情況下,你可以使用logstash( Elasticsearch/Logstash/Kibana 棧的一部分)收集、匯總和解析數(shù)據(jù),然后logstash將這些數(shù)據(jù)傳送到Elasticsearch中,一旦數(shù)據(jù)進(jìn)入Elasticsearch中,你就可以使用搜索和聚合來挖掘任何你想要的信息。

  • 價(jià)格預(yù)警平臺,允許精打細(xì)算的客戶指定特殊的規(guī)則,比如我對某個(gè)特定的電子產(chǎn)品比較感興趣,在下個(gè)月任意一個(gè)供應(yīng)商的價(jià)格下降到指定價(jià)格,然后提醒我。在這種情況下,你可以把供貨商的價(jià)格推送到Elasticsearch中,利用它的反向搜索(過濾)能力匹配用戶查詢的價(jià)格走勢,最后將已經(jīng)發(fā)現(xiàn)的匹配項(xiàng)發(fā)送給客戶。

  • 你有分析或者商業(yè)智能的需求,在大量數(shù)據(jù)(數(shù)百萬或者數(shù)十億的記錄)中想做個(gè)快速的調(diào)查、分析、可視化和詢問特殊問題。在這種情況下,你可以使用Elasticsearch存儲數(shù)據(jù),使用kibana構(gòu)建自定義的儀表盤來展示你關(guān)心的數(shù)據(jù)。此外,你還可以使用Elasticsearch的聚合功能來做復(fù)雜的商業(yè)智能分析與數(shù)據(jù)查詢。

接下來的教程,我將會引導(dǎo)你完成Elasticsearch的獲取、安裝和運(yùn)行,然后稍微深入一點(diǎn),介紹一些基本操作,比如建立索引、搜索和修改你的數(shù)據(jù)。在教程的最后你應(yīng)該對Elasticsearch有一個(gè)很好的認(rèn)識,它是什么,它是怎么工作的。無論是構(gòu)建復(fù)雜的搜索程序還是從你的數(shù)據(jù)中挖掘情報(bào),在如何使用Elasticsearch方面,希望你能受到啟發(fā)。

1 基本概念

這里有一些Elasticsearch的主要核心概念,在開始的時(shí)候就理解了這些概念,對于你的學(xué)習(xí)過程有非常大的幫助。

準(zhǔn)實(shí)時(shí)

Elasticsearch是一個(gè)準(zhǔn)實(shí)時(shí)的搜索平臺。意思是從你創(chuàng)建一個(gè)文檔的索引到這個(gè)索引可以被搜索只有很小延遲(通常是1秒)。

集群

一個(gè)集群是一個(gè)或者多個(gè)節(jié)點(diǎn)(服務(wù)器)的集合,它們共同擁有所有的數(shù)據(jù),并且提供跨節(jié)點(diǎn)的索引和搜索能力。一個(gè)集群由一個(gè)唯一的名字確定,默認(rèn)情況下是“elasticsearch”,這個(gè)名字是很重要的,因?yàn)橐粋€(gè)節(jié)點(diǎn)僅可能是一個(gè)集群的一部分,節(jié)點(diǎn)加入到哪個(gè)集群是根據(jù)這個(gè)名字來判斷的。

一定不要在不同的環(huán)境使用相同的集群名稱,否則你可能將節(jié)點(diǎn)加入到錯(cuò)誤的集群中,例如你可以分別在開發(fā)環(huán)境、預(yù)備環(huán)境和生產(chǎn)環(huán)境使用logging-dev, logging-stagelogging-prod。

注意,在一個(gè)集群中只有一個(gè)節(jié)點(diǎn)也是有效的和非常好的。此外,你可以有多個(gè)獨(dú)立的集群,如果它們的有各自唯一的集群名字。

節(jié)點(diǎn)

一個(gè)節(jié)點(diǎn)是一臺服務(wù)器,也是一個(gè)集群的一部分,存儲你的數(shù)據(jù),參與提供集群的索引和搜索能力。就像集群一樣,節(jié)點(diǎn)也是由一個(gè)名字唯一確定的,這個(gè)名字默認(rèn)是一個(gè)隨機(jī)的UUID,它是在節(jié)點(diǎn)啟動的時(shí)候分配的。如果你不想要默認(rèn)的名字,你可以隨意定義你的節(jié)點(diǎn)名稱。這個(gè)名字在集群的管理上顯得很重要,他可以讓你確定地知道在elasticsearch集群中的節(jié)點(diǎn)對應(yīng)網(wǎng)絡(luò)中的哪一臺服務(wù)器。

一個(gè)節(jié)點(diǎn)可以通過配置集群名稱加入到任意的集群中,默認(rèn)情況下,每個(gè)節(jié)點(diǎn)都被設(shè)置加入到一個(gè)名叫elasticsearch的集群中,這意味著,如果你在一個(gè)網(wǎng)絡(luò)中(服務(wù)器之間能夠彼此互相發(fā)現(xiàn))啟動大量這樣的節(jié)點(diǎn),它們將自動的形成一個(gè)集群,名叫elasticsearch

在一個(gè)集群中你可以有任意多的節(jié)點(diǎn)。此外,如果在當(dāng)前節(jié)點(diǎn)運(yùn)行的網(wǎng)絡(luò)中沒有其他的 Elasticsearch 的節(jié)點(diǎn),只啟動一個(gè)節(jié)點(diǎn),它將會形成一個(gè)新的單節(jié)點(diǎn)集群,名叫elasticsearch。

索引

索引是具有相似特性的文檔的集合,例如,你可以為客戶數(shù)據(jù)建立一個(gè)索引,你也可以為產(chǎn)品類數(shù)據(jù)建立索引,還有訂單數(shù)據(jù)也可以建立索引。一個(gè)索引都有一個(gè)特定的名字(必需為小寫)標(biāo)識,在對索引的文檔執(zhí)行索引、搜索、更新和刪除操作時(shí),該名稱用于指定索引。

在一個(gè)集群中,你可以定義任意多的索引。

類型

在一個(gè)索引中,你可以定義一個(gè)或者多個(gè)類型,類型是索引的邏輯類/分區(qū),它的語義完全由你決定。一般來說,將一組具有公共字段的文檔定義為一種類型。例如,假設(shè)你在運(yùn)行一個(gè)博客平臺,你的所有數(shù)據(jù)都存儲在一個(gè)索引中,在這個(gè)索引中你可能為用戶數(shù)據(jù)定義一個(gè)類型,博客數(shù)據(jù)定義另一個(gè)類型,注釋數(shù)據(jù)為另一個(gè)類型。

文檔

文檔是可以被索引的信息的基本單位。例如,你可以為一個(gè)單獨(dú)的客戶數(shù)據(jù)建立一個(gè)文檔,一個(gè)單獨(dú)的產(chǎn)品也可以建立一個(gè)文檔,還有一個(gè)訂單文檔。這些文檔是用JSON格式的數(shù)據(jù)來表示的,這是一種在互聯(lián)網(wǎng)上無處不在的數(shù)據(jù)交互格式。

在一個(gè)索引/類型中,你可以存儲盡可能多的文檔。注意,盡管一個(gè)文檔實(shí)際是存儲在索引中的,但是實(shí)際上你必須將文檔分配到一個(gè)索引中的一個(gè)類型。

分片&備份

一個(gè)索引可以存儲大量的數(shù)據(jù),這些數(shù)據(jù)可能超過單個(gè)節(jié)點(diǎn)硬盤限制,例如,一個(gè)存儲了十億文檔的索引占了1TB的磁盤空間,可能不太適合存儲在一個(gè)節(jié)點(diǎn)的硬盤上,或者說單個(gè)節(jié)點(diǎn)服務(wù)于搜索請求太慢了。

為了解決這個(gè)問題,Elasticsearch提供了將索引細(xì)分為多塊的能力,這些塊被稱為“分片”。在你創(chuàng)建索引的時(shí)候,可以簡單地定義所需數(shù)目的分片,每一個(gè)分片本身是一個(gè)功能全面的、獨(dú)立的“索引”,它可以被托管在集群中的任意節(jié)點(diǎn)。

索引分片有兩個(gè)原因是非常重要的:

  • 你可以水平拆分/縮放存儲數(shù)據(jù)卷。
  • 你可以分布式或者并行地執(zhí)行跨分片(可能存儲在多個(gè)節(jié)點(diǎn)上)的操作,因此可以提高性能和吞吐量。

分片的分布機(jī)制和分片的文檔是如何聚集在一起實(shí)現(xiàn)一次用戶的搜索請求,這些完全都是由Elasticsearch管理的,對用戶來說是透明的。

在網(wǎng)絡(luò)/云環(huán)境中隨時(shí)都有可能失敗,在一個(gè)分片/節(jié)點(diǎn)不知原因地掉線或者消失的情況下,故障轉(zhuǎn)移機(jī)制還是非常有用的,強(qiáng)烈推薦。為此,Elasticsearch允許你為索引的分片創(chuàng)建一個(gè)或多個(gè)副本,這就是所謂的副本分片,或者簡稱為備份。

備份有兩個(gè)原因是非常重要的:

  • 在分片或者節(jié)點(diǎn)故障的情況下,它提供了高可用性。正因?yàn)檫@一點(diǎn),注意一個(gè)副本分片從來不會被分配和主分片(copy的來源節(jié)點(diǎn))在同一個(gè)節(jié)點(diǎn)上,這一點(diǎn)是很重要的。
  • 它能提高你的搜索數(shù)據(jù)量和吞吐量,因?yàn)樗阉骺梢圆⑿械卦诙鄠€(gè)副本上執(zhí)行。

總結(jié)一下,一個(gè)索引能分成多個(gè)分片,一個(gè)索引能被復(fù)制零(無備份)或多次。一旦復(fù)制了之后,每個(gè)索引都有主分片(拷貝的來源分片)和副分片(主分片的拷貝)。一個(gè)索引的分片和副本的數(shù)量都可以在索引創(chuàng)建的時(shí)候定義,在索引創(chuàng)建之后,你可以隨時(shí)動態(tài)的改變副本的數(shù)量,但是你不能事后修改分片的數(shù)量。

默認(rèn)情況下,在Elasticsearch中的每個(gè)索引被分為5個(gè)主分片和1個(gè)副本,這就意味著在你的集群中至少有兩個(gè)節(jié)點(diǎn),你的索引將有5個(gè)主分片和另外5個(gè)副分片(一個(gè)完整的副本),每個(gè)索引總共10個(gè)分片。

注意,每一個(gè)Elasticsearch分片都是一個(gè)Lucene索引,在一個(gè)單獨(dú)的Lucene索引中擁有的文檔數(shù)量是有最大值的,根據(jù)LUCENE-5843中介紹,這個(gè)限制是2,147,483,519 (= Integer.MAX_VALUE - 128)個(gè)文檔。你可以使用api _cat/shards 來監(jiān)控分片的大小。

既然如此,我們就開始有趣的下一part吧。

2 安裝

Elasticsearch要求java 8 以上,在寫該文檔時(shí),建議你使用oracle的JDK版本1.8.0_131,關(guān)于java在各個(gè)平臺的安裝過程這里就不詳細(xì)介紹了,在oracle的官網(wǎng)上能夠找到oracle推薦的安裝文檔,我只想說,在你安裝Elasticsearch之前,請檢查你的當(dāng)前運(yùn)行的java版本(根據(jù)需要安裝或升級java):

java -version
echo $JAVA_HOME

如果你已經(jīng)安裝了java,我們就可以開始下載和運(yùn)行Elasticsearch了。在 www.elastic.co/downloads
這個(gè)網(wǎng)址上可以下載到可用的二進(jìn)制包,還有過去發(fā)行的其它版本的包。對于每一個(gè)發(fā)行版本,你都可以選擇zip或者tar壓縮包,還有DEBRPM包。為了簡單起見,我們以tar包為例。

讓我首先使用下面的命令下載Elasticsearch5.4.1版本的tar文件(windows用戶應(yīng)該下載zip包):

curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.4.1.tar.gz

然后,使用下面的命令解壓它(windows用戶應(yīng)該使用unzip來解壓zip包):

tar -xvf elasticsearch-5.4.1.tar.gz

解壓完成之后,將會在當(dāng)前目錄產(chǎn)生大量的文件和文件夾,然后我們進(jìn)入到bin目錄下,命令如下:

cd elasticsearch-5.4.1/bin

現(xiàn)在我們可以準(zhǔn)備啟動這個(gè)節(jié)點(diǎn)和單節(jié)點(diǎn)集群了(windows用戶應(yīng)該運(yùn)行.bat文件):

./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], 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[5.4.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-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] 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

不過分深入細(xì)節(jié)的話,我們可以看到名為6-bjhwl(這是一組不同的字符,視你的情況而定)的節(jié)點(diǎn)已經(jīng)啟動了,并且它自己被選舉為單節(jié)點(diǎn)的集群中的master節(jié)點(diǎn)。此刻你不用擔(dān)心master意味著什么,這里重要的事情是,我們已經(jīng)在一個(gè)集群中啟動了一個(gè)節(jié)點(diǎn)。

正如前面所說的,我們可以覆蓋集群和節(jié)點(diǎn)的名字,這個(gè)可以在啟動Elasticsearch的命令中做到,命令如下:

./elasticsearch -Ecluster.name=my_cluster_name -Enode.name=my_node_name

同時(shí)還要注意信息中的http地址(192.168.8.112)和端口(9200),這是節(jié)點(diǎn)的訪問入口。默認(rèn)情況下,Elasticsearch使用端口9200提供REST API的訪問,這個(gè)端口在必要的時(shí)候也是可以配置的。

3 探索集群

REST API

現(xiàn)在我們已經(jīng)啟動了我們的節(jié)點(diǎn)(和集群),它們正在運(yùn)行中,下一步我們需要理解怎樣和它交流。幸運(yùn)的是,Elasticsearch提供了非常全面、強(qiáng)大的REST API,你可以使用這些API接口與你的集群交互。下面這些事情都可以通過API來完成:

  • 檢查你的集群、節(jié)點(diǎn)和索引的健康情況、狀態(tài)和一些統(tǒng)計(jì)
  • 管理你的集群、節(jié)點(diǎn)和索引數(shù)據(jù)、元數(shù)據(jù)
  • 執(zhí)行CRUD(創(chuàng)建、讀取、更新和刪除)操作和針對索引的搜索操作
  • 執(zhí)行高級的搜索操作,例如分頁、排序、過濾、腳本、聚合,還有很多其他的操作

集群健康

讓我們以基本的健康檢查開始,健康檢查可以讓我們了解集群的運(yùn)行情況。我們使用curl命令來調(diào)用這些API,你也可以使用任意其它的HTTP/REST調(diào)用工具。假設(shè)我們已經(jīng)登錄了運(yùn)行著Elasticsearch的同一個(gè)節(jié)點(diǎn),并且打開了一個(gè)shell命令行窗口。

為了檢查集群的健康度,我們將使用_cat API.。你可以點(diǎn)擊"VIEW IN CONSOLE"在Kibana’s Console上執(zhí)行下面的命令,或者點(diǎn)擊 "COPY AS CURL" 之后粘貼到你的終端上使用curl命令。

GET /_cat/health?v

然后,下面就是響應(yīng):

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”,是綠色的狀態(tài)。

無論什么時(shí)候我們檢查集群的健康狀況,都是綠色、黃色、紅色。綠色意味著一切都是好的(集群的所有功能都能正常使用);黃色意味著所有數(shù)據(jù)都是可用的,但是缺少一些分片(集群的所有功能都能正常使用);紅色意味著不管什么原因一些數(shù)據(jù)是不可用的;注意,即使集群的狀態(tài)是紅色,還是有部分功能是可用的(例如,它將繼續(xù)可以用來從可用的分片上搜索數(shù)據(jù)),但是你可能需要盡快修復(fù)它,因?yàn)槟阋呀?jīng)丟失了數(shù)據(jù)。

從上面的響應(yīng)中我們還可以看出,總共就只有一個(gè)節(jié)點(diǎn),零個(gè)分片是因?yàn)槔锩鏇]有數(shù)據(jù)。注意我們使用的是默認(rèn)的集群名字(Elasticsearch),Elasticsearch默認(rèn)的使用單播網(wǎng)絡(luò)來發(fā)現(xiàn)同一臺機(jī)器上的其它節(jié)點(diǎn),你也有可能意外地在一臺機(jī)器上啟動了多個(gè)節(jié)點(diǎn),然后它們組成了一個(gè)集群。在這種情形下,你可能在上面的響應(yīng)中看到多個(gè)節(jié)點(diǎn)。

我們也可以查看集群中的所有節(jié)點(diǎn),像下面這樣:

GET /_cat/nodes?v

然后,響應(yīng)如下:

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

這里我們可以看出,只有一個(gè)名叫“PB2SGZY”的節(jié)點(diǎn),它是當(dāng)前集群中的唯一的一個(gè)節(jié)點(diǎn)。

列出所有索引

現(xiàn)在讓我們看一下我們的索引:

GET /_cat/indices?v

然后,響應(yīng)如下:

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

這僅僅意味著在我們的集群中沒有索引。

創(chuàng)建索引

現(xiàn)在讓我們創(chuàng)建一個(gè)名叫“customer”索引,然后再次列出所有的索引:

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

第一條命令使用動詞PUT創(chuàng)建了一個(gè)名叫“customer”的索引,在命令的結(jié)尾僅僅跟了一個(gè)pretty,意思是任何響應(yīng)都以JSON的形式打印出來。

然后,響應(yīng)如下:

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

第二條命令的結(jié)果告訴我們,現(xiàn)在我們由一個(gè)名叫“customer”的索引,它有5個(gè)主分片和1個(gè)副本(默認(rèn)),其中沒有任何文檔。

你可能還注意到customer索引有一個(gè)黃色的健康標(biāo)記,回顧我們之前的討論,黃色意味著一些分片沒有被分配。出現(xiàn)這種情況的原因是,Elasticsearch默認(rèn)情況下會為這個(gè)索引創(chuàng)建一個(gè)副本,但是此刻集群中僅有一個(gè)節(jié)點(diǎn),只有另一個(gè)節(jié)點(diǎn)加入到集群中時(shí),這個(gè)副本才可能被分配(高可用)。一旦副本被分配到第二個(gè)節(jié)點(diǎn)中,這個(gè)索引的健康狀態(tài)立馬就會變成綠色。

索引和查詢文檔

現(xiàn)在讓我們先在customer索引中放一些東西,還記得之前我們?yōu)槲臋n創(chuàng)建索引,必須告訴Elasticsearch文檔應(yīng)該被放在索引中的什么類型下。

讓我們將一個(gè)簡單的客戶文檔放進(jìn)customer索引中,external類型中,ID為1,就像下面這樣:

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

然后,響應(yīng)如下:

{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "created" : true
}

從上面的響應(yīng)中我們可以看出,一個(gè)新的客戶文檔被成功地創(chuàng)建在customer索引的external類型中,這個(gè)文檔還有一個(gè)內(nèi)部id為1,在索引文檔時(shí)候它需要被指定。

特別需要注意的是,Elasticsearch沒有明確要求你在索引一個(gè)文檔之前首先創(chuàng)建一個(gè)索引,就前面的例子而言,如果事先customer索引不存在,Elasticsearch會自動的創(chuàng)建它。

現(xiàn)在讓我們?nèi)z索一下剛剛編入索引的那個(gè)文檔:

GET /customer/external/1?pretty

然后,響應(yīng)如下:

{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "_source" : { "name": "John Doe" }
}

除了其中的found字段沒有什么特別的地方,說明我們找到了請求的ID為1的那個(gè)文檔,另一個(gè)字段是_source,它返回了我們上一步中編入索引的完全的JSON格式的文檔。

刪除索引

現(xiàn)在讓我們刪除剛剛創(chuàng)建的索引,然后再次列出所有的索引:

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

然后,響應(yīng)如下:

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

這就是說索引已經(jīng)被成功刪除,現(xiàn)在我們又回到了開始時(shí)集群中什么都沒有的狀態(tài)。

在繼續(xù)學(xué)習(xí)之前,讓我們再仔細(xì)看看我們學(xué)到的一些API命令:

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

如果我們仔細(xì)研究上面的命令,實(shí)際上我們可以看到一種如何從Elasticsearch中訪問數(shù)據(jù)的模式,這種模式可以概括如下:

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

這種REST訪問模式在所有的API命令中是普遍存在的,如果你能簡單地記住它,在掌握Elasticsearch上,你有了一個(gè)良好的開端。

4 修改數(shù)據(jù)

Elasticsearch提供準(zhǔn)實(shí)時(shí)的數(shù)據(jù)操作和搜索功能。默認(rèn)情況下,從你創(chuàng)建索引/更新/刪除你的數(shù)據(jù)到展現(xiàn)出搜索結(jié)果應(yīng)該是只有1秒的延遲(刷新間隔),這是與像SQL等其它平臺的重要區(qū)別,它們中的事務(wù)操作完成之后數(shù)據(jù)立即可用。

索引和替換文檔

我們前面已經(jīng)知道了如何將一個(gè)單獨(dú)的文檔編入索引,讓我們回顧一下命令:

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

同樣,上面的命令將一個(gè)指定的文檔編入到customer索引中,類型為external,ID為1. 如果我們使用不同(或者相同)的文檔再次執(zhí)行上面的命令,Elasticsearch將在現(xiàn)存的ID為1的上面替換(或者重建)一個(gè)新文檔:

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

上面的命令改變了ID為1的文檔的名字,由 "John Doe" 改為 "Jane Doe"。另一方面,如果我們使用不同的ID,一個(gè)新的文檔將會被編入索引,而索引中已經(jīng)存在的文檔保持不變。

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

上面的命令將一個(gè)ID為2的新文檔編入了索引。

當(dāng)我們索引文檔的時(shí)候,ID部分是可選的,如果你不指定的話,Elasticsearch將隨機(jī)生成一個(gè)ID,然后使用它去索引文檔。Elasticsearch實(shí)際生成的ID(或者是前面例子中我們明確指定的ID)將作為索引API調(diào)用的一部分。

下面的例子展示了在不明確指定ID的情況下如何索引一個(gè)文檔:

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

注意,上面的例子中我們使用的是動詞POST,而不是PUT,因?yàn)槲覀儧]有指定ID。

更新文檔

另外,除了可以索引和替換文檔之外,我們還可以更新文檔。需要注意的是,Elasticsearch背后并沒有真正的做更新操作,無論我們什么時(shí)候做更新操作,Elasticsearch都是刪除舊的文檔,然后將更新后的文檔一次性地作為一個(gè)新文檔編入索引。

下面的例子展示了,如何更新前面的文檔(ID為1),將它的name字段改為"Jane Doe":

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

下面的例子展示了,如何更新前面的文檔(ID為1),將它的name字段改為"Jane Doe",同時(shí)增加一個(gè)新的字段age:

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

更新操作也可以使用簡單的腳本,下面的例子使用腳本將age字段的值增加5:

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

在上面的例子中,ctx._source 是指即將更新的當(dāng)前文檔。

注意,在寫這篇文檔的時(shí)候,更新操作還只能一次更新一個(gè)文檔。未來,elasticsearch可能提供根據(jù)查詢條件更新多個(gè)文檔的能力(就像SQL UPDATE-WHERE語句一樣)。

刪除文檔

刪除文檔相對來說是比較簡單的,下面的例子展示了如何刪除ID為2的客戶文檔:

DELETE /customer/external/2?pretty

參見 Delete By Query API 刪除指定查詢條件的所有文檔。刪除整個(gè)索引比根據(jù)查詢API刪除該索引下的所有文檔更高效。

批處理

我們除了能索引、更新和刪除單個(gè)文檔,Elasticsearch還提供了使用 _bulk
API
批量執(zhí)行上面的任何操作。這個(gè)功能是很重要的,它提供了一種盡可能快并且盡量少的網(wǎng)絡(luò)切換條件下執(zhí)行多個(gè)操作的高效機(jī)制。

做一個(gè)快速示范,下面是使用一個(gè)bulk操作將兩個(gè)文檔(ID 1 - John Doe and ID 2 - Jane Doe)編入索引的例子:

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

下面的例子使用一個(gè)bulk操作更新第一個(gè)文檔(ID為1),然后刪除第二個(gè)文檔(ID為2):

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

注意上面的刪除操作,在它的后面沒有指定對應(yīng)的源文檔,因?yàn)閯h除操作僅需要被刪除文檔的ID。

多個(gè)操作中的一個(gè)失敗不會導(dǎo)致整個(gè)bulk操作的失敗,如果其中一個(gè)操作不管什么原因失敗了,將會繼續(xù)執(zhí)行它后面剩下的操作。在bulk操作的返回信息中將提供每個(gè)操作的執(zhí)行狀態(tài),因此你可以檢查某個(gè)指定的操作是否失敗了。

5 探索數(shù)據(jù)

樣本數(shù)據(jù)集

現(xiàn)在我們已經(jīng)了解了一些基本知識,讓我們研究一下更真實(shí)的數(shù)據(jù)集。我已經(jīng)準(zhǔn)備好了一個(gè)虛構(gòu)的JSON文檔示例,它是一個(gè)客戶的銀行賬戶信息。每個(gè)文檔都有下面的模式:

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

處于好奇,這個(gè)數(shù)據(jù)我是從這個(gè) www.json-generator.com/
網(wǎng)站上生成的,因此請忽略其中的數(shù)值和語義,這些都是隨機(jī)生成的。

加載樣本數(shù)據(jù)集

你可以從這里(accounts.json)下載樣本數(shù)據(jù)集,將其解壓到當(dāng)前目錄,然后使用下面的命令加載到集群中:

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

然后,響應(yīng)如下:

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個(gè)文檔編入bank索引(account 類型下面)。

搜索API

現(xiàn)在讓我們開始使用一些簡單的搜索,有兩個(gè)基本的方法執(zhí)行搜索:一個(gè)是通過 REST request URI 發(fā)送搜索參數(shù),另一個(gè)是通過 REST request body 發(fā)送搜索參數(shù);Request body方式更有表現(xiàn)力,允許你使用可讀性比較好JSON格式定義你的搜索。我們嘗試一個(gè)Request URI的方式,但是教程的其他部分我們將只使用Request Body方式。

搜索的REST API可以通_search端點(diǎn)來訪問,下面是獲取bank索引下的所有文檔的例子:

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

讓我們首先剖析一下這個(gè)搜索調(diào)用,我們正在搜索bank索引(_search端點(diǎn)),參數(shù)q=*命令Elasticsearch匹配這個(gè)索引下的所有文檔,參數(shù)sort=account_number:asc表明使用每個(gè)文檔的account_number字段進(jìn)行升序排序,另外參數(shù)pretty告訴Elasticsearch返回美化后的JSON結(jié)果。
然后,響應(yīng)如下(展示部分):

{
  "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":"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"}
    }, ...
    ]
  }
}

對于響應(yīng),我們看一下下面幾個(gè)部分:

  • took Elasticsearch執(zhí)行搜索的耗時(shí),以毫秒為單位;
  • timed_out 告訴我們搜索是否超時(shí);
  • _shards 告訴我們多少個(gè)分片被搜索到,并且顯示搜索成功和失敗的分片數(shù);
  • hits 搜索結(jié)果;
  • hits.total 匹配搜索標(biāo)準(zhǔn)的文檔總數(shù);
  • hits.hits 搜索結(jié)果的實(shí)際陣列(默認(rèn)前10個(gè)文檔);
  • hits.sort 搜索結(jié)果中的排序key(如果按照分?jǐn)?shù)排序,將會缺失);
  • hits._scoremax_score 現(xiàn)在忽略這寫字段;

下面是一個(gè)代替上面全部搜索方法的Request Body方法:

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

與上面的URI中的q=*相比的不同點(diǎn)是,我們提交了一個(gè)JSON格式的查詢請求到_search API。在下一個(gè)部分我們將討論JSON的查詢。

要明白一件重要的事,Elasticsearch一旦返回搜索結(jié)果,搜索請求就完全結(jié)束了,它不會保持任何服務(wù)端的資源,也不會維持搜索結(jié)果中的游標(biāo)。這和其他平臺是完全不同的,例如SQL平臺,開始時(shí)可以獲取查詢結(jié)果前面的部分子集,如果你想獲取(或者通過分頁查詢)結(jié)果的其余部分,可以通過某種服務(wù)端游標(biāo)繼續(xù)請求服務(wù)器。

介紹查詢語言

Elasticsearch提供了JSON格式的領(lǐng)域?qū)S玫恼Z言,你可以使用它來執(zhí)行查詢,具體參考 Query DSL。該查詢語言是很全面的,乍看一下可能很嚇人,實(shí)際上學(xué)習(xí)它的最好的方法是從一些基本的例子開始。

回到我們的最后一個(gè)例子,執(zhí)行這個(gè)查詢:

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

剖析一下上面的調(diào)用請求,query部分告訴我們查詢的定義是什么,簡單的說,match_all是我們想要執(zhí)行的查詢類型,match_all查詢是搜索指定索引的全部文檔。

除了query參數(shù)以外,我們還可以通過其它參數(shù)來干預(yù)搜索結(jié)果,在上一部分的例子中我們使用過sort,這里我們使用一下size

GET /bank/_search
{
  "query": { "match_all": {} },
  "size": 1
}

注意,如果size不指定的話,默認(rèn)是10.

下面的例子是一個(gè) match_all 的搜索,然后返回從11到20的文檔:

GET /bank/_search
{
  "query": { "match_all": {} },
  "from": 10,
  "size": 10
}

from參數(shù)(0為基礎(chǔ))指定從索引的哪個(gè)文檔開始,size參數(shù)指定返回從from參數(shù)開始的多少個(gè)文檔。這個(gè)特性在實(shí)現(xiàn)搜索結(jié)果的分頁查詢時(shí)是很有用的。注意,如果from不指定,默認(rèn)是0.

下面的例子是一個(gè)match_all的搜索,然后按照賬戶余額進(jìn)行降序排列,然后返回前10(默認(rèn))個(gè)文檔。

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

搜索操作

現(xiàn)在我們已經(jīng)看到了一些基本的搜索參數(shù),讓我們更深入地挖掘一下Query DSL. 讓我們首先看一下返回的文檔的字段,默認(rèn)情況下,作為所有搜索的一部分返回完整的JSON文檔,這個(gè)被稱作資源(在搜索結(jié)果中的hits中的_source字段)。如果我們不想要返回的整個(gè)資源文檔,我們可以僅請求返回的資源中的部分字段。

下面的例子展示了如何搜索返回兩個(gè)字段 account_numberbalance_source內(nèi)部):

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

注意,上面的例子僅僅是減少了_source的字段,它仍然僅返回一個(gè)名叫_source的字段,但是在它只包含account_numberbalance 兩個(gè)字段。

如果你有SQL背景,這個(gè)在概念上有點(diǎn)類似于SQL SELECT FROM的字段列表。

現(xiàn)在讓我們把注意力轉(zhuǎn)到查詢部分,前面我們已經(jīng)看到match_all查詢是如何匹配所有文檔的,現(xiàn)在讓我們介紹一個(gè)新的查詢叫 match query,可以認(rèn)為它是基本字段的搜索查詢(例如,指定一個(gè)字段或者多個(gè)字段來完成一次搜索)。

下面的例子是返回賬戶編號為20的文檔:

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

下面的例子是返回地址包含“mill”的所有賬戶:

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

下面的例子是返回地址包含“mill”或者“l(fā)ane”的所有賬戶:

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

下面的例子是match的一個(gè)變形(match_phrase),它返回地址中包含短語“mill lane”的所有賬戶:

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

現(xiàn)在讓我介紹bool(ean) querybool查詢允許我們使用bool邏輯將小的查詢組合成大查詢。

下面的例子是組合兩個(gè)match查詢,然后返回在地址中包含“mill” 和 “l(fā)ane”的所有賬戶:

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

在上面的例子中,bool must分句指定所有查詢條件必須是“true”才算匹配的文檔。

與此相反,下面的例子組合兩個(gè)match查詢,返回在地址中包含“mill” 或 “l(fā)ane”的所有賬戶:

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

在上面的例子中,bool should分句指定一個(gè)查詢列表,其中必須有一個(gè)是“true”的文檔才算被匹配。

下面的例子組合兩個(gè)match查詢,返回在地址中既不包含“mill” 也不包含 “l(fā)ane”的所有賬戶:

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

在上面的例子中,must_not分句指定一個(gè)查詢列表,其中必須都不是“true”的文檔才算被匹配。

我們可以同時(shí)在一個(gè)bool查詢中組合must, shouldmust_not分句。此外,我們可以在任何bool分句中組合bool查詢,以便模擬任何復(fù)雜的多級bool邏輯。

下面的例子返回年齡是40歲但不住在ID(aho)的所有人的賬戶:

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

過濾操作

在前面的部分我們跳過了一個(gè)小細(xì)節(jié)所謂的文檔得分(也就是搜索結(jié)果中的_score字段)。得分是數(shù)值型的,它是文檔和我們指定的搜索查詢匹配程度的相對度量。得分越高,文檔越相關(guān);得分越低,文檔越不相關(guān)。

但是查詢并不總是需要得分,尤其是它們僅被用作文檔集合的過濾器時(shí)。Elasticsearch探測到這種情況會自動地優(yōu)化查詢的執(zhí)行,防止計(jì)算無用的得分。

在前面我們介紹的 bool query 也是支持filter分句的,它允許使用查詢來限制其它分句匹配的文檔,而不改變得分的計(jì)算。作為一個(gè)例子,我們來介紹一下range query ,它允許我們使用一個(gè)值域來過濾文檔,這通常被用于數(shù)值型和日期型的過濾器。

下面的例子使用bool 查詢獲取余額在20000和30000之間的所有賬戶,換句話說,我們是想找到余額大于等于20000并且小于等于30000的賬戶。

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

仔細(xì)分析一下上面的例子,bool查詢包括一個(gè)match all查詢(查詢的一部分)和一個(gè)range 查詢(過濾的一部分)。我們可以將其它任何查詢替換成查詢和過濾組成。在上面的例子中,range查詢是很有意義的,因?yàn)樗性诜秶鷥?nèi)的文檔都是相等的,沒有哪個(gè)文檔比另外的文檔更相關(guān)。

除了 match_all, match, bool, and range 查詢之外,還有很多其它類型的查詢,我們不會在這里討論它們。因?yàn)槲覀儗λ鼈兊墓ぷ髟碛辛嘶镜睦斫猓瑢⑦@些知識應(yīng)用于其它類型的查詢,學(xué)習(xí)和使用它們都不會太難。

聚合操作

聚合提供了對數(shù)據(jù)進(jìn)行分組、提取統(tǒng)計(jì)結(jié)果的能力。想明白聚合的最簡單的方法是將其大致等同于SQL分組和SQL的聚合函數(shù)。在Elasticsearch中,可以執(zhí)行搜索獲取hits,同時(shí)也能在同一個(gè)響應(yīng)中返回區(qū)別于hits的聚合結(jié)果。從這一點(diǎn)來說,它是很強(qiáng)大和高效的,你可以執(zhí)行查詢和多聚合操作,并且這些操作結(jié)果一起返回,使用這樣簡單的API避免了網(wǎng)絡(luò)的切換。

首先,下面的例子是將所有賬戶按照州來分組,然后返回按照數(shù)量的降序排列的前10個(gè)州(默認(rèn)):

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

然后,響應(yīng)如下(部分展示):

{
  "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(Idaho)州由27個(gè)賬戶,在TX(Texas)州有個(gè)27個(gè)賬戶,在AL(Alabama)州有25個(gè)賬戶,等等。

注意上面我們設(shè)置size=0是為了不展示搜索結(jié)果,因?yàn)槲覀儍H想在響應(yīng)中看到聚合結(jié)果。

在前面的聚合操作的基礎(chǔ)上,下面的例子計(jì)算了每個(gè)州的賬戶余額平均值(同樣僅展示賬戶總數(shù)降序排列的前10個(gè)州):

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

請注意我們是如何將average_balance聚合嵌套在group_by_state聚合中的,這是所有聚合中常見的模式,為了從數(shù)據(jù)中提取總結(jié)你需要的信息,可以嵌套聚合到任意其它的聚合之中。

在前面的聚合的基礎(chǔ)上,現(xiàn)在讓我們按照余額的平均值進(jìn)行降序排列:

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)進(jìn)行分組,然后按照性別分組,最后獲得在每個(gè)年齡組每個(gè)性別中賬戶余額的平均值:

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

還有很多其它的聚合功能,這里我們就不詳細(xì)一一介紹了,如果你想進(jìn)一步學(xué)習(xí)聚合的操作, aggregations reference guide 是一個(gè)不錯(cuò)的參考文檔。

6 結(jié)論

Elasticsearch是一個(gè)既簡單又復(fù)雜的產(chǎn)品。到目前為止,我們已經(jīng)了解了它的基本原理,如何查看它的內(nèi)部,如何使用一些REST API操作它。我希望這個(gè)教程讓你更好地理解了Elasticsearch是什么,更重要是,激發(fā)你去進(jìn)一步探索Elasticsearch的其余特性。

Elasticsearch的起步教程終于翻譯完了,這個(gè)翻譯文檔只是入門級的介紹Elasticsearch,希望能對你的學(xué)習(xí)有所幫助。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容