Elasticsearch Basic Operation

Elasticsearch 筆記(基本操作)

基本指令

檢查 Server 狀態

curl localhost:9200
------------------------------
正常運作的話會返回類似下面的 json
{
"name" : "Apalla",
"cluster_name" : "soar-local-es",
"cluster_uuid" : "uBfjdJ-kSkCM8F6Bg7VNDg",
"version" : {
"number" : "2.4.1",
"build_hash" : "c67dc32e24162035d18d6fe1e952c4cbcbe79d16",
"build_timestamp" : "2016-09-27T18:57:55Z",
"build_snapshot" : false,
"lucene_version" : "5.5.2"
},
"tagline" : "You Know, for Search"
}

列出所有 index

curl localhost:9200/_cat/indices?v
類似出現下面的畫面,顯示每個index的狀態跟資料筆數等資訊
--------------------------------------
health status index pri rep docs.count docs.deleted store.size pri.store.size
green open quizfun 1 0 5417 301 11.9mb 11.9mb
green open soarlin 1 0 16 3 502.1kb 502.1kb
yellow open blogs 5 1 17 2 255kb 255kb

列出index下所有types

curl -XGET 'http://localhost:9200/myindex/_mapping?pretty'

回傳結果類似下方
{
"myindex": {
"mappings:: {
"type1": {
"properties" : {
...
}
}
"type2": {
"properties" : {
...
}
}
}
}
}

建立 index

curl -XPUT localhost:9200/[IndexName]?pretty
----------------------------
成功的話,回傳下面訊息
{
"acknowledged" : true
}

----------------------------
失敗的話,通常是index已經存在,則是下面訊息
{
"error" : {
"root_cause" : [ {
"type" : "index_already_exists_exception",
"reason" : "already exists",
"index" : "customer"
} ],
"type" : "index_already_exists_exception",
"reason" : "already exists",
"index" : "customer"
},
"status" : 400
}

刪除 index

curl -XDELETE localhost:9200/[IndexName]?pretty
----------------------------
成功的話,回傳下面訊息
{
"acknowledged" : true
}

建立資料index(基本單筆資料操作)

底下範例為官網提供,輸入一筆資料與回傳訊息

  • index: customer,
  • type: external,
  • id: 1
curl -XPUT 'localhost:9200/customer/external/1?pretty' -d '
{
"name": "John Doe"
}'
{
"_index" : "customer",
"_type" : "external",
"_id" : "1",
"_version" : 1,
"created" : true
}

取出資料 (使用id)

curl -XGET 'localhost:9200/customer/external/1?pretty'
{
"_index" : "customer",
"_type" : "external",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : { "name": "John Doe" }
}

更新資料 (使用id)

curl -XPOST 'localhost:9200/customer/external/1/_update?pretty' -d '
{
"doc": { "name": "Jane Doe", "age": 20 }
}'

# 需要原本資料在更新 ctx._source.xxxx (實測失敗了)
curl -XPOST 'localhost:9200/customer/external/1/_update?pretty' -d '
{
"script" : "ctx._source.age += 5"
}'

刪除資料 (使用id)

curl -XDELETE 'localhost:9200/customer/external/1?pretty'

重要指令

建立資料index (批次處理資料)

使用 bulk 方法將要處理的資料依次為

  • 操作方法帶id, 如:建立index, 更新資料, 刪除資料
  • 資料內容(刪除時不需要)
操作方法 資料內容
建立index 資料 id {“index”:{“_id”:”1”}}
建立index 資料內容 {“name”: “John Doe”}
更新資料 依據 id {“update”:{“_id”:”1”}}
更新資料 資料內容 {“doc”: {“name”: “Mary Jane”, “age”: 23} }
建立index 資料id {“index”:{“_id”:”2”}}
建立index 資料內容 {“name”: “John Doe”}
刪除資料 資料id {“delete”:{“_id”:”2”}}
curl -XPOST 'localhost:9200/customer/external/_bulk?pretty' -d '
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"update":{"_id":"1"}}
{"doc": {"name": "Mary Jane", "age": 23} }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }
{"delete":{"_id":"2"}}
'

搜尋指令

使用搜尋引擎(elasticsearch or solr)的好處就是,搜尋的結果,會將搜尋結果的資料,依據加權後總分列出來,一般關聯式資料庫使用 like %keyword% 的話,只能找出所有含有 keyword 的資料,無法根據搜尋字詞來找出關聯程度從高到低的排序過資料。

基本方法

搜尋index下所有資料,回傳資料說明如下:

  • took: 在 Elasticsearch 內搜尋所花費的時間(單位: milliseconds, 毫秒, 千分之一秒)
  • timed_out: 這次搜尋是否有超時(需要設定超時時間)
  • _shards: 搜尋了多少個片段,成功與失敗個數
  • hits: 搜尋結果回傳
  • hits.total: 搜尋回傳資料筆數
  • hits.max_score: 搜尋結果最高分數
curl 'localhost:9200/customer/_search?q=*&pretty'

搜尋的結果類似下方資料
------------------------
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 1.0,
"hits" : [ {
"_index" : "customer",
"_type" : "external",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "John Wang",
"age" : 23
}
}, {
"_index" : "customer",
"_type" : "external",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"name" : "Mary Wang"
}
} ]
}
}

針對所有欄位搜尋同一關鍵字

curl 'localhost:9200/customer/_search?q=wang&pretty'

使用 query(Query DSL) 字串做比較精確的搜尋

curl -XPOST 'localhost:9200/customer/_search?pretty' -d '
{
"query": { "match_all": {} },
"size": 1
}'

curl -XPOST 'localhost:9200/blog/_search?pretty' -d '
{
"query": { "match": { "content": "postcss" } }
}'

curl -XPOST 'localhost:9200/blog/_search?pretty' -d '
{
"query": {
"bool": {
"must": [
{ "match": { "content": "server" } },
{ "match": { "tags": "linux" } }
]
}
}
}'

後記

建立 Mapping

因為在塞資料建立 index 前,還是得先確認一下之後資料的格式,寫好一個比較OK的資料 mapping,才能再塞資料的時候確保塞進去的資料格式能夠最佳化被處理,底下是之前找到別人針對 hexo blog 資料格式設定給 elasticsearch 的 mapping,分享給有需要的人參考,當然這是有安裝 ik 中文斷詞套件了

  • index: blog
  • type: article
{
"settings": {
"number_of_shards" : 1,
"number_of_replicas" : 0
},
"_default_": {},
"mappings": {
"article": {
"dynamic": false,
"date_detection": false,
"_all": {
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart",
"term_vector": "no"
},
"properties": {
"title": {
"type": "string" ,
"term_vector": "with_positions_offsets",
"include_in_all": true,
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart",
"boost": 2
},
"slug": {
"type": "string",
"index": "no"
},
"date": {
"type": "date",
"format": "epoch_second"
},
"updated": {
"type": "date",
"format": "epoch_second"
},
"categories": {
"type": "string",
"index": "not_analyzed"
},
"tags": {
"type": "string",
"index": "not_analyzed"
},
"content": {
"type": "string",
"term_vector": "with_positions_offsets",
"include_in_all": true,
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart",
"norms": { "enabled": false }
}
}
}
}
}

建立 Server 與 API

一開始摸索 Elasticsearch 只是為了給自己 blog 做文章搜尋,所以還得弄一台 server 出來,然後把環境都弄好後,另外再用 node.js 來開發一個 API 介面,主要是為了避免 9200 這個 port 一直裸露在外,到時候被別人發現拿去玩。但是做個 API 介面,弄的很簡陋,似乎也有可能被別人拿去玩…XD。

我想一般人用 node.js 來做 RESTful API 介面的話,應該首選再搭配個 Express Framework 就可以很快搞定了吧!但是!!之前被__正豪__花言巧語騙去學 Koa Framework,結果只好硬著頭皮用 koa 來寫,真的快搞死自己了。

寫的過程才發現原來自己對於 ES6 的 Generator 還是很不熟練,在寫 API 的時候,一度快崩潰,因為有太多 non-blocking callback 要處理,只好還是乖乖地使用 Promise 這方法,雖然可以減少寫出波動拳語法,但還是覺得寫超過三個 promise 後,也是很可怕的事情。

附上部分自己連續五個 promise 的 code, 覺得慘不忍睹

yield check_db_json(tmpfile)
.then(check_last_index_file)
.then(get_last_index)
.then(bulkArticles)
.then(update_last_index_file, function(res){console.log(res.msg);})
.then(function(res){
if (res.status === 'ok' || res.status === 'done') {
console.log(res.msg);
RESULT.update = 'success';
}
}, function(res){
console.log(res.msg);
});

因為 blog 是使用 hexo+github 的方式,所以寫好 markdown 語法後,透過 hexo 的處理可以產生一個 db.json 檔案,所以可以直接使用這個檔案來建立文章 index,還算方便,而且有人寫好 python 的工具來處理

但是因為 server 架在雲端,又不開放 9200 port 來直接存取,所以花了很久的時間再將 python 更新文章 index 的行為重新改寫,中間遇到檔案必需上傳,所以還得先弄一個簡單的介面來上傳 db.json,這邊就得再去找 koa 的範例來看

雖然花了好幾天在改寫這兩百行出頭的 ptyhon,但是改寫後還是功能沒人家齊全,但是…我有多一個資料上傳介面,所以多了 form 表單要處理,所以也是做得很辛苦,寫完也是兩百多行…XD

Refrence

另外,也可以參考別人寫的教學,但是我道行太淺,有大部分有看沒有懂