Updating Documents in Mongoose
Mongoose has 4 different ways to update a document. Here's a list:
What's the difference between these 4 ways? Let's take a look at what each of these functions do.
Using save()
Below is an example of using save()
to update Jon Snow's title.
const schema = new mongoose.Schema({ name: String, title: String });
const CharacterModel = mongoose.model('Character', schema);
const doc = await CharacterModel.create({
name: 'Jon Snow',
title: `Lord Commander of the Night's Watch`
});
// Update the document by setting a property and calling `save()`
doc.title = 'King in the North';
await doc.save();
This simple example has a couple nuances. First, save()
is a method on a document, which means you must have a document to save. You need to either create()
or use find()
to get a document.
Second, Mongoose documents have change tracking. Under the hood, when you call doc.save()
, Mongoose knows you set title
and transforms your save()
call into updateOne({ $set: { title } })
. Try running Mongoose with debug mode to see what queries Mongoose executes.
Using Model.updateOne()
and Model.updateMany()
Using Model.updateOne()
and Model.updateMany()
, you can update the document without loading it from the database first. In the below example, the document with name = 'Jon Snow'
is not in the Node.js process' memory when updateOne()
is called.
// Update the document using `updateOne()`
await CharacterModel.updateOne({ name: 'Jon Snow' }, {
title: 'King in the North'
});
// Load the document to see the updated value
const doc = await CharacterModel.findOne();
doc.title; // "King in the North"
updateMany()
is similar. The difference between these two functions is that updateOne()
will update at most one document, whereas updateMany()
will update every document that matches the filter.
You should use save()
rather than updateOne()
and updateMany()
where possible. However, Model.updateOne()
and Model.updateMany()
have a few advantages:
updateOne()
is atomic. If you load a document usingfind()
, it may change before yousave()
it.updateOne()
doesn't require you to load the document into memory, which may give you better performance if your documents are huge.
Using Document#updateOne()
The Document#updateOne()
function is syntactic sugar for Model.updateOne()
. If you already have the document in memory, doc.updateOne()
structures a Model.updateOne()
call for you.
// Load the document
const doc = await CharacterModel.findOne({ name: 'Jon Snow' });
// Update the document using `Document#updateOne()`
// Equivalent to `CharacterModel.updateOne({ _id: doc._id }, update)`
const update = { title: 'King in the North' };
await doc.updateOne(update);
const updatedDoc = await CharacterModel.findOne({ name: 'Jon Snow' });
updatedDoc.title; // "King in the North"
Generally, Document#updateOne()
is rarely useful. You're better off using save()
and using Model.updateOne()
for cases when save()
is not flexible enough.
Using Model.findOneAndUpdate()
The Model.findOneAndUpdate()
function or its variation Model.findByIdAndUpdate()
behave similarly to updateOne()
: they atomically update the first document that matches the first parameter filter
. Unlike updateOne()
, it gives you back the updated document.
const doc = await CharacterModel.findOneAndUpdate(
{ name: 'Jon Snow' },
{ title: 'King in the North' },
// If `new` isn't true, `findOneAndUpdate()` will return the
// document as it was _before_ it was updated.
{ new: true }
);
doc.title; // "King in the North"
Summary
In general, you should use save()
to update a document in Mongoose, unless you
need an atomic update. Here's a summary of the key features of all 4 ways to update a document: