Browse documentation
MongoDB TutorialVerified with mongodb 7.5.0

Run MongoDB Bulk Write Operations

Combine inserts, updates, replacements, and deletes with collection bulkWrite, inspect result counts, and handle ordered or unordered failures.

When to use a bulk write

A bulk write sends a group of write models through one driver operation. It reduces application-to-server round trips when a bounded batch already contains different inserts, updates, replacements, or deletes.

Bulk writes are not transactions. With the normal collection API, each individual document write keeps its atomicity, but the whole list does not automatically commit or roll back as one unit. Use a transaction when the business rule requires all-or-nothing behavior across multiple documents.

Keep each batch bounded. The driver can split a large operation into server-compatible batches, but your script still holds the input models and results in memory.

Prepare a repeatable dataset

Create three tutorial products:

use("mongog_tutorial");
const products = db.collection("bulk_products");

await products.deleteMany({ tutorial: "bulk-write" });
await products.insertMany([
  { sku: "BULK-A", name: "Keyboard", stock: 5, status: "active", tutorial: "bulk-write" },
  { sku: "BULK-B", name: "Mouse", stock: 8, status: "active", tutorial: "bulk-write" },
  { sku: "BULK-C", name: "Old cable", stock: 0, status: "retired", tutorial: "bulk-write" },
]);

In production, protect sku or another business identifier with a unique index if concurrent writers depend on uniqueness.

Mix write models in one operation

Collection.bulkWrite() accepts an array of operation models. Every filter below includes the tutorial boundary:

const result = await products.bulkWrite([
  {
    updateOne: {
      filter: { sku: "BULK-A", tutorial: "bulk-write" },
      update: { $inc: { stock: 4 }, $set: { updatedAt: ISODate() } },
    },
  },
  {
    replaceOne: {
      filter: { sku: "BULK-B", tutorial: "bulk-write" },
      replacement: {
        sku: "BULK-B",
        name: "Wireless mouse",
        stock: 12,
        status: "active",
        tutorial: "bulk-write",
      },
    },
  },
  {
    insertOne: {
      document: {
        sku: "BULK-D",
        name: "USB-C dock",
        stock: 3,
        status: "active",
        tutorial: "bulk-write",
      },
    },
  },
  {
    deleteOne: {
      filter: { sku: "BULK-C", tutorial: "bulk-write" },
    },
  },
]);

printjson({
  inserted: result.insertedCount,
  matched: result.matchedCount,
  modified: result.modifiedCount,
  deleted: result.deletedCount,
  upserted: result.upsertedCount,
});

products.find({ tutorial: "bulk-write" }).sort({ sku: 1 });

Expected result

The result reports one insert and one delete. The update and replacement contribute to matched and modified counts. The final cursor returns BULK-A, BULK-B, and BULK-D with their new values.

matchedCount and modifiedCount are different: an update may match a document but produce no physical change because the stored values already equal the requested values.

Add an upsert model

An individual update model can use upsert: true:

const syncResult = await products.bulkWrite([
  {
    updateOne: {
      filter: { sku: "BULK-E", tutorial: "bulk-write" },
      update: {
        $set: { name: "Synced headset", stock: 7, status: "active" },
        $setOnInsert: { tutorial: "bulk-write", createdAt: ISODate() },
      },
      upsert: true,
    },
  },
]);

printjson({
  upserted: syncResult.upsertedCount,
  ids: syncResult.upsertedIds,
});

The filter contributes equality values to the inserted document, while $setOnInsert runs only when the upsert creates a new record. A unique index is still necessary when multiple clients could race to create the same logical entity.

Choose ordered or unordered execution

Bulk writes are ordered by default. The driver stops attempting later models after a write error. This is easier to reason about when one operation depends on an earlier one.

With { ordered: false }, MongoDB can continue attempting independent models after an error:

try {
  await products.bulkWrite(
    [
      { insertOne: { document: { _id: "bulk-duplicate", sku: "DUP-1" } } },
      { insertOne: { document: { _id: "bulk-duplicate", sku: "DUP-2" } } },
      { updateOne: {
        filter: { sku: "BULK-A", tutorial: "bulk-write" },
        update: { $set: { checked: true } },
      } },
    ],
    { ordered: false },
  );
} catch (error) {
  print("Bulk write failed:", error.message);
  printjson(error.result ?? error.partialResult ?? {});
}

The duplicate _id produces an error, but the independent update can still be attempted. Inspect the error’s write errors and partial result instead of assuming every model failed or succeeded.

Collection and client bulk writes

The collection method targets one namespace and works across a broad range of supported server versions. Driver 7.5.0 also exposes client-level bulk write functionality for models spanning multiple namespaces, but that feature requires MongoDB Server 8.0 or later. Prefer the simpler collection API unless a genuine cross-namespace batch and compatible server justify the client form.

Neither form replaces validation, indexes, write concern, authorization, or transaction design. It changes how operations are submitted, not the correctness rules around them.

Practice exercise

Build a four-model bulk write that:

  1. Decrements stock for BULK-A only when stock is at least one.
  2. Marks BULK-B as featured.
  3. Upserts BULK-F with a creation timestamp.
  4. Deletes only tutorial products whose status is retired.

Print the result counts and run a sorted verification query. Repeat the script and explain why the second run’s matched, modified, and upserted counts differ from the first.

Common mistakes

  • Assuming a bulk write is automatically all-or-nothing across the full model list.
  • Using { ordered: false } when later operations depend on earlier results.
  • Ignoring the partial result after an exception and retrying successful models blindly.
  • Building an unbounded in-memory operation list for an import stream.
  • Replacing a document without carrying forward fields the application still requires.
  • Using client-level bulk writes without checking the MongoDB Server version requirement.

Continue learning

Use transactions for an all-or-nothing multi-document rule, or join referenced data with $lookup and relationship queries. The official driver documentation explains bulk operations and result types.