R
Rishtaara
MongoDB Fundamentals
Lesson 13 of 40Article16 min

Update Operations

Updates modify existing documents without replacing the entire document (unless you use replaceOne). updateOne changes the first match; updateMany changes every match. Both require update operators like $set — passing a plain object without operators replaces fields incorrectly in some contexts.

updateOne() and updateMany()

Updates modify existing documents without replacing the entire document (unless you use replaceOne). updateOne changes the first match; updateMany changes every match. Both require update operators like $set — passing a plain object without operators replaces fields incorrectly in some contexts.

Real-life example: updateOne is like editing one student's grade in the register. updateMany is like raising every product price by 10% in one go.

updateOne — single document
use rishtaara

// Increase stock and stamp updatedAt
db.products.updateOne(
  { name: "Wireless Mouse" },
  {
    $set: { updatedAt: new Date() },
    $inc: { stock: 50 }
  }
)

// Return value:
// {
//   acknowledged: true,
//   matchedCount: 1,    // documents matching the filter
//   modifiedCount: 1,   // documents actually changed
//   upsertedCount: 0,
//   upsertedId: null
// }

// No match — matchedCount: 0, modifiedCount: 0
db.products.updateOne(
  { name: "Nonexistent Product" },
  { $set: { price: 999 } }
)
updateMany — all matches
// Apply 15% discount to all stationery
db.products.updateMany(
  { category: "stationery" },
  { $mul: { price: 0.85 }, $set: { onSale: true, updatedAt: new Date() } }
)

// Deactivate all students who graduated
db.students.updateMany(
  { grade: { $gt: 12 } },
  { $set: { active: false, graduatedAt: new Date() } }
)

// Add a tag to every electronics product
db.products.updateMany(
  { category: "electronics" },
  { $addToSet: { tags: "tech" } }
)

replaceOne() — full document swap

replaceOne() removes the existing document and inserts the replacement — except _id stays the same. Use only when you truly want to overwrite every field.

Real-life example: replaceOne is like tearing out a page and writing a completely new entry — not just correcting one line.

In 99% of production updates, use updateOne/updateMany with $set, $inc, and other operators. replaceOne is for full document replacement only.
replaceOne example
// Current document has many fields
db.settings.findOne({ key: "app_config" })
// { _id: ..., key: "app_config", theme: "dark", lang: "en", version: 3 }

// replaceOne overwrites ALL fields (except _id)
db.settings.replaceOne(
  { key: "app_config" },
  { key: "app_config", theme: "light", version: 4 }
)
// theme, lang changed; lang field is GONE unless you include it

// Prefer $set for partial updates in production
db.settings.updateOne(
  { key: "app_config" },
  { $set: { theme: "light", version: 4, updatedAt: new Date() } }
)

Upsert — update or insert

Setting { upsert: true } creates a new document when no match is found. Perfect for settings, counters, and idempotent sync operations.

Real-life example: Upsert is like 'update my seat assignment if I have one, otherwise create a new seat' — one operation handles both cases.

Upsert patterns
// Settings key-value store
db.settings.updateOne(
  { key: "maintenance_mode" },
  { $set: { value: false, updatedAt: new Date() } },
  { upsert: true }
)
// Creates { key: "maintenance_mode", value: false, ... } if missing

// User preference sync
db.user_prefs.updateOne(
  { userId: ObjectId("665a1b2c3d4e5f6789012345"), pref: "theme" },
  { $set: { value: "dark", updatedAt: new Date() } },
  { upsert: true }
)

// Return value when upsert creates:
// { matchedCount: 0, modifiedCount: 0, upsertedCount: 1, upsertedId: ObjectId("...") }

Legacy update() and intro to update operators

The legacy update() method is deprecated. Use updateOne, updateMany, or replaceOne instead. Update operators modify specific fields — detailed coverage of $set, $inc, $push, and more comes in the Update Operators module.

Real-life example: $set is like correcting one cell in a spreadsheet. $inc is like adding 10 to a stock count without reading the current value first.

  • Never pass a replacement document to updateOne without operators — use replaceOne if that is your intent.
  • upsert: true is powerful for config and counter collections.
  • $inc is atomic — safe for concurrent stock decrements.
  • Full operator reference ($push, $pull, $addToSet, etc.) is covered in the next module.
Legacy vs modern update
// ❌ Legacy (deprecated)
db.products.update({ name: "Mouse" }, { $set: { price: 799 } })
db.products.update({ category: "sale" }, { $set: { discounted: true } }, { multi: true })

// ✅ Modern
db.products.updateOne({ name: "Mouse" }, { $set: { price: 799 } })
db.products.updateMany({ category: "sale" }, { $set: { discounted: true } })
Quick intro — $set and $inc
// $set — assign or overwrite field values
db.students.updateOne(
  { name: "Asha Khan" },
  { $set: { grade: 11, "address.city": "Delhi", updatedAt: new Date() } }
)

// $inc — increment numeric fields (atomic)
db.products.updateOne(
  { name: "Wireless Mouse" },
  { $inc: { stock: -1, soldCount: 1 } }  // sell one unit
)

// Combine operators in one update
db.students.updateOne(
  { name: "Rahul Sharma" },
  {
    $set: { lastLogin: new Date() },
    $inc: { loginCount: 1 }
  }
)