3.3 Document Replacement & Deletion Operations
Key Takeaways
- replaceOne() replaces the entire content of a matching document while automatically preserving the existing _id.
- The replacement document in replaceOne() cannot contain atomic update operators like $set or $inc; passing update operators throws an immediate runtime error.
- deleteOne() deletes the first matching document, whereas deleteMany() deletes all documents matching the filter condition.
- deleteMany({}) removes documents one-by-one, maintaining indexes and generating oplog delete entries for each document.
- db.collection.drop() drops the entire collection and all its indexes instantly at the storage catalog level, providing vastly superior performance for complete data wipes.
3.3 Document Replacement & Deletion Operations
While field-level update operators modify selected portions of a document, MongoDB also provides document replacement and deletion primitives. replaceOne() swaps out an entire document while preserving its primary key (_id), while deleteOne() and deleteMany() remove documents based on query filter criteria. Understanding how these operations interact with storage engines, indexes, replication oplogs, and sharded environments is a core competency for developers.
1. Document Replacement: replaceOne()
db.collection.replaceOne(filter, replacement, options) completely replaces the existing document matching the query filter with a new replacement document.
db.users.replaceOne(
{ _id: 105 }, // Filter matching the target document
{
name: "Taylor Morgan",
email: "tmorgan@example.com",
tier: "platinum",
updatedAt: new Date()
}, // Replacement document (entire new document body)
{
upsert: true,
writeConcern: { w: "majority", wtimeout: 5000 }
} // Options document
);
Critical Rules of replaceOne()
- Preservation of
_id: The_idfield is immutable in MongoDB. If the replacement document does not specify_id, MongoDB retains the original document's_id. If the replacement document explicitly includes an_id, its value must exactly match the existing document's_id, or MongoDB throws anImmutableFieldor duplicate key error. - Total Field Overwrite: Any fields in the old document that are omitted from the replacement document are permanently removed. The replacement document completely supplants the old document's state.
- Prohibition of Update Operators: The replacement argument must consist solely of
<field>: <value>pairs. It cannot contain atomic update operators (such as$set,$unset, or$inc).
// ERROR EXAMPLE: Passing $set into replaceOne throws an immediate exception
db.users.replaceOne(
{ _id: 105 },
{ $set: { tier: "platinum" } } // MongoServerError: Replacement document must not contain atomic operators
);
replaceOne() Return Document
{
acknowledged: true,
matchedCount: 1,
modifiedCount: 1,
upsertedId: null,
upsertedCount: 0
}
Schema Validation and Sharding Rules for replaceOne()
- JSON Schema Validation: When collection validation rules (
validator: { $jsonSchema: ... }) are configured, MongoDB validates the replacement document in its entirety. BecausereplaceOne()replaces the whole document, the replacement payload must satisfy all required fields specified by the schema validator, regardless of whethervalidationLevelis set to"strict"or"moderate". - Sharded Collections: In a sharded cluster, executing
replaceOne()requires that the query filter includes an exact equality match on the shard key. If the shard key is omitted from the filter in a sharded collection, MongoDB throws a targeting error.
2. Document Deletion Methods: deleteOne() & deleteMany()
MongoDB provides two dedicated methods for deleting documents from a collection:
2.1 deleteOne(filter, options)
Deletes the first document that matches the query filter according to natural storage order or index traversal.
db.orders.deleteOne(
{ orderId: "ORD-8821", status: "CANCELLED" },
{ writeConcern: { w: "majority" } }
);
2.2 deleteMany(filter, options)
Deletes all documents in the collection that satisfy the query filter.
db.sessions.deleteMany(
{ expiresAt: { $lt: new Date() } },
{ writeConcern: { w: 1 } }
);
Deletion Options and Return Document
Both deleteOne() and deleteMany() accept options controlling execution:
writeConcern: Specifies the acknowledgment level (e.g.,{ w: "majority", wtimeout: 5000 }).collation: Specifies locale-sensitive string matching rules for the filter.hint: Specifies an index name or index specification document to force an index scan for the filter.
{
acknowledged: true,
deletedCount: 14
}
| Property | Type | Meaning |
|---|---|---|
acknowledged | Boolean | true if executed with a recognized write concern; false if unacknowledged (w: 0). |
deletedCount | Integer | The exact number of documents removed from the collection. |
Exam Trap: Passing an empty filter
{}todeleteOne({})will delete the first document encountered in the collection. Passing an empty filter{}todeleteMany({})will delete every document in the collection.
3. Deep Architectural Comparison: deleteMany({}) vs. db.collection.drop()
When a developer needs to clear an entire collection (for instance, during an environment reset, batch reprocessing, or test cleanup), MongoDB offers two distinct approaches: executing db.collection.deleteMany({}) or invoking db.collection.drop(). Although both result in zero documents in the collection, their underlying architectural behaviors differ drastically across the storage engine, indexing subsystem, replication oplog, and filesystem.
// Approach A: Document-level bulk wipe
db.logs.deleteMany({});
// Approach B: Catalog-level collection drop
db.logs.drop();
Architectural Comparison Matrix
| Architectural Dimension | db.collection.deleteMany({}) | db.collection.drop() |
|---|---|---|
| Storage Mechanism | Iterates through every document individually, deleting records one by one in the WiredTiger storage engine. | Drops the entire underlying table and metadata in WiredTiger in a single catalog operation. |
| Index Handling | Preserves all indexes. Traverses every index B-tree to delete individual keys, leaving empty index structures in place. | Drops all indexes associated with the collection immediately along with the collection tables. |
| Collection Metadata | Preserves collection metadata, options, JSON Schema validators, and collation settings. | Deletes all collection metadata from the database catalog (system.views and catalog tables). |
| Replication & Oplog Impact | Generates an individual delete entry in the oplog.rs for every single deleted document (massive oplog churn and network bandwidth consumption). | Generates a single drop command entry in the oplog.rs. |
| Storage Reclamation | Reclaims document space inside WiredTiger data files as free space for future MongoDB writes, but does not return space to the OS filesystem. | Immediately frees the underlying data and index files on disk, returning storage blocks to the filesystem. |
| Performance Complexity | $O(N)$ where $N$ is the number of documents + total index entries. Extremely slow for millions of records. | $O(1)$ catalog metadata update. Near instantaneous regardless of collection size. |
When to Use Which Method in Production
- Use
deleteMany({})when:- You must preserve custom indexes and collection configuration (e.g., schema validation rules, collation settings) so that incoming application writes can continue immediately without re-indexing.
- You are deleting a subset of data matching a filter (e.g.,
{ tenantId: "demo" }).
- Use
db.collection.drop()when:- You want to wipe an entire large dataset quickly without incurring severe oplog replication lag, CPU spikes, or B-tree lock contention.
- Note: You must recreate any non-default indexes if your application subsequently writes new data to the collection name.
4. replaceOne() vs. updateOne() Summary
| Feature | db.collection.replaceOne() | db.collection.updateOne() |
|---|---|---|
| Payload Structure | Complete document body ({ name: "Alex", role: "admin" }). | Update operators ({ $set: { role: "admin" } }) or pipeline. |
| Update Operators Permitted? | No (Throws MongoServerError). | Yes (Required). |
| Omitted Fields | Omitted fields are deleted from the document. | Omitted fields are preserved untouched. |
Primary Key (_id) | Preserved from original doc, or must match exactly if supplied. | Preserved automatically. |
A developer runs the following command: db.inventory.replaceOne({ _id: 50 }, { $set: { quantity: 100, status: "IN_STOCK" } }); What is the outcome of this operation?
An existing document in the 'customers' collection is: { _id: 200, name: "Alice", phone: "555-0100", tier: "Gold", loyaltyPoints: 450 }. A developer executes: db.customers.replaceOne({ _id: 200 }, { name: "Alice Smith", email: "alice@example.com" }); What is the final state of the document in the database?
A production database administrator needs to completely purge 50 million old log records from a collection on a busy replica set. Why is db.collection.drop() preferred over db.collection.deleteMany({}) for this task?
What is the result of executing db.orders.deleteOne({}) on a collection containing 100 documents?