Elasticsearch 6.1官方入門教程

入門

Elasticsearch是一個高度可伸縮的開源全文搜索和分析引擎。它容許你以近實時的方式快速存儲、搜索和分析大量的數據。它一般被用做基礎的技術來賦予應用程序複雜的搜索特性和需求。html

這裏列舉了幾個Elasticsearch能夠用來作的功能例子java

  • 你有一個在線網上商城,提供用戶搜索你所賣的商品功能。在這個例子中,你可使用Elasticsearch去存儲你的所有的商品目錄和存貨清單而且提供搜索和搜索自動完成以及搜索推薦功能。
  • 你想去收集日誌或者業務數據,而且去分析並從這些數據中挖掘尋找市場趨勢、統計資料、摘要信息或者反常狀況。在這個例子中,你可使用Logstash(part of the Elasticsearch/Logstash/Kibana stack)去收集、聚合而且解析你的數據,而後經過Logstash將數據注入Elasticsearch。一旦數據進入Elasticsearch,你就能夠運行搜索和彙集而且從中挖掘任何你感興趣的數據。
  • 你運行一個價格預警平臺,它可讓那些對價格精明的客戶指定一個規則,好比:「我相中了一個電子產品,而且我想在下個月任何賣家的這個電子產品的價格低於多少錢的時候提醒我」。在這個例子中,你能夠抓取全部賣家的價格,把價格放入Elasticsearch而且使用Elasticsearch的反向搜索(過濾器/抽出器)功能來匹配價格變更以應對用戶的查詢並最終一旦發現有匹配結果時給用戶彈出提示框。
  • 你有分析學/商業情報的需求而且想快速審查、分析並使用圖像化進行展現,而且在一個很大的數據集上查詢點對點的問題(試想有百萬或千萬的記錄)。在這個例子中,你可使用Elasticsearch去存儲你的數據而後使用Kibana(part of the Elasticsearch/Logstash/Kibana stack)去構建定製化的儀表盤。這樣你就能夠很直觀形象的瞭解對你重要的數據。此外,你可使用Elasticsearch的集成功能,靠你的數據去展示更加複雜的商業情報查詢。

在剩餘的本教程中,你將會被引導去把Elasticsearch安裝好並運行起來,而且對它有一個簡單的瞭解。使用基礎的操做好比索引,搜索以及修改你的數據。當你完成這個教程的時候,你應該對Elasticsearch有了一個不錯的瞭解,它是怎麼工做的,而且咱們但願你可以使用它構建出更加複雜精緻的搜索應用來挖掘你的數據裏的價值。node

基礎概念

這裏有一些Elasticsearch的核心概念。在一開始理解這些概念將會極大的使你的學習過程變得更加輕鬆。git

  • 近實時性(Near Realtime[NRT])

Elasticsearch是一個近實時的搜索平臺。這意味着當你導入一個文檔並把它變成可搜索的時間僅會有輕微的延時。github

  • 集羣(Cluster)

一個集羣是由一個或多個節點(服務器)組成的,經過全部的節點一塊兒保存你的所有數據而且提供聯合索引和搜索功能的節點集合。每一個集羣有一個惟一的名稱標識,默認是「elasticsearch」。這個名稱很是重要,由於一個節點(Node)只有設置了這個名稱才能加入集羣,成爲集羣的一部分。shell

確保你沒有在不一樣的環境下重用相同的名稱,不然你最終可能會將節點加入錯誤的集羣。例如你可使用logging-dev,logging-stage和logging-prod來分別給開發,展現和生產集羣命名。數據庫

注意,一個集羣中只有一個節點是有效的而且是很是好的。因此這樣的話,你可能須要部署多個集羣而且每一個集羣有它們惟一的集羣名稱。express

  • 節點(Node)

一個節點是一個單一的服務器,是你的集羣的一部分,存儲數據,而且參與集羣的索引和搜索功能。跟集羣同樣,節點在啓動時也會被分配一個惟一的標識名稱,這個名稱默認是一個隨機的UUID(Universally Unique IDentifier)。若是你不想用默認的名稱,你能夠本身定義節點的名稱。這個名稱對於管理集羣節點,識別哪臺服務器對應集羣中的哪一個節點有重要的做用。json

一個節點能夠經過配置特定的集羣名稱來加入特定的集羣。默認狀況下,每一個節點被設定加入一個名稱爲「elasticsearch」的集羣,這意味着若是你在你的網絡中啓動了一些節點,而且假設它們能相互發現,它們將會自動組織並加入一個名稱是「elasticsearch」的集羣。windows

在一個集羣中,你想啓動多少節點就能夠啓動多少節點。此外,若是沒有其它節點運行在當前網絡中,只啓動一個節點將默認造成一個新的名稱爲「elasticsearch」單節點集羣。

  • 索引(Index)

一個索引就是含有某些類似特性的文檔的集合。例如,你能夠有一個用戶數據的索引,一個產品目錄的索引,還有其餘的有規則數據的索引。一個索引被一個名稱(必須都是小寫)惟一標識,而且這個名稱被用於索引經過文檔去執行索引,搜索,更新和刪除操做。

在一個集羣中,你能夠根據本身的需求定義任意多的索引。

  • 類型(Type)[Deprecated in 6.0.0.]

警告!Type在6.0.0版本中已經不同意使用

一個類型是你的索引中的一個分類或者說是一個分區,它可讓你在同一索引中存儲不一樣類型的文檔,例如,爲用戶建一個類型,爲博客文章建另外一個類型。如今已不可能在同一個索引中建立多個類型,而且整個類型的概念將會在將來的版本中移除。查看「映射類型的移除[https://www.elastic.co/guide/...]」瞭解更多。

  • 文檔(Document)

一個文檔是一個可被索引的數據的基礎單元。例如,你能夠給一個單獨的用戶建立一個文檔,給單個產品建立一個文檔,以及其餘的單獨的規則。這個文檔用JSON格式表現,JSON是一種廣泛的網絡數據交換格式。

在一個索引或類型中,你能夠根據本身的需求存儲任意多的文檔。注意,雖然一個文檔在物理存儲上屬於一個索引,可是文檔實際上必須指定一個在索引中的類型。

  • 分片和複製(Shards & Replicas)

咱們在一個索引裏存儲的數據,潛在的狀況下可能會超過單節點硬件的存儲限制。例如,單個索引有上千萬個文檔須要佔用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安裝包。

Linux tar包安裝示例

爲了簡單,讓咱們使用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安裝

在macOS上,咱們能夠經過Homebrew來安裝Elasticsearch:

brew install elasticsearch

Windows上使用MSI安裝

對於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用戶運行。因此運行以前首先須要建立新用戶並賦予權限。

探究你的集羣

REST API

既然咱們的節點(集羣)已經安裝成功而且已經啓動運行,那麼下一步就是去了解如何去操做它。幸運的是,Elasticsearch提供了很是全面和強大的REST API,咱們能夠經過它去跟集羣交互。經過API咱們能夠完成以下的功能:

  • 檢查集羣,節點和索引的健康情況,狀態和統計數據
  • 管理集羣,節點和索引的數據和原數據
  • 執行CRUD(增刪改查)操做,依靠索引進行搜索
  • 執行高級搜索操做,好比分頁,排序,過濾,腳本化,彙集等等

集羣健康監控

讓咱們從一個簡單的健康檢查開始,經過這個咱們能夠了解咱們集羣的運行狀況。咱們將使用curl工具來作這個測試,固然你可使用任何能夠發送HTTP/REST請求的工具。讓咱們假設咱們依然在以前已啓動的Elasticsearch節點上而且打開了另外一個shell窗口。

咱們將使用 _cat API 去檢查集羣健康狀態。HTTP請求內容爲:

GET /_cat/health?v

你能夠經過點擊VIEW IN ConsoleKibana 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

不管什麼時候查看集羣健康狀態,咱們會獲得greenyellowred中的任何一個。

  • 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?pretty

GET /_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類型下)。

搜索API

如今,讓咱們從一些簡單的搜索指令開始。執行搜索有兩種基礎的方式,一種是在請求的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._scoremax_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_numberbalance (在_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_numberbalance兩個屬性。

若是你以前學過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查詢中同時指定mustshouldmust_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_allmatchbool,和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以及更重要的東西,鼓勵你去實踐它剩餘的更多的特性!

相關文章
相關標籤/搜索