Update Documents
MongoDB provides updateOne(), updateMany(), and replaceOne() for modifying documents. Unlike SQL's UPDATE statement, MongoDB updates use update operators that describe how to modify fields — making updates surgical and avoiding full-document rewrites.
updateOne()
updateOne() modifies the first document that matches the filter. It takes two required arguments — a filter document and an update document — plus an optional options object.
updateOne() — change a single field
const { MongoClient, ObjectId } = require('mongodb');
const client = new MongoClient('mongodb://localhost:27017');
const db = client.db('mydb');
const users = db.collection('users');
// Update email for a specific user
const result = await users.updateOne(
{ _id: new ObjectId('64a1f2c3d4e5f6a7b8c9d0e1') }, // filter
{ $set: { email: 'newemail@example.com' } } // update
);
// Update by a field value
await users.updateOne(
{ username: 'alice' },
{ $set: { lastLogin: new Date(), active: true } }
);updateOne() result
{
acknowledged: true,
matchedCount: 1, // number of documents that matched the filter
modifiedCount: 1, // number of documents actually changed
upsertedId: null // set if upsert: true and a new document was inserted
}updateMany()
updateMany() applies the same update to all documents matching the filter. The syntax is identical to updateOne() — only the scope differs.
updateMany() — bulk field update
// Mark all users who signed up in 2023 as legacy accounts
await users.updateMany(
{
createdAt: {
$gte: new Date('2023-01-01'),
$lt: new Date('2024-01-01'),
},
},
{ $set: { accountType: 'legacy', migrated: false } }
);
// Add a new field to every document in the collection
await users.updateMany(
{}, // empty filter matches all documents
{ $set: { schemaVersion: 2 } }
);
console.log(`Modified ${result.modifiedCount} documents`);The $set Operator
$set is the most common update operator. It sets the value of a field. If the field does not exist, $set creates it. You can update multiple fields in one operation, and you can target nested fields using dot notation.
$set — add or update fields
// Set multiple top-level fields at once
await users.updateOne(
{ username: 'bob' },
{
$set: {
email: 'bob@example.com',
age: 30,
verified: true,
},
}
);
// Set a nested field using dot notation
await users.updateOne(
{ username: 'bob' },
{
$set: {
'address.city': 'Berlin',
'address.country': 'Germany',
'preferences.theme': 'dark',
},
}
);The $unset Operator
$unset removes a field from a document entirely. The value you specify for the field in the operator document is irrelevant — conventionally an empty string "" is used.
$unset — remove fields
// Remove the 'temporaryToken' field from a user
await users.updateOne(
{ username: 'carol' },
{ $unset: { temporaryToken: '', resetCode: '' } }
);
// Remove a nested field
await users.updateOne(
{ username: 'carol' },
{ $unset: { 'preferences.betaFeatures': '' } }
);The $inc Operator
$inc increments a numeric field by the specified amount. Use a negative value to decrement. If the field does not exist, $inc creates it and sets it to the increment value.
$inc — increment a counter
// Increment a post's view counter by 1
await db.collection('posts').updateOne(
{ slug: 'getting-started-with-mongodb' },
{ $inc: { views: 1 } }
);
// Decrement stock and increment sold count simultaneously
await db.collection('products').updateOne(
{ sku: 'WIDGET-42' },
{ $inc: { stock: -1, soldCount: 1 } }
);
// Works even if fields don't exist yet
await db.collection('stats').updateOne(
{ userId: 'user123' },
{ $inc: { loginCount: 1 } },
{ upsert: true }
);The $push and $pull Operators
$push appends a value to an array field. $pull removes elements from an array that match a condition. Both create the array field if it does not exist.
$push — add to array
// Push a single tag onto an article's tags array
await db.collection('articles').updateOne(
{ slug: 'intro-to-mongodb' },
{ $push: { tags: 'database' } }
);
// Push multiple items at once using $each
await db.collection('articles').updateOne(
{ slug: 'intro-to-mongodb' },
{ $push: { tags: { $each: ['nosql', 'tutorial', 'beginner'] } } }
);
// Push and cap the array at 10 items (keeps the last 10)
await db.collection('users').updateOne(
{ _id: userId },
{
$push: {
recentlyViewed: {
$each: [{ productId: 'abc', viewedAt: new Date() }],
$slice: -10, // keep only the last 10 elements
},
},
}
);$pull — remove from array
// Remove a specific value from the tags array
await db.collection('articles').updateOne(
{ slug: 'intro-to-mongodb' },
{ $pull: { tags: 'draft' } }
);
// Pull elements matching a condition (remove expired items)
await db.collection('users').updateOne(
{ _id: userId },
{
$pull: {
notifications: { expiresAt: { $lt: new Date() } },
},
}
);
// Pull from all matching documents
await db.collection('products').updateMany(
{},
{ $pull: { categories: 'discontinued' } }
);The $addToSet Operator
$addToSet adds a value to an array only if it is not already present — preventing duplicates. It is ideal for maintaining unique sets of values like tags or role lists.
$addToSet — unique array items
// Add a role only if the user doesn't already have it
await users.updateOne(
{ username: 'dave' },
{ $addToSet: { roles: 'editor' } }
);
// Running this twice still results in roles containing 'editor' only once
// Add multiple unique values at once
await users.updateOne(
{ username: 'dave' },
{ $addToSet: { roles: { $each: ['editor', 'reviewer'] } } }
);The $rename Operator
$rename renames a field. The document key is the current field name; the value is the new name. If the field does not exist, $rename does nothing.
// Rename 'fname' to 'firstName' across all users
await users.updateMany(
{ fname: { $exists: true } },
{ $rename: { fname: 'firstName', lname: 'lastName' } }
);The $mul Operator
$mul multiplies the value of a field by the specified number. Useful for applying percentage price changes or scaling values.
// Apply a 10% price increase to all premium products
await db.collection('products').updateMany(
{ tier: 'premium' },
{ $mul: { price: 1.1 } }
);
// If the field doesn't exist, $mul sets it to 0
await db.collection('items').updateOne(
{ _id: itemId },
{ $mul: { discountFactor: 0.9 } }
);Upsert — Insert if Not Found
Pass { upsert: true } as the third argument to insert a document if no match is found. The inserted document is built from the filter fields merged with the update operators.
Upsert example
// Create or update user preferences — insert if doesn't exist
const result = await db.collection('preferences').updateOne(
{ userId: 'user123' },
{
$set: {
theme: 'dark',
language: 'en',
updatedAt: new Date(),
},
$setOnInsert: {
createdAt: new Date(), // only set when inserting
},
},
{ upsert: true }
);
if (result.upsertedId) {
console.log('Inserted new preferences doc:', result.upsertedId);
} else {
console.log('Updated existing preferences');
}replaceOne()
replaceOne() replaces the entire document matched by the filter with a new document. Unlike update operators, you pass a plain document as the second argument — not an operator expression.
replaceOne() — full document replacement
// Replace the entire document (only _id is preserved)
await users.replaceOne(
{ username: 'eve' },
{
username: 'eve',
email: 'eve@example.com',
role: 'admin',
createdAt: new Date(),
// All previous fields not listed here are gone
}
);findOneAndUpdate()
findOneAndUpdate() atomically finds a document, updates it, and returns either the original or the updated version. It's ideal for atomic read-modify cycles such as claiming a queued job.
findOneAndUpdate() — atomic update and return
// Return the document AFTER the update
const updated = await db.collection('jobs').findOneAndUpdate(
{ status: 'pending' },
{
$set: { status: 'processing', startedAt: new Date() },
$inc: { attempts: 1 },
},
{
returnDocument: 'after', // 'before' returns the pre-update version
sort: { createdAt: 1 }, // process oldest first
}
);
if (updated) {
console.log('Processing job:', updated._id);
} else {
console.log('No pending jobs');
}Array Update Operators Summary
Operator | Purpose | Example |
|---|---|---|
$push | Append to array | { $push: { tags: "new" } } |
$pull | Remove matching elements | { $pull: { tags: "old" } } |
$addToSet | Append only if not present | { $addToSet: { roles: "admin" } } |
$pop | Remove first (-1) or last (1) element | { $pop: { items: 1 } } |
$pullAll | Remove all listed values | { $pullAll: { scores: [0, -1] } } |
$each | Push/addToSet multiple values | { $push: { tags: { $each: ["a","b"] } } } |
$slice | Trim array after $push (with $each) | { $push: { log: { $each: [entry], $slice: -100 } } } |
$sort | Sort array after $push (with $each) | { $push: { scores: { $each: [9], $sort: -1 } } } |