MongoDB에서 배열 안의 여러 요소를 한 번에 업데이트해야 할 때는 initializeOrderedBulkOp() 메서드를 활용하는 것이 가장 효율적입니다. 이 메서드는 컬렉션을 위한 새로운 Bulk() 연산 빌더를 초기화한 뒤 반환하며, 이 빌더는 MongoDB가 한 번에 대량으로 실행할 수 있도록 정렬된 쓰기 작업 목록을 구성합니다.
1. 예제 컬렉션 생성하기
먼저 실습에 사용할 컬렉션을 만들고 문서를 삽입해 보겠습니다.
> db.demo550.insertOne({"Name":"Chris","details":[{"Marks":49,Result:"fail"},{"Marks":58,Result:"fail"}]});
{
"acknowledged" : true,
"insertedId" : ObjectId("5e8e35bd9e5f92834d7f05e4")
}2. find()로 문서 조회하기
find() 메서드를 사용하면 컬렉션에 저장된 모든 문서를 확인할 수 있습니다.
> db.demo550.find();
위 쿼리를 실행하면 다음과 같은 결과가 출력됩니다.
{ "_id" : ObjectId("5e8e35bd9e5f92834d7f05e4"), "Name" : "Chris", "details" : [ { "Marks" : 49, "Result" : "fail" }, { "Marks" : 58, "Result" : "fail" } ] }현재 details 배열의 두 요소 모두 Result 값이 "fail"로 저장되어 있습니다.
3. 대량 작업으로 배열 요소 업데이트하기
다음은 initializeOrderedBulkOp()를 활용해 배열 내 요소들을 일괄 업데이트하는 쿼리입니다.
> var all = db.demo550.initializeOrderedBulkOp(),
... itr = 0;
> db.demo550.find({ "Name": "Chris", "details.Result": "fail" }).forEach(function(doc) {
... doc.details.filter(function(d){ return d.Result == "fail" }).forEach(function(d) {
... all.find({ "_id": doc._id, "details.Result": "fail" }).updateOne({
... "$set": { "details.$.Result": "PASS" }
... });
... itr++;
... if ( itr % 10 == 0 ) {
... all.execute();
... all = db.demo550.initializeOrderedBulkOp();
... }
... });
... if ( itr % 10 != 0 )
... all.execute();
... });
이 코드의 동작 방식을 단계별로 살펴보면 다음과 같습니다.
- 빌더 초기화: initializeOrderedBulkOp()로 대량 작업 빌더를 생성합니다.
- 문서 순회: find()와 forEach()를 조합해 Name이 "Chris"이고 details.Result가 "fail"인 문서만 순회합니다.
- 요소 필터링: filter()를 통해 Result가 "fail"인 배열 요소만 추출합니다.
- 위치 연산자($): updateOne()과 $set 연산자, 그리고 위치 연산자($)를 사용해 조건에 일치하는 배열 요소의 Result 값을 "PASS"로 변경합니다.
- 배치 실행: 누적된 작업이 10건이 될 때마다 execute()를 호출해 중간 실행을 하고, 마지막에는 남은 작업을 한 번 더 실행해 처리를 완료합니다.
이처럼 일정 개수 단위로 나누어 execute()를 호출하면 메모리 부담을 줄이면서 대량의 업데이트를 안정적으로 처리할 수 있습니다.
4. 업데이트 결과 확인
업데이트가 완료되었으니 다시 find() 메서드로 모든 문서를 조회해 보겠습니다.
> db.demo550.find();
실행 결과는 다음과 같습니다.
{ "_id" : ObjectId("5e8e35bd9e5f92834d7f05e4"), "Name" : "Chris", "details" : [ { "Marks" : 49, "Result" : "PASS" }, { "Marks" : 58, "Result" : "PASS" } ] }출력 결과를 보면 details 배열 내 모든 요소의 Result 값이 "fail"에서 "PASS"로 성공적으로 변경된 것을 확인할 수 있습니다. 이처럼 대량 작업(Bulk Operation)을 활용하면 여러 업데이트 명령을 하나의 요청으로 묶어 실행할 수 있어 네트워크 왕복 횟수가 줄어들고 전체적인 처리 성능이 크게 향상됩니다.