00:00

MongoDB Update Document

Last updated: 6 min read

Data rarely stays the same: prices change, stock goes up and down, orders move from "pending" to "shipped". MongoDB updates documents in place with update operators that change only the fields you name, so you never have to read a document, modify it in code and write the whole thing back.

This tutorial covers updateOne(), updateMany(), replaceOne() and findOneAndUpdate(), the field and array operators, upserts, updates with aggregation pipelines, and the Java equivalents.

1. Sample Data

mongosh
use bookstore
db.books.drop()
db.books.insertMany([
  { _id: 1, title: "Practical Java Streams",   category: "Java",     price: 499, stock: 40, tags: ["java"] },
  { _id: 2, title: "Designing Event Systems",  category: "Kafka",    price: 650, stock: 12, tags: ["kafka"] },
  { _id: 3, title: "Document Modeling Basics", category: "Database", price: 420, stock: 0,  tags: ["mongodb"] }
])

2. The Update Methods

MethodWhat it does
updateOne(filter, update, options)Updates the first matching document
updateMany(filter, update, options)Updates every matching document
replaceOne(filter, replacement, options)Replaces the whole document except _id
findOneAndUpdate(filter, update, options)Updates one document and returns it, before or after the change

3. updateOne() and the Result

mongosh
db.books.updateOne(
  { _id: 1 },                          // filter
  { $set: { price: 459, onSale: true } }   // update
)
// {
//   acknowledged: true,
//   insertedId: null,
//   matchedCount: 1,
//   modifiedCount: 1,
//   upsertedCount: 0
// }
  • matchedCount: how many documents matched the filter.
  • modifiedCount: how many actually changed. Setting a field to the value it already has counts as matched but not modified.

4. updateMany()

mongosh
// 10% off every Java book
db.books.updateMany({ category: "Java" }, { $mul: { price: 0.9 } })

// Mark everything out of stock as unavailable
db.books.updateMany({ stock: 0 }, { $set: { available: false } })
An empty filter {} matches every document in the collection. Before a large updateMany, run countDocuments() with the same filter to see how many documents it will touch.

5. Field Update Operators

OperatorEffectExample
$setSets fields (creates them if missing){ $set: { "publisher.country": "IN" } }
$unsetRemoves fields{ $unset: { onSale: "" } }
$incAdds to a number (use negative to subtract){ $inc: { stock: -1 } }
$mulMultiplies a number{ $mul: { price: 1.05 } }
$min / $maxUpdates only if the new value is lower / higher{ $min: { lowestPrice: 399 } }
$renameRenames a field{ $rename: { stock: "quantity" } }
$currentDateSets a field to the current date{ $currentDate: { updatedAt: true } }
$setOnInsertSets fields only when an upsert inserts{ $setOnInsert: { createdAt: new Date() } }
mongosh: several operators at once
// A sale: one copy sold, record the time, remember the lowest price seen
db.books.updateOne(
  { _id: 2, stock: { $gt: 0 } },            // only if in stock
  {
    $inc: { stock: -1, sold: 1 },
    $min: { lowestPrice: 650 },
    $currentDate: { updatedAt: true }
  }
)

Including stock: { $gt: 0 } in the filter makes the "check and decrement" a single atomic step, so two customers can never both buy the last copy.

6. Array Update Operators

mongosh
db.books.updateOne({ _id: 1 }, { $push: { tags: "functional" } })            // append
db.books.updateOne({ _id: 1 }, { $addToSet: { tags: "java" } })              // append only if absent
db.books.updateOne({ _id: 1 }, { $push: { tags: { $each: ["lambdas", "jdk21"] } } })  // append several
db.books.updateOne({ _id: 1 }, { $pull: { tags: "jdk21" } })                 // remove matching values
db.books.updateOne({ _id: 1 }, { $pop: { tags: 1 } })                        // remove last (-1: first)

$push with $sort and $slice keeps a bounded, ordered list, for example the latest five reviews:

mongosh
db.books.updateOne({ _id: 2 }, {
  $push: { recentReviews: {
    $each: [ { user: "ken", stars: 5, at: new Date() } ],
    $sort: { at: -1 },
    $slice: 5
  } }
})

7. Updating Elements Inside Arrays

mongosh
db.orders.insertOne({ _id: 5001, items: [
  { bookId: 1, qty: 1, status: "PACKED" },
  { bookId: 3, qty: 2, status: "BACKORDER" }
] })

// $   : the FIRST element that matched the filter
db.orders.updateOne({ _id: 5001, "items.bookId": 3 }, { $set: { "items.$.qty": 1 } })

// $[] : EVERY element
db.orders.updateOne({ _id: 5001 }, { $set: { "items.$[].status": "SHIPPED" } })

// $[name] with arrayFilters: every element matching a condition
db.orders.updateOne(
  { _id: 5001 },
  { $set: { "items.$[it].gift": true } },
  { arrayFilters: [ { "it.qty": { $gte: 1 } } ] }
)

8. Upserts: Update or Insert

With upsert: true, if no document matches the filter, MongoDB inserts a new one built from the filter's equality fields plus the update:

mongosh
db.dailyStats.updateOne(
  { day: "2026-10-18", page: "/mongodb-tutorial" },
  { $inc: { views: 1 }, $setOnInsert: { firstSeen: new Date() } },
  { upsert: true }
)
// First call inserts { day, page, views: 1, firstSeen }; later calls just increment views

Put a unique index on the filter fields of upserts that may run concurrently, so two parallel requests cannot create two documents.

9. replaceOne()

replaceOne() swaps the whole document, keeping only _id. Fields not in the replacement disappear, so use it only when you really have the complete new document:

mongosh
db.books.replaceOne(
  { _id: 3 },
  { title: "Document Modeling Basics, 2nd Edition", category: "Database", price: 480, stock: 30 }
)

10. findOneAndUpdate()

When you need the document back, for example to show the new stock level or to claim a job from a queue, use findOneAndUpdate(). It is atomic, so two workers will never claim the same job:

mongosh
db.jobs.findOneAndUpdate(
  { status: "QUEUED" },
  { $set: { status: "RUNNING", worker: "worker-7", startedAt: new Date() } },
  { sort: { priority: -1, createdAt: 1 }, returnDocument: "after" }
)

11. Updates with an Aggregation Pipeline

An update can also be an array of aggregation stages ($set, $unset, $replaceWith…), which lets new values depend on other fields of the same document:

mongosh
// Set a priceBand field computed from the price
db.books.updateMany({}, [
  { $set: { priceBand: {
      $switch: {
        branches: [
          { case: { $lt: ["$price", 450] }, then: "budget" },
          { case: { $lt: ["$price", 600] }, then: "standard" }
        ],
        default: "premium"
      } } } }
])

12. Updating from Java

UpdateBooksExample.java
import com.mongodb.client.*;
import com.mongodb.client.model.*;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Updates.*;

public class UpdateBooksExample {
    public static void main(String[] args) {
        try (MongoClient client = MongoClients.create("mongodb://localhost:27017")) {
            MongoCollection<Document> books = client.getDatabase("bookstore").getCollection("books");

            UpdateResult r = books.updateOne(eq("_id", 1),
                    combine(set("price", 459), addToSet("tags", "functional"), currentDate("updatedAt")));
            System.out.println("matched=" + r.getMatchedCount() + " modified=" + r.getModifiedCount());

            books.updateMany(eq("stock", 0), set("available", false));

            // Atomic stock decrement that returns the new document
            Document after = books.findOneAndUpdate(
                    and(eq("_id", 2), gt("stock", 0)),
                    inc("stock", -1),
                    new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER));

            // Upsert
            client.getDatabase("bookstore").getCollection("dailyStats").updateOne(
                    and(eq("day", "2026-10-18"), eq("page", "/mongodb-tutorial")),
                    inc("views", 1),
                    new UpdateOptions().upsert(true));
        }
    }
}

13. Summary

  • Use updateOne() or updateMany() with operators such as $set, $inc and $unset to change only what is needed.
  • Array operators ($push, $addToSet, $pull) and positional paths ($, $[], $[name] with arrayFilters) update lists precisely.
  • upsert: true inserts when nothing matches; protect concurrent upserts with a unique index.
  • replaceOne() replaces the whole document; findOneAndUpdate() updates atomically and returns the document.
  • Pipeline updates compute new values from existing fields.