문서 추가·업데이트

문서 추가·업데이트

Meilisearch에 데이터를 넣는 방법은 크게 세 가지 문서 연산으로 나눠요. 새로 추가하거나, 기존 문서를 통째로 교체하거나, 특정 필드만 부분 업데이트하는 방식이죠. 이 글에서는 각 연산이 정확히 어떤 동작을 하는지, 그리고 어떤 상황에서 어떤 연산을 골라야 하는지 설명드릴게요. 특히 POSTPUT의 차이를 이해하면 대량 데이터 처리에서 실수를 많이 줄일 수 있어요.

출처: Meilisearch 공식 문서 — Add and update documents

본문

Add or replace 문서 (추가 또는 교체)

POST /indexes/{index_uid}/documents는 새 문서를 추가하거나 기존 문서를 교체할 때 사용해요. 같은 프라이머리 키를 가진 문서가 이미 존재하면 Meilisearch는 기존 문서 전체를 새 버전으로 통째로 교체합니다.

client.index('movies').addDocuments([{
    id: 287947,
    title: 'Shazam',
    poster: 'https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg',
    overview: 'A boy is given the ability to become an adult superhero in times of need with a single magic word.',

Add or update 문서 (추가 또는 부분 업데이트)

PUT /indexes/{index_uid}/documents는 새 문서를 추가하거나 기존 문서를 부분 업데이트할 때 사용해요. 같은 프라이머리 키를 가진 문서가 이미 존재하면 Meilisearch는 새 필드를 기존 문서에 병합합니다. 업데이트에 포함되지 않은 필드는 그대로 유지되죠.

부분 업데이트는 최상위 필드에만 적용돼요. 객체 필드를 업데이트하면 객체 전체가 교체되어, 생략된 하위 필드는 사라집니다.

curl \
  -X PUT 'MEILISEARCH_URL/indexes/movies/documents' \
  -H 'Authorization: Bearer API_KEY' \
  -H 'Content-Type: application/json' \
  --data-binary '[
    {
      "id": 287947,
      "title": "Shazam ⚡️",
      "genres": "comedy"
    }
  ]'

이 연산은 전체 문서를 다시 보내지 않고 특정 필드만 바꿔야 할 때 아주 유용해요.

Delete 문서 (삭제)

DELETE /indexes/{index_uid}/documents/{document_id}는 프라이머리 키로 단일 문서를 삭제할 때 사용합니다:

curl \
  -X DELETE 'MEILISEARCH_URL/indexes/movies/documents/25684' \
  -H 'Authorization: Bearer API_KEY'
client.index('movies').delete_document(25684)

Meilisearch는 배치 삭제와 필터 기반 삭제도 지원해요:

  • 배치 삭제(Delete by batch): POST /indexes/{index_uid}/documents/delete-batch 요청에 문서 ID 배열을 보냅니다.
  • 필터 삭제(Delete by filter): POST /indexes/{index_uid}/documents/delete 요청에 필터 표현식을 보내 일치하는 모든 문서를 제거합니다.

올바른 연산 고르기

| 연산 | HTTP 메서드 | 동작 | 사용 시점 | | Add or replace | POST | 문서 전체 교체 | 완전한 문서를 갖고 있고 정확히 제어하고 싶을 때 | | Delete | DELETE | 문서 완전히 제거 | 인덱스에서 문서를 제거해야 할 때 |

배치 연산

curl \
  -X POST 'MEILISEARCH_URL/indexes/movies/documents' \
  -H 'Content-Type: application/json' \
  --data-binary '[
    { "id": 1, "title": "Movie One" },
    { "id": 2, "title": "Movie Two" },
    { "id": 3, "title": "Movie Three" }
  ]'

배치 연산은 하나의 task로 처리돼요. Meilisearch는 큰 배치도 효율적으로 처리하므로, 문서를 하나씩 보내기보다는 벌크로 보내는 걸 권장합니다.

새 문서 생성을 막고 싶다면 (skipCreation)

기본적으로 POSTPUT 문서 연산 모두, 같은 프라이머리 키를 가진 문서가 없으면 새 문서를 만듭니다. 이 동작을 바꾸려면 요청에 skipCreation=true 쿼리 파라미터를 추가하세요.

curl \
  -X POST 'MEILISEARCH_URL/indexes/movies/documents?skipCreation=true' \
  -H 'Content-Type: application/json' \
  --data-binary '[
    { "id": 1, "title": "Updated Title" },
    { "id": 99999, "title": "This document does not exist" }
  ]'

이 예시에서는 문서 1만 업데이트됩니다. 문서 99999는 인덱스에 아직 존재하지 않으므로 무시되죠. 이런 방식은 기존 문서의 필드만 안전하게 업데이트하려고 할 때, 불완전한 레코드가 실수로 생성되는 것을 막아줘요.

ID로 여러 문서 조회

POST /indexes/{index_uid}/documents/fetch는 프라이머리 키로 특정 문서들을 조회할 때 사용합니다:

curl \
  -X POST 'MEILISEARCH_URL/indexes/movies/documents/fetch' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "ids": ["id1", "id2", "id3"]
  }'

Meilisearch는 일치하는 문서를 results 배열로 반환해요. 문서가 요청한 순서대로 반환되는 건 아니고, 존재하지 않는 ID는 조용히 무시됩니다.

⚠️ GET /indexes/{index_uid}/documents보다는 POST /indexes/{index_uid}/documents/fetch를 권장해요. GET 방식은 프록시/CDN 레벨의 HTTP 캐싱을 활용해야 할 특별한 이유가 아니라면 비추천입니다. GET 라우트는 파라미터가 적고 문자열 필터 표현식만 지원하지만, POST 라우트는 이 가이드 전반에서 쓰는 더 풍부한 JSON 본문을 받아요.

조회 시 필터 적용

POST /indexes/{index_uid}/documents/fetchfilter 표현식을 넘기면 조건에 맞는 문서만 조회할 수 있어요:

curl \
  -X POST 'MEILISEARCH_URL/indexes/movies/documents/fetch' \
  -H 'Authorization: Bearer API_KEY' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "filter": "genres = Action AND rating > 8"
  }'

문서 filter에서 참조하는 속성은 먼저 인덱스의 filterableAttributes 설정에 선언되어 있어야 해요. 이 규칙은 검색 필터와 동일하며, 문서 조회·삭제를 필터링하는 documents 엔드포인트에서도 동일하게 적용됩니다.

지원되는 콘텐츠 타입

기본적으로 Meilisearch는 요청 본문에 JSON 배열을 기대하며 Content-Type: application/json 헤더를 사용해요. documents 엔드포인트는 해당 헤더를 설정하면 NDJSON(application/x-ndjson)과 CSV(text/csv) 페이로드도 받아들입니다.

curl \
  -X POST 'MEILISEARCH_URL/indexes/movies/documents' \
  -H 'Authorization: Bearer API_KEY' \
  -H 'Content-Type: text/csv' \
  --data-binary @movies.csv
curl \
  -X POST 'MEILISEARCH_URL/indexes/movies/documents' \
  -H 'Authorization: Bearer API_KEY' \
  -H 'Content-Type: application/x-ndjson' \
  --data-binary @movies.ndjson

CSV 데이터를 업로드할 때는 기본 쉼표 구분자를 csvDelimiter 쿼리 파라미터로 바꿀 수 있어요 (예: ?csvDelimiter=;).

⚠️ csvDelimiter는 요청이 Content-Type: text/csv를 사용할 때만 유효합니다. JSON이나 NDJSON 페이로드와 함께 넣으면 오류가 반환돼요.

더 알아보기