Computer >> 컴퓨터 >  >> 프로그래밍 >> MongoDB

MongoDB bulkWrite()로 배열 요소 조건부 삽입·업데이트하기 (여러 조건 활용)

MongoDB에서 배열 요소 조건부 삽입·업데이트 처리하기

배열 내부에 특정 조건을 만족하는 요소가 없으면 새로 삽입하고, 있으면 기존 값을 업데이트해야 하는 상황이라면 MongoDB의 bulkWrite() 메서드를 활용할 수 있습니다. 이 메서드는 여러 개의 쓰기 작업을 하나의 요청으로 묶어 한 번에 처리할 수 있어, 조건에 따라 분기되는 삽입/업데이트 로직을 구현하기에 적합합니다.

아래 예제에서는 학생 성적 정보를 담은 배열을 대상으로, 여러 조건(Marks2, Marks3)을 조합해 요소의 존재 여부를 판단한 뒤 삽입 또는 업데이트를 수행하는 과정을 단계별로 살펴보겠습니다.

1단계: 샘플 컬렉션 생성

먼저 문서 하나를 포함하는 컬렉션을 생성합니다.

> db.demo105.insertOne( { _id:'101', Name:'Chris', Details:[{ Marks1:60, Marks2:70, Marks3:70 }, { Marks1:70, Marks2:70, Marks3:90 }] } );
{ "acknowledged" : true, "insertedId" : "101" }

2단계: 초기 데이터 확인

find() 메서드를 사용해 컬렉션의 모든 문서를 조회합니다.

> db.demo105.find().pretty();

위 명령은 다음과 같은 결과를 출력합니다.

{
    "_id" : "101",
    "Name" : "Chris",
    "Details" : [
       {
          "Marks1" : 60,
          "Marks2" : 70,
          "Marks3" : 70
       },
       {
          "Marks1" : 70,
          "Marks2" : 70,
          "Marks3" : 90
       }
    ]
}

3단계: 조건부 삽입/업데이트 쿼리 실행

다음은 요소가 존재하지 않으면 배열에 삽입하고, 존재하면 여러 조건을 기반으로 업데이트하는 bulkWrite() 쿼리입니다.

> db.demo105.bulkWrite([
...    { "updateOne": {
...        "filter": {
...           "_id": "101",
...           "Details": {
...              "$elemMatch": { Marks2: 70, Marks3: 70 }
...              }
...           },
...           "update": {
...              "$set": { "Details.$.Marks3": 96 }
...              }
...           }},
...           { "updateOne": {
...                 "filter": {
...                    "_id": "101",
...                    "Details": {
...                       "$not": {
...                          "$elemMatch": { Marks2: 70, Marks3: 70 }
...                          }
...                    }
...                 },
...                 "update": {
...                    "$push": { "Details": { Marks1: 94, Marks2: 97,Marks3:99} }
...                 }
...           }},
...           { "updateOne": {
...             "filter": { "_id": 101 },
...             "update": {
...                "$setOnInsert": {
...                   "Details": [
...                { Marks1: 34, Marks2: 67,Marks3:87 }
...             ]
...         }
...     },
...     "upsert": true
...   }}
... ])
{
    "acknowledged" : true,
    "deletedCount" : 0,
    "insertedCount" : 0,
    "matchedCount" : 2,
    "upsertedCount" : 1,
    "insertedIds" : {
    },
    "upsertedIds" : {
       "2" : 101
    }
}

이 쿼리는 세 가지 작업으로 구성됩니다.

  • 첫 번째 updateOne: $elemMatch를 사용해 Marks2: 70Marks3: 70 조건을 동시에 만족하는 요소가 배열에 존재하면, 위치 연산자($)를 통해 해당 요소의 Marks3 값을 96으로 변경합니다.
  • 두 번째 updateOne: $not$elemMatch를 조합해 위 조건을 만족하는 요소가 존재하지 않는 경우에만 $push로 새로운 성적 객체를 배열 끝에 추가합니다.
  • 세 번째 updateOne: upsert: true$setOnInsert를 함께 사용해, _id: 101 문서 자체가 존재하지 않을 때만 새 문서를 생성하며 초기 배열을 설정합니다.

4단계: 최종 결과 확인

다시 find() 메서드로 전체 문서를 조회해 봅니다.

> db.demo105.find().pretty();

첫 번째 문서에서는 조건(Marks2: 70, Marks3: 70)에 일치하는 요소가 발견되어 Marks3 값이 96으로 업데이트되었고, 이후에는 일치하는 요소가 더 이상 없어 새 요소가 $push로 추가된 것을 확인할 수 있습니다. 또한 문자열 "101"과 숫자 101은 서로 다른 _id로 취급되기 때문에, 세 번째 upsert 작업에 의해 숫자형 _id를 가진 새 문서도 함께 생성되었습니다.

{
    "_id" : "101",
    "Name" : "Chris",
    "Details" : [
       {
          "Marks1" : 60,
          "Marks2" : 70,
          "Marks3" : 96
       },
       {
          "Marks1" : 70,
          "Marks2" : 70,
          "Marks3" : 90
       },
       {
          "Marks1" : 94,
          "Marks2" : 97,
          "Marks3" : 99
       }
    ]
}
{
    "_id" : 101,
    "Details" : [
       {
          "Marks1" : 34,
          "Marks2" : 67,
          "Marks3" : 87
       }
    ]
}

정리

이처럼 bulkWrite()를 활용하면 $elemMatch로 요소의 존재 여부를 검사해 업데이트하고, $not 조건으로 존재하지 않을 때만 $push하며, upsert로 문서 자체의 부재까지 보완하는 패턴을 하나의 배치로 처리할 수 있습니다. 다만 위 예제에서 확인할 수 있듯이 _id의 타입(문자열 vs 숫자)이 다르면 별개의 문서로 인식되므로, 실무에서는 _id 타입의 일관성을 유지하는 것이 매우 중요합니다.