MongoDBinsertOne & insertMany Deep Dive

Insert

Inserting is how documents enter a collection. MongoDB gives you two current methods — insertOne() for a single document and insertMany() for a batch — plus bulkWrite() for mixing inserts with updates and deletes in one round-trip.

insertOne()

Inserting a single document

JS
const result = db.users.insertOne({
  name: "Alice",
  email: "alice@example.com",
  age: 28
})

result.acknowledged   // true
result.insertedId     // ObjectId("...") — auto-generated since we didn't supply _id

If the target collection doesn't exist yet, MongoDB creates it silently on the first insert — you never need a separate "create table" step.

insertMany()

Inserting multiple documents in one call

JS
const result = db.products.insertMany([
  { sku: "WIDGET-42", name: "Blue Widget", price: 9.99 },
  { sku: "GADGET-7",  name: "Red Gadget",  price: 24.5 },
  { sku: "GIZMO-1",   name: "Green Gizmo", price: 4.99 }
])

result.insertedCount   // 3
result.insertedIds     // { '0': ObjectId(...), '1': ObjectId(...), '2': ObjectId(...) }
Note
insertedIds is a map keyed by array index (as strings), not an array — access a specific one with result.insertedIds[0].
Generated _id Values

Omit _id and MongoDB generates an ObjectId for you automatically before the write. Supply your own value — a string, number, or embedded document — and MongoDB uses it as-is, as long as it's unique in the collection.

Custom vs generated _id

JS
db.users.insertOne({ name: "Bob" })                          // auto _id
db.countries.insertOne({ _id: "CA", name: "Canada" })         // custom string _id
db.inventory.insertOne({ _id: { wh: "YYZ", sku: "W-1" }, qty: 5 })  // compound _id
Ordered vs Unordered Inserts

By default, insertMany() is ordered: MongoDB inserts documents in array order and stops at the first error. Pass { ordered: false } to keep going and collect every error at the end.

Mode

Behavior

When to Use

ordered: true (default)

Stops at the first failed document; later documents in the array are never attempted.

When later documents logically depend on earlier ones succeeding.

ordered: false

Attempts every document regardless of earlier failures; errors are collected into one BulkWriteError.

Bulk imports where a few duplicate/invalid rows shouldn't block the rest.

Unordered insert with a duplicate key

JS
try {
  db.users.insertMany(
    [
      { _id: 1, name: "A" },
      { _id: 1, name: "B" },   // duplicate _id — fails
      { _id: 2, name: "C" }
    ],
    { ordered: false }
  )
} catch (e) {
  console.log(e.writeErrors.length)   // 1
  console.log(e.result.insertedCount) // 2 — documents 1 and 3 still went in
}
Write Concern

Write concern controls how many replica set members must acknowledge a write before the driver considers it successful. It's a durability-vs-latency dial.

Setting write concern per insert

JS
db.orders.insertOne(
  { item: "Widget", qty: 10 },
  { writeConcern: { w: "majority", wtimeout: 5000 } }
)
Duplicate Key Errors

Handling E11000

JS
try {
  db.users.insertOne({ _id: "alice@example.com", name: "Alice" })
  db.users.insertOne({ _id: "alice@example.com", name: "Alice Again" })
} catch (e) {
  if (e.code === 11000) {
    console.error("Duplicate key:", e.keyValue)
    // { _id: 'alice@example.com' }
  }
}
Warning
A duplicate key error can come from any unique index, not just _id. Check e.keyPattern to see which index triggered it before assuming it's always the primary key.
Insert Performance Tips
  • Prefer insertMany() over a loop of insertOne() calls — batching cuts network round-trips dramatically.

  • Batch large imports into chunks of roughly 1,000–5,000 documents per call rather than one enormous array.

  • Use { ordered: false } for bulk imports where a handful of bad rows shouldn't stop the rest.

  • Avoid unnecessary secondary indexes on a collection during a huge bulk load — each index adds write overhead per insert; build large indexes after the load when practical.

  • Use bulkWrite() when a single logical operation needs a mix of inserts, updates, and deletes atomically batched together.

Example Session
test> use shop
switched to db shop

test> db.products.insertOne({ name: "Keyboard", price: 79.99 })
{
  acknowledged: true,
  insertedId: ObjectId('66a1c2d3e4f5a6b7c8d9e0f1')
}

test> db.products.insertMany([
...   { name: "Mouse", price: 29.99 },
...   { name: "Monitor", price: 349.99 }
... ])
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId('66a1c2d3e4f5a6b7c8d9e0f2'),
    '1': ObjectId('66a1c2d3e4f5a6b7c8d9e0f3')
  }
}

test> db.products.countDocuments()
3
Tip
Run db.collection.insertOne() against a collection that doesn't exist yet in a throwaway database (e.g. use scratch) to sanity-check syntax without touching real data.