上一篇文章 中,我們使用了 POST /users/_update/1
的方式來更新 _id
為 1 的 user 的值。
但這樣的操作方式,再結合了商業邏輯之後,就會需要先 GET
去拿到值接著在判斷是不是要增加或減少。
但 Elasticsearch ( 以下簡稱 ES ) 其實可以讓我們寫 script,也就是把一些判斷式放到 payload 裡面。
一樣使用昨天的例子,我們來針對使用者的年紀進行增加。
ctx 代表的是 context
上面的方式寫起來就很像程式語言 --
就是針對目前的值減 1 的意思,反之 ++ 則是加 1。
我們可以想像這個是整個 document,而後面的 ._source
其實就很像是取值。
複習一下,整個 document 物件的長相:
{
"_index": "users",
"_id": "1",
"_version": 6,
"_seq_no": 6,
"_primary_term": 1,
"found": true,
"_source": {
"name": "John",
"age": 42,
"gender": "male",
"habits": [
"baseball"
]
}
}
也可以直接使用 =
的方式來更新值:
更甚者,還可以支援 params 的寫法,這樣就可以透過應用程式處理完商業邏輯後,拋到 ES 進行更新。
返回的結果
這邊有一個很重要的概念,這種 request 以及 response 的模式,很重要的是回傳的結果。
而在 _update
的 endpoint 中,可以看到 result
這個 key 都是回傳 updated
代表的是成功更新,那失敗呢?
{
"_index": "users",
"_id": "1",
"_version": 9,
"result": "updated", // 更新成功
"_shards": {
"total": 3,
"successful": 3,
"failed": 0
},
"_seq_no": 9,
"_primary_term": 1
}
上一篇文章 中,有一個點我忘記提到了,如果不使用 script
的方式去更新值的話,當 ES 發現該值沒有任何變化,會回傳 noop
的結果。
假設 user 1 的 age = 20,接著我們嘗試用 doc
的方式直接更新成 20,會回傳 noop
代表什麼都沒發生:
而當今天使用 script
的方式去更新,不論值有變與否,都會回傳 updated
,這或許不會是個問題,但有時候結合商業邏輯之後,我們會想要知道更多,那該怎麼做?
可以在 script
裡面利用 ctx.op
的方式來控制回傳的 result
op 代表的 operation,執行的動作
假設今天使用者的年紀等於 20 歲,就不做更新,並且回傳 noop
該怎麼做呢?其餘一律改成 20 歲:
神奇的魔法!ES 的 script
內是可以寫 if else
的判斷式的!
而我們也成功地看到回傳的結果是 noop
,這樣就可以讓我們在使用這個 endpoint 的同時,知道這個操作的結果是到底真的更新,還是沒有,方便應用程式做後續的處理。
而關於 ctx.op
,甚至還可以使用 delete
,當某個條件式達成時,刪除掉這個 document。
隨便寫一下,大概像是這樣:
{
"script": {
"source": """
if (ctx._source.age == 20) {
ctx.op = 'delete';
}
ctx._source.age = 20;
"""
}
}
所以當 ES 發現這個使用者是 20 歲,就會刪掉這個 document,我知道我的例子很爛。
但真實的使用場景可以變成,當商品的庫存為 0 的時候,刪除掉 document,因為我們不希望使用者在搜尋到這項商品之類的。
總之,ES 的 _update
API 搭配 script
的方式真的非常實用。