안녕하세요. 이번 시간에는 몽고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만큼 깎은 겁니다.
�
첫 번째 인자가 수정할 대상, 두 번째 인자가 수정할 내용입니다. 세 번째 인자로 옵션을 넣을 수 있습니다. 옵션에는 크게 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 }); // { 데몬 }
�
수정한 후에 수정된 결과를 다시 가져오고 싶다면 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 시간에 배웠던 save와 insert 메소드의 차이가 upsert의 개념에 있습니다. save는 upsert 메소드입니다. 만약 해당하는 다큐먼트가 없으면 새로 만듭니다. 하지만 해당하는 다큐먼트가 있으면(_id를 제공해야합니다) 수정합니다.
다음 시간에는 CRUD의 마지막 Delete 관련 메소드에 대해 알아보겠습니다!
{_id: req.body.id},
{content: req.body.content}
);
이 코드에 잘못된 부분이 있나요?
const editContent = await Content.find({
_id: req.body.id
});
find 는 되고 update 는 안돼는 이유를 모르겠습니다.