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에 대해서 알아봤어요.

 

 

끝.