Update Operators
Update operators are the vocabulary you use inside the second argument of updateOne()/updateMany() to describe what changes, as opposed to a full document replacement. This page catalogs the field, array, and pipeline update operators you'll reach for constantly.
Field Update Operators
Operator | Effect |
|---|---|
$set | Sets a field to a value, creating it if it does not exist |
$unset | Removes a field entirely |
$inc | Increments (or decrements, with a negative value) a numeric field |
$mul | Multiplies a numeric field by a given value |
$rename | Renames a field |
$min | Sets the field only if the given value is less than the current value |
$max | Sets the field only if the given value is greater than the current value |
$currentDate | Sets a field to the current date/time |
$set / $unset
$set and $unset
db.users.updateOne(
{ _id: 1 },
{
$set: { status: "active", "profile.title": "Engineer" },
$unset: { tempFlag: "" }
}
)$inc / $mul
Atomic increment and multiply
// Increment (or decrement with a negative number)
db.products.updateOne({ sku: "WIDGET-42" }, { $inc: { stock: -1, viewCount: 1 } })
// Multiply — e.g., apply a 10% price increase across a category
db.products.updateMany({ category: "widgets" }, { $mul: { price: 1.10 } })$inc is atomic at the document level — safe for concurrent counters like view counts or inventory decrements without a separate read-modify-write cycle.$rename
Renaming a field across a collection
db.users.updateMany({}, { $rename: { "username": "handle" } })$rename only renames a field if it exists on that document. It's a schema-level change applied per document — run it with an empty filter ({}) to apply it across an entire collection, and expect it to be slow on large collections since every document is rewritten.$min / $max
Conditional set — only if the new value wins
// Only updates if 3 is less than the current "lowestPrice"
db.products.updateOne({ sku: "WIDGET-42" }, { $min: { lowestPrice: 3 } })
// Only updates if the new score is higher than the current "highScore"
db.leaderboard.updateOne({ userId: 7 }, { $max: { highScore: 9800 } })$currentDate
Timestamping an update
db.sessions.updateOne(
{ _id: "sess_123" },
{ $currentDate: { lastActivity: true, "meta.lastModifiedTs": { $type: "timestamp" } } }
)
// lastActivity → BSON Date
// meta.lastModifiedTs → BSON Timestamp (internal type — Date is almost always what you want)Array Update Operators
Operator | Effect |
|---|---|
$push | Appends a value (or values, with $each) to an array |
$pull | Removes all array elements matching a condition |
$addToSet | Appends a value only if it is not already present |
$pop | Removes the first (-1) or last (1) element of an array |
$push, $addToSet, $pull, $pop
// $push — append one or more values
db.posts.updateOne({ _id: 1 }, { $push: { tags: "mongodb" } })
db.posts.updateOne({ _id: 1 }, { $push: { tags: { $each: ["nosql", "database"] } } })
// $addToSet — append only if not already present (a "set" semantics)
db.posts.updateOne({ _id: 1 }, { $addToSet: { tags: "mongodb" } }) // no-op if already there
// $pull — remove every element matching a condition
db.posts.updateOne({ _id: 1 }, { $pull: { tags: "deprecated" } })
db.orders.updateOne({ _id: 1 }, { $pull: { items: { qty: { $lt: 1 } } } })
// $pop — remove the last (1) or first (-1) element
db.queue.updateOne({ _id: 1 }, { $pop: { pending: 1 } }) // removes the last elementThe Positional $ Operator
The positional $ operator updates the first array element that matched the query filter, without needing to know its index.
Update the first matching array element
// Mark the first item with sku "WIDGET-42" as shipped
db.orders.updateOne(
{ _id: 42, "items.sku": "WIDGET-42" },
{ $set: { "items.$.shipped": true } }
)$ operator only affects the first matching element, even if several elements in the array match the filter. Use $[] (all elements) or $[identifier] with arrayFilters to affect more than one.$[] — Update All Array Elements
Update every element in an array
// Apply a 5% discount to every item in every order
db.orders.updateMany({}, { $mul: { "items.$[].price": 0.95 } })$[identifier] and arrayFilters
When you need to update only the array elements that match a condition, use a named identifier in the update path and describe that condition separately in arrayFilters.
Conditional array element updates
// Mark only the line items with qty >= 2 as bulk-eligible
db.orders.updateOne(
{ _id: 42 },
{ $set: { "items.$[el].bulkEligible": true } },
{ arrayFilters: [{ "el.qty": { $gte: 2 } }] }
)
// Multiple identifiers can target nested array levels independently
db.students.updateOne(
{ _id: 1 },
{ $set: { "grades.$[g].scores.$[s].verified": true } },
{
arrayFilters: [
{ "g.subject": "Math" },
{ "s.value": { $gte: 90 } }
]
}
)Pipeline Updates
Since MongoDB 4.2, the update argument can be an aggregation pipeline (an array of stages) instead of a plain update document. This unlocks computing a new field's value from other fields in the same document — something plain $set alone cannot do in one operator (short of $expr-adjacent tricks).
Pipeline update — derive a field from other fields
db.orders.updateMany(
{},
[
{ $set: { total: { $sum: "$items.price" } } },
{ $set: { totalWithTax: { $multiply: ["$total", 1.13] } } }
]
)
// Each stage can reference fields set by a previous stage in the SAME pipeline update$set/$addFields, $unset, $replaceRoot/$replaceWith, and $project. You cannot use $match, $group, or $lookup inside an update pipeline.$set/$unset— the everyday field mutators.$inc/$mul— atomic numeric changes, safe under concurrency.$push/$addToSet/$pull/$pop— array mutation.$[]and$[identifier]witharrayFilters— target multiple or conditional array elements.Pipeline updates (array form) — compute a field from other fields in the same update.