게시글

강좌4 - MongoDB - 4년 전 등록 / 3년 전 수정

수정(Update,Upsert 또는 Modify)

조회수:
0

안녕하세요. 이번 시간에는 몽고DB CRUD 기능 중 U를 담당하는 Update 메소드들에 대해서 알아보겠습니다.

일단 수정을 하려면 수정할 대상을 선택해야 합니다. 그러고나서 이제 어떻게 수정할 지를 알려줘야겠죠.

Update

가장 유명한 메소드로 update가 있습니다. 슬라임 몬스터를 좀 버프해볼까요? hp가 너무 낮은 거 같아서 올려봅니다.

db.monsters.update({ name: 'Slime' }, { $set: { hp: 30 } }); // WriteResult({ nMatched: 1, nUpserted: 0, nModified: 1 });

수행 결과를 반환하네요. $set을 해야 해당 필드만 바뀝니다. 만약 $set을 넣지 않고 그냥 { hp: 30 } 하면 Slime 다큐먼트가 다 지워지고 { hp: 30 } 이라는 객체로 통째로 바뀌어버립니다. 처음 하는 분들은 이 실수를 정말 많이 합니다.

hp를 너무 많이 올린 것 같아서 다시 원래대로 복구하겠습니다. 그런데 이번에는 좀 다른 방법으로 하겠습니다.

db.monsters.update({ name: 'Slime' }, { $inc: { hp: -5 } }); // WriteResult({ nMatched: 1, nUpserted: 0, nModified: 1 });

$inc를 사용하면 숫자를 올리거나 내릴 수 있습니다. 음수를 넣으면 내리고 양수를 넣으면 올립니다. 위의 코드는 hp를 5만큼 깎은 겁니다.

undefined

첫 번째 인자가 수정할 대상, 두 번째 인자가 수정할 내용입니다. 세 번째 인자로 옵션을 넣을 수 있습니다. 옵션에는 크게 multi와 upsert가 있습니다. multi는 여러 개를 동시에 수정할 때 사용합니다. { multi: true } 하면 됩니다. 기본적으로는 한 다큐먼트만 수정하지만 만약 이름이 Slime인 다큐먼트가 여러 개 있다면 그 다큐먼트들을 모두 수정하는 거죠. upsert는 아래에 따로 설명합니다.

FindAndModify

또한 많이 쓰이는 메소드가 findAndModify입니다. update 메소드와는 달리 upsert과 remove까지 같이 수행할 수 있습니다. 인자가 옵션 객체 하나인데 여러 개의 속성을 넣어줘야 합니다. query가 대상을 찾는 법, update가 대상을 수정할 내용, new가 수정 이전의 다큐먼트를 반환할지, 수정 이후의 다큐먼트를 반환할 지 결정하는 부분입니다. { new: true }를 넣으면 수정 이후의 다큐먼트를 반환합니다. 

데몬 몬스터가 너무 세니 너프해보겠습니다.

db.monsters.findAndModify({ query: { name: 'Demon' }, update: { $set: { att: 150 } }, new: true }); // { 데몬 }

undefined

수정한 후에 수정된 결과를 다시 가져오고 싶다면 update 대신 findAndModify 메소드를 쓰는 게 낫겠죠?

UpdateOne, UpdateMany, ReplaceOne

몽고DB 3.2 버전부터 update를 대체하는 세 메소드가 추가되었습니다. update 메소드와 거의 유사하지만, updateOne은 매칭되는 다큐먼트 중 첫 번째만 수정하고, updateMany는 매칭되는 모든 다큐먼트를 수정합니다. 기존의 multi 옵션이 두 메소드로 나누어졌다고 생각하시면 됩니다.

db.monsters.updateOne({ name: 'Slime' }, { $set: { hp: 25 } });

replaceOne 메소드는 다큐먼트를 통째로 다른 것으로 대체합니다. $set을 안 썼을 때 상황과 유사합니다.

FindOneAndUpdate, FindOneAndReplace

또한 몽고DB 3.2 버전부터 findAndModify 메소드를 대체하는 새로운 두 메소드가 만들어졌습니다. findAndModify는 update, upsert, remove를 모두 담당할 수 있는데요. 이 기능을 쪼개서 하나의 역할만 전담하는 메소드입니다. 역시나 findAndModify 시리즈답게 수정 이전 또는 이후의 다큐먼트를 반환받습니다. 대신 new 옵션이 아니라 returnNewDocument로 이름이 바뀌었습니다.

db.monsters.findOneAndUpdate({ name: 'Demon' }, { $set: { att: 150 } }, { returnNewDocument: true }); // { 데몬 }

Upsert

만약 수정할 대상이 없다면 어떡할까요? 보통의 경우는 아무것도 변하지 않고 종료됩니다. 특정한 옵션을 주어서 수정할 대상이 없는 경우 insert 동작을 수행하도록 할 수 있습니다. 이를 upsert라고 부릅니다. update + insert의 합성어입니다.

Upsert 기능을 하려면 update, updateOne, updateMany, replaceOne 메소드에 옵션으로 { upsert: true } 를 주면 됩니다. 또는 findAndModify, findOneAndUpdate, findOneAndReplace 메소드에 upsert: true를 추가할 수도 있습니다.

Insert 시간에 배웠던 saveinsert 메소드의 차이가 upsert의 개념에 있습니다. save는 upsert 메소드입니다. 만약 해당하는 다큐먼트가 없으면 새로 만듭니다. 하지만 해당하는 다큐먼트가 있으면(_id를 제공해야합니다) 수정합니다.

다음 시간에는 CRUD의 마지막 Delete 관련 메소드에 대해 알아보겠습니다!

투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.

댓글

3개의 댓글이 있습니다.
7달 전
findAndUpdate 시에 $get 과 같은 방법으로 해당 find된 값에 접근 해서 true false 로 토글 할 수 있는 방법이 따로 있을까요 지금은 find로 해당 값을 찾은 다음에 다시 update를 통해 값을 토글 시키고 있는데 뭔가 findAndUpdate라는 부분에서 find 값을 참조 할 수 있지 않을까 해서요!!
2년 전
findAndModify 실용적이네요 감사합니다.
3년 전
음 .. 제가 save는 데이터가 없을경우 insert를 실행하게 되고
insert는 데이터가 있든 없든 데이터를 추가한다는 말씀이신거죠??
또 궁금한건 save == upsert인거죠?? ?? upsert를 통해 데이터가 있다면 _id를 제공해야한다는것은 _id가 자동으로 되는게 아니라 직접 추가해주어야 한다는 말씀이신건가요??
3년 전
save는 upsert랑 비슷합니다. _id를 주지 않으면 insert를 하고, _id를 제공하면 update를 합니다. insert는 데이터가 있든 없든이 아니라 그냥 새로운 하나를 추가하는 겁니다.
3년 전
아 같은건아니고 비슷한거군요 ㅎㅎ 감사합니다 이해 됬습니다 ㅎㅎ