Elasticsearch-更新現有文檔


ES-更新現有文檔

ES的更新API允許發送文檔所需要做的修改,而且API會返回一個答復,告知操作是否成功。
更新流程如下

 

1. 檢索現有的文檔。為了使這步奏效,必須打開_source字段,否則ES並不知道原有文檔的內容。
2. 進行制定的修改。例如,如果文檔是
{"name":"Elasticsearch Denver","organizer":"Lee"}
修改組織者,修改后的文檔是
{"name":"Elasticsearch Denver","organizer":"Roy"}
3. 刪除舊的文檔,在其原有位置索引新的文檔(包含修改的內容)。


使用更新API

(1).通過發送部分文檔,增加或替換現有文檔的一部分。
(2).如果文檔不存在,當發送部分文檔或者腳本時,請確認文檔是否被創建。如果文檔之前不存在,可以指定被索引的文檔原始內容
(3).發送腳本來更新文檔。

1. 發送部分文檔

發送部分的文檔內容,包含所需要設置的字段值,是更新一個或多個字段最容易的方法。為了實現這個操作,需要將這些信息通過HTTP POST請求發送到該文檔URL的_update端點。
先插入一條新文檔

curl -XPUT 'localhost:9200/get-together/group/1?pretty' -d '{
"name":"Elasticsearch Denver",
"organizer":"Lee"
}'

更新

curl -XPOST 'localhost:9200/get-together/group/1/_update?pretty' -d '{
"doc":{
"organizer":"Roy"
}
}'

這條命令設置了在doc下指定的字段,將其值設置為所提供的值。它並不考慮這些字段之前的值,也不考慮這些字段之前是否存在。如果之前整個文檔是不存在的,那么更新操作會失敗,並提示文檔缺失。
驗證

FengZhendeMacBook-Pro:cv FengZhen$ curl 'localhost:9200/get-together/group/1?pretty'
{
"_index" : "get-together",
"_type" : "group",
"_id" : "1",
"_version" : 2,
"found" : true,
"_source" : {
"name" : "Elasticsearch Denver",
"organizer" : "Roy"
}
}

注意:在更新的時候,要牢記可能存在沖突。例如,如果將分組的組織者修改為”Roy”,還有人將其修改為”Feng”,那么其中一次更新會被另一次覆蓋。為了控制這種局面,可以使用版本功能。

2. 使用upsert來創建尚不存在的文檔

為了處理更新時文檔不存在的情況,可以使用upsert.(update和insert的混成詞)
如果被更新的文檔不存在,可以在JSON的upsert部分中添加一個初始文檔用於索引。

curl -XPOST 'localhost:9200/get-together/group/1/_update?pretty' -d '{
"doc":{
"organizer":"Roy"
},
"upsert":{
"name":"Elasticsearch Denver",
"organizer":"Roy"
}
}'

3. 通過腳本來更新文檔

(1).默認的腳本語言是Groovy。它的語法和Java相似,但是作為腳本,使用更加簡單。
(2).由於更新要獲得現有文檔的_source內容,修改並重新索引新的文檔,因此腳本會修改_source中的字段。使用ctx._source來使用_source,使用ctx._source[字段名]來引用某個指定的字段。
(3).如果需要變量,推薦在params下作為參數單獨定義,和腳本本身區分開來。這是因為腳本需要編譯,一旦編譯完成,就會被緩存。如果使用不同參數,多次運行同樣的腳本,腳本只需要編譯一次。之后的運行都會從緩存中獲取現有腳本。相比每次不同的腳本,這樣運行會更快,因為不同的腳本每次都需要編譯。

注意:由於安全因素,通過API運行以下腳本可能默認的被禁止,這取決於所運行的ES版本。這成為動態腳本,在elasticsearch.yml中將script.disable_dynamic設置為false,就可以打開這個功能。

curl -XPUT 'localhost:9200/online-shop/shirts/1?pretty' -d '{
"caption":"Learning Elasticsearch",
"price":15
}'

curl -XPOST 'localhost:9200/online-shop/shirts/1/_update?pretty' -d '{
"script":"ctx._source.price += price_diff",
"params":{
"price_diff":10
}
}'

FengZhendeMacBook-Pro:cv FengZhen$ curl 'localhost:9200/online-shop/_search?pretty'
{
"took" : 37,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 1.0,
"hits" : [ {
"_index" : "online-shop",
"_type" : "shirts",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"caption" : "Learning Elasticsearch",
"price" : 25
}
} ]
}
}

可以看到,這里使用的是ctx._source.price而不是ctx._source[‘price’].這是指向price字段的另一個方法。

通過版本來實現並發控制

如果同一時刻多次更新都在執行,將面臨並發的問題。ES支持並發控制,為每篇文檔設置了一個版本號。最初被索引的文檔版本是1。當更新操作重新索引它的時候,版本號就設置為2了。如果與此同時另一個更新將版本設置為2,那么就會產生沖突,目前的更新也會失敗。可以重試這個更新操作,如果不再有沖突,那么版本就會設置為3.
並發控制流程如下

curl -XPOST 'localhost:9200/online-shop/shirts/1/_update?pretty' -d '{
"script":"Thread.sleep(10000);ctx._source.price = 2"
}'

curl -XPOST 'localhost:9200/online-shop/shirts/1/_update?pretty' -d '{
"script":"ctx._source.caption = \"Knowing Elasticsearch\""
}'

 

(1).索引文檔然后更新它
(2).更新1在后台啟動,有一定時間的等待(睡眠)
(3).在睡眠期間,發出另一個update的指令來修改文檔。變化發送在更新1獲取原有文檔之后、重新索引回去之前。
(4).由於文檔的版本已經變為2,更新1就會失敗,而不會取消更新2所做的修改。這個時候有就機會重試更新1,然后進行版本為3的修改。
這種並發控制稱為樂觀鎖,因為它允許並行的操作並假設沖突時很少出現的,真的出現時就拋出錯誤。它和悲觀鎖是相對的,悲觀鎖通過鎖住可能引起沖突的操作,第一時間預防沖突。

1. 沖突發生時自動重試更新操作

當版本沖突出現的時候,可以再應用程序中處理。如果是更新操作,可以再次嘗試,但是也可以通過設置retry_on_conflict參數,讓ES自動重試

curl -XPOST 'localhost:9200/online-shop/shirts/1/_update?retry_on_conflict=3' -d '{
"script":"ctx._source.price = 2"
}'


2.索引文檔的時候使用版本號

更新文檔的另一個方法是不使用更新API,而是在同一個索引、類型和ID之處索引一個新的文檔。這樣的操作會覆蓋現有的文檔,這種情況下仍然可以使用版本字段來進行並發控制。為了實現這一點,要設置HTTP請求中的version參數。其值應該是期望該文檔要擁有的版本號(當前的版本號)。如:如果認為現有的版本已經是3,一個重新索引的請求如下

curl -XPUT 'localhost:9200/online-shop/shirts/1?version=3' -d '{
"caption":"i konw about es versioning",
"price":"5"
}'

如果現有的版本實際上不是3,name這個操作就會拋出版本沖突異常並失敗。
有了版本號就可以安全的索引和更新文檔了。

使用外部版本

目前為止都是使用ES的內部版本,每次操作,無論是索引還是更新,ES都會自動的增加版本號。如果數據源是另一個數據存儲,也許在那里有版本控制系統。例如,一種基於時間戳的系統。
為了使用外部版本,需要為每次請求添加version_type=external,以及版本號

curl -XPUT 'localhost:9200/online-shop/shirts/1?version=4&version_type=external' -d '{
"caption":"i konw about es versioning",
"price":"5"
}'

這將使ES接受任何版本號,只要比現有版本高,而且ES也不會自己增加版本號。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM