Account 컬렉션은 transaction 도큐먼트의 objectId를 자신의 필드인 transactions에 추가하여야 한다.
그 과정은 다음 두 가지 일을 해야 한다.
Account Object Id로 Account 도큐먼트를 찾는다.
그 도큐먼트의 trasactions에 새롭게 만든 transaction 도큐먼트의 Object Id를 추가한다.
공동가계부를 관리하게 된다면, 동시에 여러 사람이 거래내역을 추가, 삭제 등의 요청을 할 수 있다.
그러므로, 우리는 모든 디비에 대한 접근 요청이 유효하게 만들어주는 것을 고려해야 한다.
몽구스에서 Atomic Update
를 하기 위한 방법을 찾아보자.
우선 update를 하는 여러 방법들을 찾아보았다.
in-memory update
const targetAccount = await this.findById(accountObjId).exec();
targetAccount.transactions.push(transactionObjId);
await targetAccount.save();
find를 해서 가져온 도큐먼트를 update한 후 save하는 방식이다. 이 방식은 도큐먼트를 가져온 후, save가 되기 전에 다른 쿼리에 의해 도큐먼트가 변경될 수 있다.
findOneAndUpdate()
User.findByIdAndUpdate(id, { $push: { createdEvents: eventId } }).exec();
몽구스 공식문서에 따르면 findOndAndUpdate는 atomic하다고 한다.
With the exception of an unindexed upsert, findOneAndUpdate() is atomic.
$push operator는 배열에 값을 추가한다. 이 방식으로 id에 해당하는 도큐먼트를 찾아서 createdEvents 배열에 eventId를 추가할 수 있게 된다.
update도 마찬가지로 atomic하다. 첫 번째 인자로 필터를 넣어주고, 두 번째 인자로 실행할 도큐먼트를 입력해주면 된다. findOneAndUpdate와 다른 점은 findOneAndUpdate는 도큐먼트를 리턴해주지만, update는 도큐먼트를 리턴하지 않는다.
PersonModel.update(
{ _id: person._id },
{ $push: { friends: friend } },
done
);
this.findByIdAndUpdate(accountObjId, {
$push: { transactions: transactionObjId },
}).exec();
findOneAndUpdate는 atomic하다고 한다. 우리는 Account 컬렉션에서 Object Id를 찾아서 update를 해줘야하므로, findByIdAndUpdate
가 적합하다고 생각하였다. findByIdAndUpdate 는 내부적으로 findOneAndUpdate를 불러준다고 하니, findByIdAndUpdate 또한 atomic 할 것 이라고 생각한다.
update만 하면 쿼리가 실행되는 줄 알았는데, 공식문서의 예제를 보면, exec()
를 마지막에 불러오는게 있었다. exec를 왜 해주는 걸까???
그냥 query문을 호출하면, 실제적으로 쿼리가 실행되는 것이 아니다.
콜백 함수의 유무
에 따라 실행이 달라지게 된다.
콜백 함수가 있으면 실행이 되고, 콜백 함수가 없으면 쿼리를 리턴하게 된다.
A.findByIdAndUpdate(id, update, options, callback) // executes
A.findByIdAndUpdate(id, update, callback) // executes
A.findByIdAndUpdate(id, update, options) // returns Query
A.findByIdAndUpdate(id, update) // returns Query
A.findByIdAndUpdate() // returns Query
따라서, 콜백 함수 없이 바로 쿼리를 실행시키려면, update() 호출 후 exec()를 호출해야 한다.
const q = Model.where({ _id: id });
q.update({ $set: { name: 'bob' }}).update(); // not executed
q.update({ $set: { name: 'bob' }}).exec(); // executed
upsert를 true로 주면, document가 있으면 update를 하고, 없으면, filter와 update를 결합하여 생성한다.
const filter = { name: 'Will Riker' };
const update = { age: 29 };
await Character.countDocuments(filter); // 0
let doc = await Character.findOneAndUpdate(filter, update, {
new: true,
upsert: true // Make this update into an upsert
});
doc.name; // Will Riker
doc.age; // 29
Push items into mongo array via mongoose
https://ddcode.net/2019/05/12/is-mongodb-findoneandupdate-thread-safe/