본문 바로가기
OpenSearch (ElasticSearch)

[4] 인덱스 데이터

by parkjp 2022. 2. 18.

OpenSearch에는 인덱스 API와 _bluk API 두 가지가 있다.

 

새로운 데이터가 점진적으로 ( 소규모 비즈니스의 고객 주문 등 ) 쌓이는 구조의 경우 인덱스 API를 사용하여 개별적으로 문서를 추가할 수 있다.

데이터의 흐름이 더욱 천천히 쌓이는 경우 ( 마케팅 웹사이트에 대한 주간 업데이트 등 ) 파일을 생성하여 _bluk API로 보내는 것이 좋습니다.

많은 수의 문서에 대해 함께 묶어서 _bluk API를 사용하면 우수한 성능을 제공 받을 수 있습니다. 하지만 문서가 그보다 방대하다면 개별적으로 인덱스를 생성하는 것이 좋습니다.

 

 

데이터를 검색하려면 먼저 인덱스를 생성해야 합니다. OpenSearch는 고유 ID를 사용하여 각 문서를 식별합니다.

// Index API 요청
PUT <index>/_doc/<id>
{ "A JSON": "document" }

 

 

_bulk API에 대한 요청은 데이터 내부에서 인덱스와 ID를 지정하기 때문에 약간 다르게 보일 수 있습니다.

// _bulk API 요청
POST _bulk
{ "index": { "_index": "<index>", "_id": "<id>" } }
{ "A JSON": "document" }

 

 

bulk(대량의) 데이터는 마지막 줄을 포함하여 모든 줄 끝에 개행문자 '\n'를 붙여야 합니다.

Action and metadata\n
Optional document\n
Action and metadata\n
Optional document\n

 

 

curl 명령어로 bulk(대량의) 데이터를 인덱싱하려면, 데이터가 저장되어있는 폴더로 이동하여 아래 명령어를 수행해야 합니다.

// curl 명령어를 통한 대량 데이터 인덱싱 커멘드
curl -H "Content-Type: application/x-ndjson" -POST https://localhost:9200/data/_bulk -u 'admin:admin' --insecure --data-binary "@data.json"

만약 여러 _bulk API들 중 하나가 fail이 나도 OpenSearch는 다른 작업들을 계속 수행합니다.

 

 

 

OpenSearch는 존재하지 않는 index에 문서를 추가할 때 자동으로 index를 생성합니다. 또한 요청에 ID를 지정하지 않으면 자동으로 ID를 생성합니다. 아래는 간단한 예제입니다.

// 자동 ID 생성의 예
POST movies/_doc    // 자동 ID생성의 단점 : ID를 지정하지 않았기 때문에 나중에 문서를 업데이트하기 어려움. 이같은 요청을 10번 실행한다면 이 문서를 고유한 ID를 가진 10개의 다른 문서로 인덱싱함.
{ "title": "Spirited Away" }
 
 
// ID 지정하여 생성 (ID지정하여 생성 시 PUT으로 요청)
PUT movies/_doc/1   // ID를 지정하여 생성했기 때문에 이 명령을 10번 실행하면, _version필드가 10으로 증가된 index에 하나의 문서만 남게된다.
{ "title": "Spirited Away" }

 

 

인덱스는 기본적으로 하나의 기본 샤드와 하나의 복제본으로 설정됩니다. 기본값이 아닌 다른 설정을 원하면 문서를 추가하기 전에 색인을 생성해야 합니다.

// 인덱스 커스텀 설정
PUT more-movies
{ "settings": { "number_of_shards": 6, "number_of_replicas": 2 } }

 

 

:: 데이터 읽기

 

문서를 인덱싱 한 후 동일한 엔드포인트에 GET 요청을 보내 문서를 검색할 수 있습니다.

// 인덱스 GET 요청 및 응답
GET movies/_doc/1
{
  "_index" : "movies",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "title" : "Spirited Away"
  }
}
 
 
// _source 개체에서 문서를 볼 수 있습니다. 문서를 찾을 수 없는 경우 found 값은 false로 나타납니다.

 

// 다중 문서 검색
GET _mget
{
  "docs": [
    {
      "_index": "<index>",
      "_id": "<id>"
    },
    {
      "_index": "<index>",
      "_id": "<id>"
    }
  ]
}
 
 
// 단일 명령으로 여러 문서를 검색하려면 _mget 요청을 보내면 됩니다.
요청 데이터는 본문에 _index와 _id를 보내야하는 _bulk 요청과 유사합니다.

 

// 특정 필드 반환 검색
GET _mget
{
  "docs": [
    {
      "_index": "<index>",
      "_id": "<id>",
      "_source": "field1"
    },
    {
      "_index": "<index>",
      "_id": "<id>",
      "_source": "field2"
    }
  ]
}
 
 
// 문서가 있는지 확인하려면
HEAD movies/_doc/<doc-id>
 
 
// 문서가 있으면 200 OK 응답을 반환하고, 없으면 404 - Not Found 오류를 반환합니다.

 

 

:: 데이터 업데이트

 

문서에 기존 필드를 업데이트하거나 새 필드를 추가하려면 개체의 변경사항과 함께 POST _update 요청을 보냅니다.

// 데이터 추가 및 업데이트
POST movies/_update/1
{
  "doc": {
    "title": "Castle in the Sky", // 기존에 있는 필드
    "genre": ["Animation", "Fantasy"] // 기존에 없던 필드
  }
}
 
 
// 업데이트 후 GET 요청
GET movies/_doc/1
 
{
  "_index" : "movies",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 2,
  "_seq_no" : 1,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "title" : "Castle in the Sky",  // update 처리됨
    "genre" : [ // 새로 add 됨.
      "Animation",
      "Fantasy"
    ]
  }
}
 
 
// 문서에는 업데이트된 횟수를 추적할 수 있는 _version필드가 있습니다.
// POST 요청은 문서를 부분적으로 업데이트할 때 쓰입니다.
// 문서를 완전히 바꾸려면 PUT요청을 사용해야 합니다.
PUT movies/_doc/1
{
  "title": "Spirited Away"
}
 
 
// 위 PUT 요청에 해당 문서는 title 필드 하나만 있는 문서로 업데이트 됩니다.

 

 

upsert요청으로 문서가 이미 존재하는지 여부에 따라 조건부로 문서를 업데이트할 수 있습니다.

// 문서가 있는 경우 title 필드는 'Castle in the Sky'로 변경됩니다.
// 문서가 없는 경우 upsert 객체로 인덱싱 합니다.
POST movies/_update/2
{
  "doc": {
    "title": "Castle in the Sky"
  },
  "upsert": {
    "title": "Only Yesterday",
    "genre": ["Animation", "Fantasy"],
    "date": 1993
  }
}
 
 
// 응답 예시
{
  "_index" : "movies",
  "_type" : "_doc",
  "_id" : "2",
  "_version" : 2,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 3,
  "_primary_term" : 1
}
 
 
// 각 업데이트 작업에는 _seq_no와 _primary_term 두 개의 값으로 유니크가 걸려지게 됩니다.
 
 
// OpenSearch는 먼저 기본 샤드에 업데이트를 기록한 다음 변경 사항을 모든 복제본 샤드에 보냅니다.
 
 
// OpenSearch에서 여러 사용자가 동일한 index에 있는 동일한 문서를 업데이트하면 문제가 발생할 수 있습니다.
// 이 상황에서 다른 사용자는 기본 샤드에서 업데이트를 수신하기 전에 복제본에서 문서를 읽고 업데이트할 수 있습니다.
// 이러한 상황을 방지하려면 요청헤더에 _seq_no와 _primary_term값을 사용해야 합니다.
POST movies/_update/2?if_seq_no=3&if_primary_term=1
{
  "doc": {
    "title": "Castle in the Sky",
    "genre": ["Animation", "Fantasy"]
  }
}
// 문서를 검색한 후 업데이트하면 _seq_no 및 _primary_term값이 다르며 업데이트 작업이 409 - Conflict 오류와 함께 실패하게 됩니다.
// _bulk API에서는 데이터 내에서 _seq_no와 _primary_term값을 지정합니다.

 

 

:: 데이터 삭제

 

// 데이터 삭제
DELETE movies/_doc/1

// DELETE 작업은 _version 필드를 증가시킵니다. 문서를 동일한 ID에 다시 추가하면 _version필드가 다시 증가합니다.
// OpenSearch에서는 문서의 _source를 삭제하지만 해당 메타데이터는 유지하기 때문입니다.
반응형