MongoDB replaceOne
Learn how to replace documents with replaceOne in MongoDB. Understand the syntax, behavior, and practical use cases with hands-on examples.
Focus: replace documents with replaceone
You've built collections, inserted documents, updated fields with $set and $inc, and removed documents you no longer need. But what happens when a document's structure itself is outdated — not just a field value? Patching individual fields with update operators can get tedious and error-prone, especially when an entire document represents a snapshot of a real-world entity that has changed completely. That's exactly the pain point MongoDB's replaceOne method solves: it lets you swap out an entire document in a single atomic operation, giving you a clean slate without deleting and re-inserting. In this lesson, you'll master replaceOne — from its syntax and behavior to practical use cases and common pitfalls — so you can confidently replace documents with replaceone (yes, the method name is case-sensitive) in your MongoDB applications.
The problem this lesson solves
Imagine you have a users collection where each document stores profile information like name, email, and preferences. Over time, your application evolves: you add new fields, remove legacy ones, and change data shapes. Using updateOne with $set works fine for small changes, but what if you need to replace the entire document because the old structure is completely obsolete?
Doing it manually would mean deleting the old document and inserting a new one — a two-step process that risks leaving your data in an inconsistent state if something fails in between. You'd also lose the _id unless you manually preserve it.
MongoDB's replaceOne solves this by allowing you to replace an entire document in a single atomic operation. This means the replacement is all-or-nothing: either the old document is swapped for the new one, or nothing happens. No intermediate states, no orphaned data.
But there's a catch: replaceOne is not a shortcut for partial updates. It replaces the entire document — all fields not included in the replacement are removed. Understanding this distinction is critical to avoiding data loss, and that's what this lesson will make clear.
Core concept / mental model
Think of a MongoDB document as a file folder with multiple pages. updateOne with $set lets you edit a single page — change a name, add a note — while keeping the folder intact. replaceOne, on the other hand, throws the whole folder away and puts a brand-new folder in its place. The only thing that stays is the label (the _id field).
Here's the formal definition:
replaceOne(filter, replacement, options)— finds the first document that matches thefilter, replaces it with thereplacementdocument, and returns a result object with metadata.
Key characteristics:
- Atomic operation — the replacement happens in one server-side step.
- Preserves
_id— MongoDB keeps the original_idvalue automatically, even though you don't include it in the replacement (if you include_id, it must match the original). - Replaces all fields — anything not in the replacement document is gone.
- Only replaces the first match — if multiple documents match the filter, only the first one (in natural order) is replaced.
- If no match — nothing is replaced; you can optionally insert a new document with
upsert: true.
Pro tip: Think of
replaceOneas the "nuclear option" for document updates. Use it when the new data is a complete snapshot of the entity, not a small change.
What replaceOne is not
- It's not
updateOnewith a replacement object — update operators ($set,$inc) are not allowed in the replacement document. If you try, MongoDB throws an error. - It's not a partial update — even if you want to keep most fields, you must include them explicitly in the replacement.
How it works step by step
Let's walk through what happens when you call replaceOne:
- Construct a filter — a query document that identifies which document to replace. This uses the same syntax as
find()(e.g.,{ _id: ObjectId('...') }). - Write the replacement document — a plain document with the new field values. Do not include update operators like
$set. You can include_idonly if it matches the original document's_id. - Call
replaceOnewith optional settings — e.g.,{ upsert: true }to insert if no match is found. - Inspect the result object — it contains
matchedCountandmodifiedCount(orupsertedCountif upsert happened) so you know what actually occurred.
Let's see the result object in detail:
matchedCount— how many documents matched the filter (0 or 1).modifiedCount— how many documents were actually replaced (0 or 1). If the replacement is identical to the original, this may be 0.upsertedCount— ifupsert: trueand no match was found, this is 1 (andupsertedIdwill contain the new_id).
Hands-on walkthrough
Let's put theory into practice. We'll use a product_catalog collection to demonstrate. First, insert a sample document:
// Connect to MongoDB (assuming mongosh or a driver)
db.product_catalog.insertOne({
_id: ObjectId('65f2a1b2c3d4e5f6a7b8c9d0'),
sku: 'TSHIRT-001',
name: 'Classic Tee',
price: 19.99,
category: 'apparel',
tags: ['cotton', 'basic'],
stock: 100,
createdAt: new Date()
});
Now, we need to replace the entire product document with a new version that has completely different fields (e.g., we're rebranding it):
const filter = { sku: 'TSHIRT-001' };
const replacement = {
sku: 'TSHIRT-001',
name: 'Signature Tee',
price: 29.99,
isActive: true
};
const result = db.product_catalog.replaceOne(filter, replacement);
printjson(result);
Expected output:
{
acknowledged: true,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
Now check the document:
db.product_catalog.findOne({ sku: 'TSHIFT-001' });
Output:
{
_id: ObjectId('65f2a1b2c3d4e5f6a7b8c9d0'),
sku: 'TSHIFT-001',
name: 'Signature Tee',
price: 29.99,
isActive: true
}
Notice: category, tags, stock, and createdAt are gone — they were not in the replacement. The _id remained the same.
Using upsert to insert if missing
If you want to replace an existing document or insert a new one when no match is found, use { upsert: true }:
const filter = { sku: 'COFFEE-002' };
const replacement = {
sku: 'COFFEE-002',
name: 'Dark Roast Beans',
price: 14.50,
origin: 'Colombia'
};
const result = db.product_catalog.replaceOne(filter, replacement, { upsert: true });
printjson(result);
Expected output (since no match):
{
acknowledged: true,
matchedCount: 0,
modifiedCount: 0,
upsertedCount: 1,
upsertedId: ObjectId('...')
}
Now a new document is inserted with an auto-generated _id.
Compare options / when to choose what
How does replaceOne differ from updateOne, findOneAndReplace, and the manual delete+insert approach? Here's a quick comparison:
| Method | What it does | Preserves _id |
Can partially update? | Atomic? | Use case |
|---|---|---|---|---|---|
updateOne with $set |
Patches specific fields | Yes | Yes | Yes | Small changes to one field |
replaceOne |
Replaces entire document (except _id) |
Yes | No | Yes | Full document snapshot updates |
findOneAndReplace |
Replaces and returns the document (old or new) | Yes | No | Yes | When you need the result immediately |
| Delete + Insert | Removes and recreates | No | N/A | Not atomic | Legacy/custom scripts (avoid if possible) |
When to choose replaceOne over updateOne:
- Choose
replaceOnewhen the document's structure is fundamentally changing — new shape, different set of fields. - Choose
updateOnewhen you only need to change a few field values while keeping the rest. - Choose
findOneAndReplacewhen you need the replaced document returned in the same operation (e.g., to use in your application logic).
Pro tip: If you find yourself writing
updateOnewith dozens of$setfields, considerreplaceOne— a clean replacement is often more readable and maintainable.
Troubleshooting & edge cases
Error: "The replacement document must not contain atomic operators"
If you try to use $set or $inc in the replacement, MongoDB throws a WriteError. For example:
// Wrong: will fail
db.product_catalog.replaceOne({ sku: 'TSHIFT-001' }, { $set: { price: 25 } });
Fix: Use updateOne with $set, or write the replacement as a plain document.
Problem: The _id in the replacement doesn't match the original
If you include an _id field in the replacement that differs from the original document's _id, MongoDB throws an error. For example:
// Wrong: different _id
const replacement = { _id: ObjectId('different'), ... };
Fix: Omit _id from the replacement, or ensure it matches exactly. If you need a new _id, delete the old document and insert a new one.
Problem: modifiedCount is 0 even though the filter matched
MongoDB may detect that the replacement is identical to the existing document and skip the actual write. For example:
const result = db.product_catalog.replaceOne({ sku: 'TEER-001' }, { name: 'Classic Tee', ... }); // same fields
printjson(result.modifiedCount); // might be 0
Fix: This is expected behavior. Check matchedCount to see if a match was found, and treat modifiedCount as "actual write performed."
Problem: Only the first matching document is replaced
replaceOne replaces only the first document that matches the filter, not all. If you expect to replace multiple documents, you'll need a loop or replaceMany (which doesn't exist — you'll need to use bulkWrite or iterate).
// Only one document updated
db.product_catalog.replaceOne({ category: 'apparel' }, { ... });
Fix: Use updateMany with update operators, or write a cursor loop that calls replaceOne for each document.
What you learned & what's next
You've just added a powerful tool to your MongoDB toolkit. You learned that replace documents with replaceone is a way to swap out an entire document atomically while preserving the _id. You now understand:
- The mental model of replacing a folder with a new one, keeping the label.
- How to use
replaceOne(filter, replacement, options)in practice. - The importance of omitting update operators in the replacement.
- How to use
upsertto insert when no match is found. - When to choose
replaceOneoverupdateOneorfindOneAndReplace. - How to handle common errors like using
$setin replacement or_idmismatches.
You're ready to apply replaceOne in your own projects — whether you're migrating a data model or syncing external system snapshots into MongoDB.
Next in the track, you'll explore bulk write operations to efficiently perform multiple replacements and updates in a single call. This will take your MongoDB proficiency to the next level by reducing network round-trips and making your code faster and more maintainable. Keep going!
Practice recap
Now it's your turn: Using the product_catalog collection from this lesson, practice replacing a document that has an entirely new shape but the same _id. Try using upsert: true with a non-existent SKU and check the result object. Then, deliberately attempt to use $set inside a replacement and observe the error — this will make the rule stick.
Common mistakes
- Using update operators like
$setinside the replacement document — MongoDB throws a WriteError. Remember:replaceOneexpects a plain document, not an update expression. - Forgetting that
replaceOnereplaces ALL fields — if you omit fields from the replacement, they are lost. Always include every field you want to keep. - Changing the
_idin the replacement document — MongoDB will throw an error if the_iddoesn't match the original. Omit it or keep it identical. - Assuming
replaceOneupdates all matching documents — it only replaces the first match. UseupdateManyfor multiple documents. - Ignoring
matchedCountandmodifiedCountin the result — these tell you whether a match occurred and whether a write actually happened, crucial for debugging.
Variations
- Use
findOneAndReplacewhen you need the replaced document returned immediately, e.g., to use in your application logic. - Use
updateOnewith update operators when you only need to modify a few fields — replacing the whole document would be wasteful. - Use
bulkWriteto perform multiplereplaceOneoperations in a single call for better performance.
Real-world use cases
- Syncing a user's profile from an external CRM — replace the entire user document with the latest snapshot each time, preserving
_id. - Rebranding a product catalog — replacing outdated product documents with new structures (new fields, removed legacy ones) in one atomic operation.
- Resetting a session or state document — when a user's session needs a fresh start, replace the whole document with initial values instead of patching many fields.
Key takeaways
replaceOneswaps an entire document in a single atomic operation, preserving the_id.- The replacement document must be a plain object — no update operators like
$setallowed. - All fields not in the replacement are removed; include every field you want to keep.
- Only the first matching document is replaced;
upsert: trueinserts if no match is found. - Use
matchedCountandmodifiedCountto verify the operation's outcome. - Choose
replaceOnefor full-document snapshot updates,updateOnefor partial modifications.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.