What is the difference between findOneAndUpdate and updateOne in MongoDB?
Learn the difference between findOneAndUpdate and updateOne in MongoDB: return values, atomicity, returnDocument, and when to use each with real examples.
Expected Interview Answer
updateOne modifies the first matching document and returns only an acknowledgement with counts, whereas findOneAndUpdate atomically modifies the first match and also returns the document itself — the pre-update version by default, or the post-update version when returnDocument is set to 'after'.
Both target a single document and both are atomic on that document, but they serve different needs. updateOne is the lightweight choice when you only care that the write happened. findOneAndUpdate is used when you need the document back in the same round-trip — for example to read a freshly incremented counter, implement optimistic workflows, or claim-and-return a job from a queue without a race between a separate read and write.
- findOneAndUpdate returns the affected document in one atomic operation
- updateOne is lighter when you only need write acknowledgement
- returnDocument controls whether you get the before or after state
- Both avoid the read-then-write race on a single document
- findOneAndUpdate supports sort to pick which match to modify
AI Mentor Explanation
Think of the third umpire adjusting a batter's score. updateOne is like the umpire signalling the change and walking off — the total is corrected but you must look at the big screen yourself to see it. findOneAndUpdate is the umpire correcting the score and immediately handing you the updated scorecard slip, so you hold the new figure without a second glance at the board.
Step-by-Step Explanation
Step 1
Pick by intent
If you only need to know a write happened, reach for updateOne; if you need the document back, use findOneAndUpdate.
Step 2
Write the filter and update
Both take a query filter and an update document using operators like $set, $inc, or an aggregation pipeline.
Step 3
Choose returnDocument
On findOneAndUpdate set returnDocument to 'before' (default) or 'after' to control which version is returned.
Step 4
Add sort if needed
findOneAndUpdate accepts a sort so you can deterministically pick which matching document to modify, useful for queue-style claims.
Step 5
Handle upsert
Both support upsert:true to insert when nothing matches; check upsertedId/upsertedCount or the returned document accordingly.
Step 6
Read the result shape
updateOne returns matchedCount/modifiedCount; findOneAndUpdate returns the document (or null when nothing matched and no upsert).
What Interviewer Expects
- Knowing findOneAndUpdate returns a document while updateOne returns counts
- Awareness of the returnDocument before/after option
- Understanding both are atomic on a single document
- Recognising the claim-and-return / counter use cases
- Familiarity with sort and upsert options on findOneAndUpdate
Common Mistakes
- Assuming updateOne returns the modified document
- Forgetting returnDocument defaults to 'before' so you get the old version
- Doing a separate find after updateOne, reintroducing a race condition
- Believing findOneAndUpdate affects multiple documents
- Ignoring that findOneAndUpdate returns null when no document matches without upsert
Best Answer (HR Friendly)
“Both commands change one record in the database. updateOne just tells you the change happened, while findOneAndUpdate also hands back the record — either how it looked before or after the change. You pick findOneAndUpdate when you need the updated data immediately without asking the database twice.”
Code Example
const res = await db.collection('users').updateOne(
{ _id: userId },
{ $set: { lastLogin: new Date() } }
);
console.log(res.matchedCount, res.modifiedCount); // e.g. 1 1
// res does NOT contain the documentconst doc = await db.collection('counters').findOneAndUpdate(
{ _id: 'orderId' },
{ $inc: { seq: 1 } },
{ returnDocument: 'after', upsert: true }
);
console.log(doc.seq); // the freshly incremented value, atomicallyconst job = await db.collection('jobs').findOneAndUpdate(
{ status: 'pending' },
{ $set: { status: 'processing', claimedAt: new Date() } },
{ sort: { priority: -1, createdAt: 1 }, returnDocument: 'after' }
);
if (job) process(job); // atomically claimed, no raceFollow-up Questions
- What does returnDocument: 'after' change in the response?
- How would you implement an atomic counter in MongoDB?
- When would findOneAndDelete be preferable to deleteOne?
- How does the sort option help implement a job queue?
- What does updateOne return when no document matches?
MCQ Practice
1. What does findOneAndUpdate return by default (without returnDocument set)?
By default returnDocument is 'before', so findOneAndUpdate returns the pre-update version of the document.
2. Which option lets you deterministically choose which matching document findOneAndUpdate modifies?
The sort option orders matches so the first one is modified — key for queue-style claim operations.
3. What does updateOne return in its result object?
updateOne returns an acknowledgement with counts such as matchedCount and modifiedCount, not the document itself.
Flash Cards
updateOne return value? — An acknowledgement with matchedCount and modifiedCount — not the document.
findOneAndUpdate return value? — The affected document itself — pre-update by default, post-update with returnDocument: 'after'.
How to get the new value after incrementing a counter atomically? — findOneAndUpdate with $inc and returnDocument: 'after'.
Which supports sort to pick the match? — findOneAndUpdate (updateOne does not).
Are both atomic? — Yes — both are atomic on the single document they modify.