Elasticsearch 是一款穩定高效的分散式搜尋和分析引擎,它的底層基於 Lucene,並提供了友好的 RESTful API 來對資料進行操作,還有比較重要的一點是, Elasticsearch 開箱即可用,上手也比較容易。
目前 Elasticsearch 在搭建企業級搜尋(如日誌搜尋、商品搜尋等)平臺中很廣泛,官網也提供了不少案例,比如:
- GitHub 使用 Elasticsearch 檢索超過 800 萬的程式碼庫
- eBay 使用 Elasticsearch 搜尋海量的商品資料
- Netflix 使用 Elasticsearch 來實現高效的訊息傳遞系統
本文主要介紹 Elasticsearch 的基本概念及入門使用。
安裝
在安裝 Elasticsearch 之前,請確保你的計算機已經安裝了 Java。目前 Elasticsearch 的最新版是 5.2,需要安裝 Java 8,如果你用的是老版本的 Elasticsearch,如 2.x 版,可用 Java 7,但還是推薦使用 Java 8。
可以使用如下的命令檢查 Java 的版本:
$ java -version複製程式碼
接著,我們可以從這裡下載最新版本的 Elasticsearch,也可使用 wget 下載,如下:
$ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.2.2.tar.gz複製程式碼
Windows 使用者也可以下載 .zip
格式的安裝包。
下載完後進行解壓:
$ tar -zxvf elasticsearch-5.2.2.tar.gz複製程式碼
執行
首先,我們進入到剛剛解壓出來的目錄中:
$ cd elasticsearch-5.2.2複製程式碼
接著,使用如下命令啟動 Elasticsearch:
$ bin/elasticsearch複製程式碼
此時,如果正常的話,你可以在終端看到類似如下的輸出:
[2017-03-04T23:25:09,961][INFO ][o.e.n.Node ] [] initializing ...
[2017-03-04T23:25:10,073][INFO ][o.e.e.NodeEnvironment ] [yO11WLM] using [1] data paths, mounts [[/ (/dev/disk0s2)]], net usable_space [141.1gb], net total_space [232.9gb], spins? [unknown], types [hfs]
[2017-03-04T23:25:10,074][INFO ][o.e.e.NodeEnvironment ] [yO11WLM] heap size [1.9gb], compressed ordinary object pointers [true]
[2017-03-04T23:25:10,095][INFO ][o.e.n.Node ] node name [yO11WLM] derived from node ID [yO11WLMOQDuAOpZbYZYjzw]; set [node.name] to override
[2017-03-04T23:25:10,100][INFO ][o.e.n.Node ] version[5.2.2], pid[7607], build[db0d481/2017-02-09T22:05:32.386Z], OS[Mac OS X/10.11.5/x86_64], JVM[Oracle Corporation/Java HotSpot(TM) 64-Bit Server VM/1.8.0_102/25.102-b14]
[2017-03-04T23:25:11,363][INFO ][o.e.p.PluginsService ] [yO11WLM] loaded module [aggs-matrix-stats]
...複製程式碼
上面的命令是在前臺執行的,如果想在後臺以守護程式模式執行,可以加 -d
引數。
Elasticsearch 啟動後,也啟動了兩個埠 9200 和 9300:
- 9200 埠:HTTP RESTful 介面的通訊埠
- 9300 埠:TCP 通訊埠,用於叢集間節點通訊和與 Java 客戶端通訊的埠
現在,讓我們做一些測試。在瀏覽器訪問連結 http://localhost:9200/ ,或使用 curl 命令:
$ curl 'http://localhost:9200/?pretty'複製程式碼
我們可以看到類似如下的輸出:
{
"name" : "yO11WLM",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "yC8BGwzlSnu_zGbKL918Xg",
"version" : {
"number" : "5.2.1",
"build_hash" : "db0d481",
"build_date" : "2017-02-09T22:05:32.386Z",
"build_snapshot" : false,
"lucene_version" : "6.4.1"
},
"tagline" : "You Know, for Search"
}複製程式碼
概念
在進一步使用 Elasticsearch 之前,讓我們先了解幾個關鍵概念。
在邏輯層面:
- Index (索引):這裡的 Index 是名詞,一個 Index 就像是傳統關聯式資料庫的 Database,它是 Elasticsearch 用來儲存資料的邏輯區域
- Document (文件):Elasticsearch 使用 JSON 文件來表示一個物件,就像是關聯式資料庫中一個 Table 中的一行資料
- Type (型別):文件歸屬於一種 Type,就像是關聯式資料庫中的一個 Table
- Field (欄位):每個文件包含多個欄位,類似關聯式資料庫中一個 Table 的列
我們用一個表格來做類比,如下:
Elasticsearch | MySQL |
---|---|
Index | Database |
Type | Table |
Document | Row |
Field | Column |
在物理層面:
- Node (節點):node 是一個執行著的 Elasticsearch 例項,一個 node 就是一個單獨的 server
- Cluster (叢集):cluster 是多個 node 的集合
- Shard (分片):資料分片,一個 index 可能會存在於多個 shard
使用
接下來,我們看看如何建立索引、建立文件等,就好比在 MySQL 中進行諸如建立資料庫,插入資料等操作。
新增文件
下面,我們將建立一個儲存電影資訊的 Document:
- Index 的名稱為 movie
- Type 為 adventure
- Document 有兩個欄位:name 和 actors
我們使用 Elasticsearch 提供的 RESTful API 來執行上述操作,如圖所示:
- 用 url 表示一個資源,比如
/movie/adventure/1
就表示一個index
為 movie,type
為 adventure,id
為 1 的 document - 用 http 方法操作資源,如使用 GET 獲取資源,使用 POST、PUT 新增或更新資源,使用 DELETE 刪除資源等
我們可以使用 curl 命令來執行上述操作:
$ curl -i -X PUT "localhost:9200/movie/adventure/1" -d '{"name": "Life of Pi", "actors": ["Suraj", "Irrfan"]}'複製程式碼
不過,本文推薦使用 httpie,類似 curl,但比 curl 更好用,將上面的命令換成 httpie,如下:
$ http put :9200/movie/adventure/1 name="Life of Pi" actors:='["Suraj", "Irrfan"]'複製程式碼
上面的命令結果如下:
HTTP/1.1 201 Created
Location: /movie/adventure/1
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
{
"_id": "1",
"_index": "movie",
"_shards": {
"failed": 0,
"successful": 1,
"total": 2
},
"_type": "adventure",
"_version": 1,
"created": true,
"result": "created"
}複製程式碼
可以看到,我們已經成功建立了一個 _index
為 movie,_type
為 adventure,_id
為 1 的文件。
我們通過 GET 請求來檢視這個文件的資訊:
$ http :9200/movie/adventure/1複製程式碼
結果如下:
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
{
"_id": "1",
"_index": "movie",
"_source": {
"actors": [
"Suraj",
"Irrfan"
],
"name": "Life of Pi"
},
"_type": "adventure",
"_version": 1,
"found": true
}複製程式碼
可以看到,原始的文件資料存在了 _source
欄位中。
如果我們的資料沒有 id,也可以讓 Elasticsearch 自動為我們生成,此時要使用 POST 請求,形式如下:
POST /movie/adventure/
{
"name": "Life of Pi"
}複製程式碼
更新整個文件
當我們使用 PUT 方法指明文件的 _index
, _type
和 _id
時,如果 _id
已存在,則新文件會替換舊文件,此時文件的 _version
會增加 1,並且 _created
欄位為 false。比如:
$ http put :9200/movie/adventure/1 name="Life of Pi"複製程式碼
結果如下:
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
{
"_id": "1",
"_index": "movie",
"_shards": {
"failed": 0,
"successful": 1,
"total": 2
},
"_type": "adventure",
"_version": 2,
"created": false,
"result": "updated"
}複製程式碼
使用 GET 請求檢視新文件的資料:
$ http :9200/movie/adventure/1複製程式碼
結果如下:
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
{
"_id": "1",
"_index": "movie",
"_source": {
"name": "Life of Pi"
},
"_type": "adventure",
"_version": 2,
"found": true
}複製程式碼
可以看到,actors 這個欄位已經不存在了,文件的 _version
變成了 2。
因此,為了避免在誤操作的情況下,原文件被替換,我們可以使用 _create
這個 API,表示只在文件不存在的情況下才建立新文件(返回 201 Created),如果文件存在則不做任何操作(返回 409 Conflict),命令如下:
$ http put :9200/movie/adventure/1/_create name="Life of Pi"複製程式碼
由於文件 id 存在,會返回 409 Conflict。
區域性更新
在有些情況下,我們只想更新文件的區域性,而不是整個文件,這時我們可以使用 _update
這個 API。
現在,待更新的文件資訊如下:
{
"_id": "1",
"_index": "movie",
"_source": {
"name": "Life of Pi"
},
"_type": "adventure",
"_version": 2,
"found": true
}複製程式碼
最簡單的 update 請求接受一個區域性文件引數 doc
,它會合併到現有文件中:將物件合併在一起,存在的標量欄位被覆蓋,新欄位被新增。
形式如下:
POST /movie/adventure/1/_update
{
"doc": {
"name": "life of pi"
}
}複製程式碼
由於有巢狀欄位,我們可以這樣使用 http(這裡需要注意使用 POST 方法):
$ echo '{"doc": {"actors": ["Suraj", "Irrfan"]}}' | http post :9200/movie/adventure/1/_update複製程式碼
上面的命令中,我們新增了一個新的欄位:actors,結果如下:
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
{
"_id": "1",
"_index": "movie",
"_shards": {
"failed": 0,
"successful": 1,
"total": 2
},
"_type": "adventure",
"_version": 3,
"result": "updated"
}複製程式碼
可以看到,_version 增加了 1,result 的結果是 updated。
檢索文件
檢索某個文件
要檢索某個文件很簡單,我們只需使用 GET 請求並指出文件的 index, type, id 就可以了,比如:
$ http :9200/movie/adventure/1/複製程式碼
響應內容會包含文件的元資訊,文件的原始資料存在 _source
欄位中。
我們也可以直接檢索出文件的 _source
欄位,如下:
$ http :9200/movie/adventure/1/_source複製程式碼
檢索所有文件
我們可以使用 _search
這個 API 檢索出所有的文件,命令如下:
$ http :9200/movie/adventure/_search複製程式碼
返回結果如下:
{
"_shards": {
"failed": 0,
"successful": 5,
"total": 5
},
"hits": {
"hits": [
{
"_id": "1",
"_index": "movie",
"_score": 1.0,
"_source": {
"actors": [
"Suraj",
"Irrfan"
],
"name": "Life of Pi"
},
"_type": "adventure"
}
],
"max_score": 1.0,
"total": 1
},
"timed_out": false,
"took": 299
}複製程式碼
可以看到,hits
這個 object 包含了 hits 陣列,total 等欄位,其中,hits 陣列包含了所有的文件,這裡只有一個文件,total 表明了文件的數量,預設情況下會返回前 10 個結果。我們也可以設定 From/Size
引數來獲取某一範圍的文件,可參考這裡,比如:
$ http :9200/movie/adventure/_search?from=1&size=5複製程式碼
當不指定 from 和 size 時,會使用預設值,其中 from 的預設值是 0,size 的預設值是 10。
檢索某些欄位
有時候,我們只需檢索文件的個別欄位,這時可以使用 _source
引數,多個欄位可以使用逗號分隔,如下所示:
$ http :9200/movie/adventure/1?_source=name
$ http :9200/movie/adventure/1?_source=name,actors複製程式碼
query string 搜尋
query string 搜尋以 q=field:value
的形式進行查詢,比如查詢 name 欄位含有 life 的電影:
$ http :9200/movie/adventure/_search?q=name:life複製程式碼
DSL 搜尋
上面的 query string 搜尋比較輕量級,只適用於簡單的場合。Elasticsearch 提供了更為強大的 DSL(Domain Specific Language)查詢語言,適用於複雜的搜尋場景,比如全文搜尋。我們可以將上面的 query string 搜尋轉換為 DSL 搜尋,如下:
GET /movie/adventure/_search
{
"query" : {
"match" : {
"name" : "life"
}
}
}複製程式碼
如果使用 httpie,可以這樣:
$ echo '{"query": {"match": {"name": "life"}}}' | http get :9200/movie/adventure/_search複製程式碼
如果使用 curl,可以這樣:
$ curl -X GET "127.0.0.1:9200/movie/adventure/_search" -d '{"query": {"match": {"name": "life"}}}'複製程式碼
文件是否存在
使用 HEAD 方法檢視文件是否存在:
$ http head :9200/movie/adventure/1複製程式碼
如果文件存在則返回 200,否則返回 404。
刪除文件
使用 DELETE 方法刪除文件:
$ http delete :9200/movie/adventure/1複製程式碼
小結
- Elasticsearch 通過簡單的 RESTful API 來隱藏 Lucene 的複雜性,從而讓全文搜尋變得簡單
- 在建立文件時,我們可以用 POST 方法指定將文件新增到某個
_index/_type
下,來讓 Elasticsearch自動生成唯一的_id
;而用 PUT 方法指定將文件的_index/_type/_id
本文由 funhacks 發表於個人部落格,採用 Creative Commons BY-NC-ND 4.0(自由轉載-保持署名-非商用-禁止演繹)協議釋出。
非商業轉載請註明作者及出處。商業轉載請聯絡作者本人。
本文標題為: Elasticsearch 入門使用
本文連結為: funhacks.net/2017/03/22/…