Explain Codes LogoExplain Codes Logo

How do I update/upsert a document in Mongoose?

javascript
mongoose
database
upsert
Nikita BarsukovbyNikita Barsukov·Oct 10, 2024
TLDR

Upsert a document with Mongoose using the findOneAndUpdate() method paired with the { upsert: true } option. This command either updates an existing document if a match exists, or inserts a new one if there's no match:

const filter = { name: 'John Doe' }; // Looking for John? const update = { age: 30 }; // John must be 30. Model.findOneAndUpdate(filter, update, { upsert: true, new: true }, (err, doc) => { if (err) console.error(err); // Oops. Something went wrong. console.log('Result:', doc); // Here you are. A fresh John Doe, aged 30! });

Use {new: true} for immediate results

Including { new: true } in your operation is important. This returns the modified document rather than the original. Without this, you’ll get back the old document, while your database will quietly keep the updated one a secret. We definitely don’t want that, do we?

Keep track of time with createdAt and updatedAt

By enabling timestamps in your schema, Mongoose will automatic track and update createdAt and updatedAt timestamps for us:

const schema = new mongoose.Schema({ // your schema fields }, { timestamps: true }); // This magic line enables timestamps.

Imagine the power of time travel right within your database! 🔮

Targeted field updates using update()

What if you want to surgically alter a specific field without bothering the others? In such delicate operations, Model.update() paired with { upsert: true } is your scalpel:

const conditions = { name: 'John Doe' }; const update = { $set: { age: 30 } }; // Making John 30 without asking for his ID. Model.update(conditions, update, { upsert: true }, (err) => { // Handle the error here, or do a victory dance. });

Avoid _id duplicates during upsert

Beware of unexpected duplicate _id errors during upserts. A simple trick to avoid this headache is to remove the _id field before upserting:

let upsertData = contact.toObject(); delete upsertData._id; // Let's just pretend that never existed, shall we? Model.findOneAndUpdate(filter, upsertData, { upsert: true }, (err, doc) => { // Handle the error or marvel at your flawless document here. });

The slow and steady findOne + save()

If you're using middleware or hooks not triggered by findOneAndUpdate, you could opt for the traditional findOne method followed by save():

Model.findOne({ name: 'John Doe' }, (err, doc) => { if (doc) { // Oh, John already exists! doc.age = 30; // Let's get him older a bit. doc.save(); // Saving the elderly John. } else { // No John here, let's make one. const newDoc = new Model({ name: 'John Doe', age: 30 }); // Fresh model-hot John. newDoc.save(); // Save precious John. } });

Warning! Alert! Alert! This method doubles your database calls. Handle with care. 🚨

Quiet the findOneAndUpdate deprecation warnings

Wave goodbye to findOneAndUpdate deprecation warnings by using { useFindAndModify: false }:

mongoose.set('useFindAndModify', false); // You got it, captain!

Goodbye, pesky warnings! 🥳

Use Model.updateOne() for unique cases

For updates with unique properties, Model.updateOne() is your best buddy:

const filter = { email: '[email protected]' }; // Zoning in on a unique email. const update = { subscribed: true }; // Subscribe status verified! Model.updateOne(filter, update, { upsert: true }, (err, result) => { // Just handle your result, you magnificent coder! });

Call for Model.upsert()?

We could use a Model.upsert() function to simplify upsert operations, don't you agree? What a gamechanger that would be!

Guides for further study

Take a dive into detailed tutorials and read the Mongoose documentation to broaden your understanding and level-up your efficiency with upserts.