Elasticsearch 6.1官方入門教程
入門
Elasticsearch是一個高度可伸縮的開源全文搜尋和分析引擎。它允許你以近實時的方式快速儲存、搜尋和分析大量的資料。它通常被用作基礎的技術來賦予應用程式複雜的搜尋特性和需求。
這裡列舉了幾個Elasticsearch可以用來做的功能例子
你有一個線上網上商城,提供使用者搜尋你所賣的商品功能。在這個例子中,你可以使用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有了一個不錯的瞭解,它是怎麼工作的,並且我們希望你能夠使用它構建出更加複雜精緻的搜尋應用來挖掘你的資料裡的價值。
基礎概念
這裡有一些Elasticsearch的核心概念。在一開始理解這些概念將會極大的使你的學習過程變得更加輕鬆。
- 近實時性(Near Realtime[NRT])
Elasticsearch是一個近實時的搜尋平臺。這意味著當你匯入一個文件並把它變成可搜尋的時間僅會有輕微的延時。
- 叢集(Cluster)
一個叢集是由一個或多個節點(伺服器)組成的,通過所有的節點一起儲存你的全部資料並且提供聯合索引和搜尋功能的節點集合。每個叢集有一個唯一的名稱標識,預設是“elasticsearch”。這個名稱非常重要,因為一個節點(Node)只有設定了這個名稱才能加入叢集,成為叢集的一部分。
確保你沒有在不同的環境下重用相同的名稱,否則你最終可能會將節點加入錯誤的叢集。例如你可以使用logging-dev,logging-stage和logging-prod來分別給開發,展示和生產叢集命名。
注意,一個叢集中只有一個節點是有效的並且是非常好的。所以這樣的話,你可能需要部署多個叢集並且每個叢集有它們唯一的叢集名稱。
- 節點(Node)
一個節點是一個單一的伺服器,是你的叢集的一部分,儲存資料,並且參與叢集的索引和搜尋功能。跟叢集一樣,節點在啟動時也會被分配一個唯一的標識名稱,這個名稱預設是一個隨機的UUID(Universally Unique IDentifier)。如果你不想用預設的名稱,你可以自己定義節點的名稱。這個名稱對於管理叢集節點,識別哪臺伺服器對應叢集中的哪個節點有重要的作用。
一個節點可以通過配置特定的叢集名稱來加入特定的叢集。預設情況下,每個節點被設定加入一個名稱為“elasticsearch”的叢集,這意味著如果你在你的網路中啟動了一些節點,並且假設它們能相互發現,它們將會自動組織並加入一個名稱是“elasticsearch”的叢集。
在一個叢集中,你想啟動多少節點就可以啟動多少節點。此外,如果沒有其它節點執行在當前網路中,只啟動一個節點將預設形成一個新的名稱為“elasticsearch”單節點叢集。
- 索引(Index)
一個索引就是含有某些相似特性的文件的集合。例如,你可以有一個使用者資料的索引,一個產品目錄的索引,還有其他的有規則資料的索引。一個索引被一個名稱(必須都是小寫)唯一標識,並且這個名稱被用於索引通過文件去執行索引,搜尋,更新和刪除操作。
在一個叢集中,你可以根據自己的需求定義任意多的索引。
- 型別(Type)[Deprecated in 6.0.0.]
警告!Type在6.0.0版本中已經不贊成使用
- 文件(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包:
使用如下命令解壓:
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安裝包進行安裝。這個安裝包使用圖形使用者介面來引導你進行安裝。
然後雙擊下載好的安裝包檔案啟動圖形化安裝程式,在第一個介面,選擇安裝目錄:
然後選擇是否將Elasticsearch安裝為一個系統服務,為了和用tar包安裝示例保持一致,我們選擇不安裝為系統服務,根據自己需要手動啟動:
然後到了配置頁面,這裡就簡單的使用預設的配置值:
進入外掛安裝頁面,同樣為了跟tar包安裝示例保持一致,將所有的選擇去掉,不安裝任何外掛:
然後點選安裝按鈕,Elasticsearch將會被安裝:
預設情況下,Elasticsearch將會被安裝在%PROGRAMFILES%\Elastic\Elasticsearch
。進入這個目錄並且切換到bin目錄下:
使用命令提示符:
cd %PROGRAMFILES%\Elastic\Elasticsearch\bin
使用PowerShell:
cd $env:PROGRAMFILES\Elastic\Elasticsearch\bin
接下來我們就可以啟動我們的單節點叢集了:
.\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
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
中的任何一個。
- Green - 一切執行正常(叢集功能齊全)
- Yellow - 所有資料是可以獲取的,但是一些複製品還沒有被分配(叢集功能齊全)
- Red - 一些資料因為一些原因獲取不到(叢集部分功能不可用)
注意:當一個叢集處於red狀態時,它會通過可用的分片繼續提供搜尋服務,但是當有未分配的分片時,你需要儘快的修復它。
另外,從上面的返回結果中我們可以看到,當我們裡面沒有資料時,總共有1個節點,0個分片。注意當我們使用預設的叢集名稱(elasticsearch)並且當Elasticsearch預設使用單播網路發現在同一臺機器上的其它節點時,很可能你會在你電腦上不小心啟動不止一個節點並且他們都加入了一個叢集。在這種情況下,你可能會從上面的返回結果中看到不止一個節點。
我們也可以通過如下請求獲取叢集中的節點列表:
Http請求體
GET /_cat/nodes?v
Curl命令
curl -XGET ‘localhost:9200/_cat/nodes?v&pretty’
Kibana Console
返回結果為:
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
得到的返回結果為:
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
第一個命令使用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的過程中有一個很好的開端。
如下是我在上述章節實際做的操作:
[[email protected] 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%
[[email protected] 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%
[[email protected] 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
[[email protected] 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
[[email protected] elasticsearch-6.1.1]# curl -XPUT 'localhost:9200/customer?pretty&pretty'
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "customer"
}
[[email protected] 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
[[email protected] 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
}
[[email protected] 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"
}
}
[[email protected] elasticsearch-6.1.1]# curl -XDELETE 'localhost:9200/customer?pretty&pretty'
{
"acknowledged" : true
}
[[email protected] 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
[[email protected] 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": "[email protected]",
"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":"[email protected]","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":"[email protected]","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/_