Updating documents

MongoDB · Essentials · 10 min

Updating in place with operators is atomic and avoids read-modify-write races.

updateOne(filter, update, options) changes the first matching document; updateMany changes all matches. The update uses update operators:

  • $set: { status: "shipped" } sets fields (creating them, including nested paths), $unset: { coupon: "" } removes them,
  • $inc: { stock: -1 } adds to a number, $min/$max keep the smaller/larger value,
  • $push: { tags: "sale" } appends to an array, $addToSet appends only if absent, $pull removes matching elements,
  • $currentDate: { updatedAt: true } stamps the server time.

The result reports matchedCount and modifiedCount; setting a field to the value it already has counts as matched but not modified.

With { upsert: true }, if nothing matches, a new document is built from the equality fields of the filter plus the update, which is handy for counters and "create or update" endpoints.

Each update to one document is atomic, so $inc from many clients at once never loses an increment, unlike reading a value, adding in your code and writing it back.

To swap the whole document use replaceOne(filter, newDoc). Passing a plain document without operators to updateOne is an error, which protects you from accidentally wiping fields. findOneAndUpdate returns the document before or after the change (returnDocument: "after").

Example

db.stock.insertOne({ _id: "PEN-01", qty: 10, tags: ["office"] })
db.stock.updateOne(
  { _id: "PEN-01" },
  { $inc: { qty: -3 }, $addToSet: { tags: "sale" }, $currentDate: { updatedAt: true } }
)
// { acknowledged: true, matchedCount: 1, modifiedCount: 1, upsertedCount: 0, ... }
db.stock.findOne({ _id: "PEN-01" }, { qty: 1, tags: 1 })
// { _id: 'PEN-01', qty: 7, tags: [ 'office', 'sale' ] }

db.pageviews.updateOne({ page: "/home" }, { $inc: { views: 1 } }, { upsert: true })
// first call: upsertedCount: 1 and creates { page: '/home', views: 1 }
db.stock.updateOne({ _id: "PEN-01" }, { qty: 0 })
// MongoInvalidArgumentError: Update document requires atomic operators

Remember

  • Use operators: $set/$unset, $inc, $push/$addToSet/$pull
  • updateOne changes the first match, updateMany all matches
  • upsert: true inserts when nothing matches
  • Operator updates are atomic per document: no lost increments

Watch out for

  • Reading a value, changing it in application code and writing it back: concurrent requests overwrite each other; use $inc or $set with a condition.

Check yourself

  1. Two clients run updateOne({ _id: 1 }, { $inc: { n: 1 } }) at the same moment on n = 5. What is n afterwards?
  2. What does updateOne({ sku: "X" }, { $set: { qty: 1 } }, { upsert: true }) do if no document has sku X?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.mongodb.com/docs/manual/tutorial/update-documents/