검색 엔진 Search/Elastic Search

Elasticsearch 문서 업데이트 (document update)

이미 인덱싱되어 있는 문서의 데이터를 변경할 경우에는 Update 요청을 사용합니다.

Update 요청으로 어떻게 지정된 스크립트를 사용하여 문서를 업데이트하는지 예제와 함께 주의할 점도 알아봅시다!

 

Update

Request 는 다음과 같이 보낼 수 있습니다.

POST /<index>/_update/<_id>

 

 

Update 실행 시 문서의 버전이 증가합니다.

 

스크립트와 함께 문서 업데이트가 가능한데, 이때 스크립트는 문서를 업데이트하거나 삭제하거나 수정을 건너뛸 수도 있습니다.

 

문서 일부분만 전달하여 기존 문서와 병합할 수도 있습니다.

굳이 전체 문서 내용을 전달할 필요가 없어 기본 인덱싱 파이프라인과 다르게 수행되어야 하는 등 다른 파이프라인으로 업데이트 되는 내용에 대하여 부분 업데이트가 가능합니다.

 

인덱스에서 문서를 가져와 특정 스크립트를 수행하고, 그 결과를 다시 인덱싱하는 과정으로 진행됩니다.

여전히 문서는 리인덱싱 되어야하지만, GET 이나 인덱스 작업 간의 버전 충돌을 줄여줄 수 있습니다.

 

_source 필드를 사용 가능하도록 설정해야 update 시 사용 가능합니다.

스크립트에서 _source 필드 이외에도, _index_type_id_version_routing, 그리고 현재의 timestamp에도 접근 가능합니다.

 

Query Parameter

쿼리 파라미터에는 많은 목록이 있지만, 그 중 몇가지만 살펴보겠습니다.

  • require_alias : 디폴트는 false입니다. true 로 설정하면, 인덱스 자체가 아니라 alias 로만 요청을 보낼 수 있게 됩니다. 서비스 구현 방식에 따라 해당 옵션을 사용할 수 있습니다.
  • refresh: 디폴트는 false입니다. true 로 설정하면, ES는 문서 업데이트 요청에 영향을 받는 샤드를 refresh 하여 이 작업이 검색에 표시되도록 합니다. wait_for 인 경우 refresh를 기다렸다가 이 작업이 검색에 표시되도록 하며, false인 경우 refresh로 아무것도 하지 않습니다.

 

Example

다음과 같이 업데이트 요청시 스크립트를 사용하여 이미 존재하는 문서의 필드 값을 증가시키거나, 파라미터 값을 받아 필드에 추가하거나, 이미 존재하는 필드 값 중 특정 값을 제거할 수 있습니다.

PUT test/_doc/1
{
  "counter" : 1,
  "tags" : ["red"]
}

# 증가
POST test/_update/1
{
  "script" : {
    "source": "ctx._source.counter += params.count",
    "lang": "painless",
    "params" : {
      "count" : 4
    }
  }
}

# 값 추가
POST test/_update/1
{
  "script": {
    "source": "ctx._source.tags.add(params.tag)",
    "lang": "painless",
    "params": {
      "tag": "blue"
    }
  }
}

# 값 삭제
POST test/_update/1
{
  "script": {
    "source": "if (ctx._source.tags.contains(params.tag)) { ctx._source.tags.remove(ctx._source.tags.indexOf(params.tag)) }",
    "lang": "painless",
    "params": {
      "tag": "blue"
    }
  }
}

 

또는 부분 업데이트를 통해 새로운 필드를 추가할 수도 있습니다.

POST test/_update/1
{
  "doc": {
    "field_name": "new_field"
  }
}

*업데이트 요청시 doc 과 script가 동시에 명시되면, doc 이 무시됩니다.

 

만약, 기존 문서를 전혀 바꾸지 않는 업데이트 요청이라면, 어떻게 될까요?

기존 문서를 바꾸지 않는 경우, 문서 업데이트 요청은 무시되고, "result": "noop” 를 결과로 반환하게 됩니다.

 

Upsert

문서가 존재하지 않는 경우 새 문서를 생성하고 upsert 요소의 내용을 새 문서로 삽입할 수 있습니다.

존재하는 문서의 경우에는 함께 명시하는 script 를 적용합니다.

POST test/_update/1
{
  "script": {
    "source": "ctx._source.counter += params.count",
    "lang": "painless",
    "params": {
      "count": 4
    }
  },
  "upsert": {
    "counter": 1
  }
}

 

upsert 사용시에는 다음의 파라미터를 설정할 수 있습니다.

  • scripted_upsert : true일 경우 문서 존재여부에 상관없이 스크립트 실행 가능합니다.
  • doc_as_upsert: true일 경우, partial doc 과 upsert doc 를 동시에 사용하는 것 대신, doc의 내용을 upsert 값으로 사용할 수 있으나, 이때 인제스트 파이프라인은 사용 불가합니다.

 

 

주의할 점

문서의 일부만 업데이트하면 soft delete가 일어나고 새로운 문서가 생기는 방식으로 ES 내부에서 작동합니다.

완전히 데이터가 삭제되는 것은 아니기 때문에, 어찌되었든 계속 인덱스 크기가 커지게 됩니다.

이를 해결할 수 있는 방법으로는 주기적으로 직접 reindexing을 진행하거나, 일정 기간 또는 인덱스 크기에 따라 인덱스 노드 상태를 변화하며 관리할 수 있는 인덱스 라이프 사이클 기능을 이용할 수 있습니다.