âœī¸ Update Operators in MongoDB

MongoDB's update operators are special $-prefixed keys used inside update documents to describe exactly how a document's fields should change — without needing to send the entire document back to the server. This tutorial covers every major category of update operator, from simple field assignment to advanced array manipulation and aggregation-style update pipelines.

Information

Examples use the official MongoDB Node.js Driver and assume a collection named products.

📖 1. Introduction

Instead of replacing a whole document, update operators let you target specific fields — incrementing a counter, pushing a value into an array, or renaming a field — in a single atomic operation.

Combining update operators

await collection.updateOne(
  { _id: productId },
  { $set: { price: 24.99 }, $inc: { views: 1 } }
);

Note

An update document must contain only update operators, or it will be interpreted as a full replacement document instead.

🧩 2. Update Operations

Update operators are applied through updateOne(), updateMany(), findOneAndUpdate(), or as part of a bulkWrite() call. Each accepts a filter to select documents and an update document describing the changes.

Update Anatomy
Filter (which documents)
Operator (what kind of change)
Modifier (the new value)

🔧 3. Field Update Operators

Field update operators modify scalar and document-level fields: setting values, removing fields, renaming, incrementing, and more.

$set

Sets the value of a field, creating it if it doesn't already exist.

Setting field values

await collection.updateOne(
  { name: "Mouse" },
  { $set: { price: 22.99, onSale: true } }
);

$unset

Removes a field from a document entirely. The value passed is ignored — an empty string is conventional.

Removing a field

await collection.updateOne(
  { name: "Mouse" },
  { $unset: { discountCode: "" } }
);

$rename

Renames a field, preserving its value.

Renaming a field

await collection.updateOne(
  { name: "Mouse" },
  { $rename: { qty: "stockCount" } }
);

Warning

$rename errors if the target field name already exists on the document — it will not silently overwrite it in some MongoDB versions, so test carefully.

$inc

Increments (or decrements, with a negative value) a numeric field atomically.

Incrementing and decrementing fields

await collection.updateOne(
  { name: "Mouse" },
  { $inc: { stockCount: -1, views: 1 } }
);

$mul

Multiplies a field's current value by the given number.

Multiplying a field value

// Apply a 10% price increase
await collection.updateOne(
  { name: "Mouse" },
  { $mul: { price: 1.1 } }
);

$min / $max

$min updates a field only if the new value is less than the current value; $max only if it's greater than.

Conditional bounds with $min and $max

// Only updates if 15.99 is lower than the current lowestPrice
await collection.updateOne(
  { name: "Mouse" },
  { $min: { lowestPrice: 15.99 } }
);

// Only updates if 199.99 is higher than the current highestPrice
await collection.updateOne(
  { name: "Mouse" },
  { $max: { highestPrice: 199.99 } }
);

$currentDate

Sets a field to the current date, either as a Date or a BSON timestamp.

Stamping the current date

await collection.updateOne(
  { name: "Mouse" },
  { $currentDate: { lastModified: true } }
);

Tip

Use $currentDate to automatically maintain audit fields like updatedAt without computing the timestamp client-side.

📚 12. Array Update Operators

Array update operators add, remove, or modify elements within array fields without needing to fetch and rewrite the entire array.

OperatorDescription
$pushAppends a value to an array
$pullRemoves all elements matching a condition
$popRemoves the first or last element
$addToSetAdds a value only if not already present
$pullAllRemoves all matching listed values

$push

Appending to an array

// Append a single tag
await collection.updateOne(
  { name: "Mouse" },
  { $push: { tags: "wireless" } }
);

// Append multiple values with $each
await collection.updateOne(
  { name: "Mouse" },
  { $push: { tags: { $each: ["sale", "featured"] } } }
);

$pull

Removing matching elements

// Remove exact value
await collection.updateOne(
  { name: "Mouse" },
  { $pull: { tags: "discontinued" } }
);

// Remove elements matching a condition
await collection.updateOne(
  { name: "Mouse" },
  { $pull: { reviews: { rating: { $lt: 2 } } } }
);

$pop

Removes the first (-1) or last (1) element of an array.

Popping array elements

// Remove the last element
await collection.updateOne(
  { name: "Mouse" },
  { $pop: { tags: 1 } }
);

// Remove the first element
await collection.updateOne(
  { name: "Mouse" },
  { $pop: { tags: -1 } }
);

$addToSet

Adds a value to an array only if it doesn't already exist, preventing duplicates.

Avoiding duplicate array values

await collection.updateOne(
  { name: "Mouse" },
  { $addToSet: { tags: "wireless" } }
);

Tip

Combine $addToSet with $each to add multiple unique values in a single operation.

$pullAll

Removes all instances of each value in a given list, matching exact values only (no query conditions).

Removing a list of exact values

await collection.updateOne(
  { name: "Mouse" },
  { $pullAll: { tags: ["clearance", "old-stock"] } }
);

đŸŽ¯ 18. Positional Operators

Positional operators target specific array elements within an update, rather than the array as a whole.

OperatorDescription
$Updates the first array element that matched the query filter
$[]Updates all elements in the array
$[<identifier>]Updates elements matching an arrayFilters condition

Positional operators in action

// Update the rating of the first review that matches
await collection.updateOne(
  { _id: productId, "reviews.author": "Alice" },
  { $set: { "reviews.$.rating": 5 } }
);

// Set every review's "verified" flag to true
await collection.updateOne(
  { _id: productId },
  { $set: { "reviews.$[].verified": true } }
);

Important

The $ positional operator updates only the first matching array element, even if multiple elements match the query filter.

🧮 19. Array Filters

The arrayFilters option, combined with the $[identifier] syntax, lets you update array elements matching custom conditions — independent of the top-level query filter.

Updating with arrayFilters

await collection.updateOne(
  { _id: productId },
  { $set: { "reviews.$[elem].flagged": true } },
  { arrayFilters: [{ "elem.rating": { $lte: 1 } }] }
);

Example

This updates every review inside the array whose rating is 1 or lower, regardless of its position.

🔁 20. Upserts

An upsert creates a new document when no document matches the filter, applying the update operators to the new document as if it started empty.

Upsert with $setOnInsert

const result = await collection.updateOne(
  { sku: "SKU-2001" },
  { $set: { name: "Desk Lamp", price: 29.99 }, $setOnInsert: { createdAt: new Date() } },
  { upsert: true }
);

Hint

$setOnInsert applies values only when a new document is inserted, never on an existing match — ideal for fields like createdAt.

🔄 21. Replace Operations

Unlike operator-based updates, replaceOne() swaps out the entire document (except _id) rather than modifying individual fields.

Replacing an entire document

await collection.replaceOne(
  { name: "Mouse" },
  { name: "Wireless Mouse", price: 25.99, inStock: true }
);

Danger

Any field not present in the replacement document is permanently removed from the original — use update operators instead if you only need a partial change.

âš™ī¸ 22. Bulk Updates

bulkWrite() lets you batch many updateOne, updateMany, and other operations into a single round-trip, significantly improving throughput.

Batching updates with bulkWrite()

await collection.bulkWrite([
  {
    updateOne: {
      filter: { name: "Mouse" },
      update: { $inc: { stockCount: -1 } }
    }
  },
  {
    updateMany: {
      filter: { inStock: false },
      update: { $set: { discontinued: true } }
    }
  }
]);

🧠 23. Update Pipelines

Since MongoDB 4.2, updates can use an aggregation pipeline (an array of stages) instead of a plain update document, unlocking expressions that reference the document's own fields.

Using an update pipeline

await collection.updateOne(
  { name: "Mouse" },
  [
    { $set: { finalPrice: { $multiply: ["$price", 0.9] } } },
    { $set: { updatedAt: "$$NOW" } }
  ]
);

Reference

Update pipelines support only a subset of aggregation stages — $set, $unset, $replaceRoot, and a few others. See the MongoDB update pipeline docs for the full list.

⚡ 24. Performance Optimization

  • Ensure the filter portion of an update is covered by an index to avoid a full collection scan.
  • Prefer $inc over read-modify-write patterns to avoid race conditions and extra round-trips.
  • Batch related updates with bulkWrite() instead of looping individual updateOne() calls.
  • Use arrayFilters instead of fetching, mutating, and replacing entire arrays client-side.

🌟 25. Best Practices

  • Always use $set for partial field updates instead of replaceOne().
  • Use $setOnInsert alongside upsert: true to safely initialize fields only on creation.
  • Prefer $addToSet over $push when array values must remain unique.
  • Name arrayFilters identifiers descriptively (e.g. elem, review) for readability.
  • Test update pipelines on a sample document before running them against production data.

âš ī¸ 26. Common Mistakes

  1. Mixing update operators and plain fields in the same update document, which MongoDB rejects.
  2. Forgetting that the $ positional operator only updates the first matching array element.
  3. Using $push when $addToSet was intended, resulting in duplicate array entries.
  4. Omitting arrayFilters when using the $[identifier] syntax, causing an error.
  5. Accidentally using replaceOne() instead of updateOne(), wiping out unspecified fields.

Best Practice

Before deploying a new update to production, run it against a single test document with findOneAndUpdate() and inspect the result carefully.

❓ 27. Frequently Asked Questions

Yes. If the target field doesn't already exist, $set creates it with the given value.

Yes. A single update document can combine multiple operators, such as $set, $inc, and $push, as long as they don't target the same field.

Yes. $inc is applied atomically at the document level, making it safe for concurrent increments like counters.

📌 28. Summary

>>"The right update operator changes exactly what needs to change — nothing more, nothing less."

Summary

Mastering update operators lets you modify MongoDB documents precisely and atomically. Next, explore transactions for multi-document consistency and the aggregation framework for advanced data transformations.