Elasticsearch C.R.U.D에서 책이 한 권 팔렸다고 가정하고 기존 in_stock 값에서 하나 뺀 값을 넣어서 업데이트를 해줬었는데 하나 빼는 건 암산으로 가능한데 10개 이상 빼면 암산이 쉽지 않을 것 같아요. 나는 몇 개가 팔렸는지만 이야기해주고 뺄셈은 콤퓨-타가 알아서 해주면 정말 좋을 텐데요.
Elasticsearch는 위와 같은 기능을 Script라는 이름으로 제공하고 있어요.
https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting.html
Scripting | Elasticsearch Guide [7.14] | Elastic
With scripting, you can evaluate custom expressions in Elasticsearch. For example, you can use a script to return a computed value as a field or evaluate a custom score for a query. The default scripting language is Painless. Additional lang plugins are av
www.elastic.co
Scripted Update
해리포터와 비밀의 방이 86권 팔렸어요. 어떻게 업데이트해야 할까요?
먼저 해리포터와 비밀의 방 도큐먼트를 찾아서 아이디를 확인하고 업데이트를 진행해야겠지요?
GET books/_search
{
"query": {"term": {
"title": {
"value": "secrets"
}}}}
{
...
"hits" : [
{
"_index" : "books",
"_type" : "_doc",
"_id" : "LSeSu3sBSx-nWPvrcdo5",
"_score" : 0.9611689,
"_source" : {
"title" : "Harry Potter and the Chamber of Secrets",
"price" : 29.99,
"rating" : 4.9,
"in_stock" : 32049
}}]}}
해리포터와 비밀의 방을 title에서 secrets로 검색해서 찾았어요. id와 in_stock값을 확인하고 이제 스크립트를 사용한 업데이트를 해보아요.
POST books/_update/LSeSu3sBSx-nWPvrcdo5
{
"script": {
"source": "ctx._source.in_stock -= 86"
}
}
요청은 이렇게 보냈어요. 그리고 결과는 아래와 같이 왔네요.
result: updated라고 나온 걸 보니 뭔가 바뀌긴 바뀌었나 봐요. 확인해볼게요.
32049 - 86 = 31963 맞는 거 같네요. 뺄셈 잘하네요. 매번 이렇게 손으로 작성해서 요청을 보내는 것도 좀 번거로운 일이에요. 한 번 딱 작성해 놓고 넘겨받는 값만큼만 딱 딱 빼주면 더 편할 것 같아요. 그러면 이렇게 작성할 수 있어요.
POST books/_update/LSeSu3sBSx-nWPvrcdo5
{
"script": {
"source": "ctx._source.in_stock -= params.quantity",
"params": {"quantity": 4}
}
}
그리고 """{코드}""" 이렇게 큰따옴표 3개를 붙여서 사용하면
POST books/_update/LSeSu3sBSx-nWPvrcdo5
{
"script": {
"source": """if(ctx._source.in_stock <= 0) {
ctx._source.in_stock = 0;
} else {
ctx._source.in_stock --;
}
"""
}
}
이렇게 여러 줄에 걸쳐서 스크립트를 작성할 수 있어요.
Upsert
도큐먼트를 업데이트하는 또 다른 방법으로 upsert라는 게 있어요. Upsert는 조건부로 도큐먼트를 update 하거나 insert 하는 것을 의미해요. Upsert 하는 방법은 아래와 같아요.
POST /books/_update/HarryPotter-Book03
{
"script": {
"source": "ctx._source.in_stock = 31586"
},
"upsert": {
"title": "Harry Potter and the Prisoner of Azkaban",
"price": 29.99,
"rating": 4.9,
"in_stock": 315
}
}
{
"_index" : "books",
"_type" : "_doc",
"_id" : "HarryPotter-Book03",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 2,
"failed" : 0
},
"_seq_no" : 2,
"_primary_term" : 1
}
위에서 보여주는 요청과 응답에서 눈여겨볼 부분이 두 군데 있어요. 먼저 POST 요청을 하면서 인덱스명 다음에 HarryPotter-Book3라고 아이디를 준 것. 그리고 응답에서 result: created라는 부분이에요.
지금까지는 도큐먼트를 생성하면서 아이디는 따로 주지 않아서 엘라스틱서치가 자동으로 생성해줬었는데요 도큐먼트를 생성할 때 저 부분에 아이디 값을 넣어주면 그 값을 아이디로 사용해요. 물론 upsert 할 때도 마찬가지구요. 안에 내용을 보면 scripted update와 크게 다른 부분은 없어요 재고를 315로 바꿔주세요 라는 건데 밑에 upsert 부분이 추가가 된 거예요 upsert의 내용은 기존에 다른 책 도큐먼트를 만들 때와 같아요. 그래서 저 요청을 나눠보면 books 인덱스에 HarryPotter-Book3이라는 id를 가진 도큐먼트에서 in_stock을 315로 바꿔주세요. 그런데 그런 도큐먼트가 없으면 title은 이렇게 price는 뭐 뭐는 뭐 이렇게 만들어주세요. 라는 뜻이에요.
응답에서 result: created라고 뜨는 건 새로운 도큐먼트가 생성되었다는 뜻일까요?
같은 요청을 한번 더 보내면 어떻게 될까요?
{
"_index" : "books",
"_type" : "_doc",
"_id" : "HarryPotter-Book03",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 2,
"failed" : 0
},
"_seq_no" : 3,
"_primary_term" : 1
}
GET /books/_doc/HarryPotter-Book03
{
"_index" : "books",
"_type" : "_doc",
"_id" : "HarryPotter-Book03",
"_version" : 2,
"_seq_no" : 3,
"_primary_term" : 1,
"found" : true,
"_source" : {
"title" : "Harry Potter and the Prisoner of Azkaban",
"price" : 29.99,
"rating" : 4.9,
"in_stock" : 31586
}
}
같은 요청을 다시 보내고 받은 응답과 도큐먼트 내용을 확인해 본 값이에요.
이렇게 scripted update와 upsert에 대해서 알아봤어요.
끝.