Inserting Documents: A Detailed Explanation of `insertOne` and `insertMany`

Inserting documents is the first step in writing data to MongoDB—mastering insertOne and insertMany is the foundation of data manipulation.

This course provides an in-depth look at various methods for inserting documents, error handling, performance optimization, and the Write Concern mechanism.

1. What You'll Learn


2. A Data Engineer’s True Story

(1) Pain Point: Batch imports of 1 million records often fail

Alice is a data engineer at an e-commerce company who needs to migrate 1 million product records from MySQL to MongoDB:

"I used insertMany to import 1 million product records, but the entire import failed at the 500,000th record due to a duplicate _id, wasting four hours. It’s either all or nothing—this is a disaster for incremental migration."

The problems she faces:

Issue Impact
ordered: true (default) If one item in the batch fails, the entire batch fails
Lack of Write Concern Data loss due to server failure
_id conflict Failure due to duplicate auto-increment IDs in MySQL
Bulk Insertion 1 million individual insertions took 1 hour

(2) A Solution Using MongoDB and bulkWrite

JAVASCRIPT
// === Usage bulkWrite + ordered: false Resolving Partial Failure Issues ===
const products = [...];  // 100,000 product records

const BATCH_SIZE = 1000;
for (let i = 0; i < products.length; i += BATCH_SIZE) {
  const batch = products.slice(i, i + BATCH_SIZE);

  try {
    await Product.bulkWrite(
      batch.map(doc => ({
        insertOne: { document: doc }
      })),
      { ordered: false }  // Allow for Partial Failure,Continue execution
    );
  } catch (err) {
    console.error(`Batch ${i / BATCH_SIZE} Failure:${err.writeErrors?.length} items`);
  }
}

// === Usage Write Concern Ensure Data Persistence ===
await Product.bulkWrite(
  batch.map(doc => ({ insertOne: { document: doc } })),
  {
    ordered: false,
    writeConcern: { w: 'majority', j: true, wtimeout: 5000 }
  }
);

(3) Revenue

Dimension Single-row insertion Bulk insertion + ordered: false
Performance 1 million records ~60 minutes 1 million records ~3 minutes
Fault Tolerance Single failure results in loss Partial failure allows continuation
Data Security Prone to loss Write Concern Persistence
Code Complexity Simple Moderate

3. insertOne: Insert a Single Document

Concept Explanation: insertOne is the most basic write method in MongoDB, used to insert a document into a collection. Each document in MongoDB is stored in BSON format and is automatically assigned a unique _id primary key. Unlike INSERT INTO in relational databases, insertOne does not require a predefined table structure, and documents can contain any combination of fields.

How It Works: When a client initiates a insertOne request, the MongoDB server performs the following steps: validates the BSON format → checks the uniqueness of _id → writes to the WiredTiger storage engine → applies the Write Concern → returns insertedId. The entire write process is atomic for a single document.

100%
sequenceDiagram
    participant App as Applications
    participant Mongod as MongoDB Server-side
    participant WT as WiredTiger Engine

    App->>Mongod: insertOne({ doc })
    Mongod->>Mongod: Verification BSON Format
    Mongod->>Mongod: Inspection _id Unique Index
    alt _id Conflict
        Mongod-->>App: E11000 duplicate key error
    else _id The Only One
        Mongod->>WT: Write to the document + Update Index
        WT-->>Mongod: Confirm Write
        Mongod-->>App: { acknowledged: true, insertedId }
    end
Parameter Type Description
document Document Document to insert (required)
writeConcern Document Write Confirmation Level (Optional)
Applicable Scenarios Non-Applicable Scenarios
Creating a Single Record (User Registration) Bulk Data Import
Need to retrieve insertedId Bulk write of more than 1,000 records
Documents with Complex Nesting Import with Duplicate Data Removal

(1) Basic Syntax

JAVASCRIPT
// === insertOne Basic Usage ===
db.products.insertOne({
  sku: "PHONE-001",
  title: "Smartphone X",
  price: NumberDecimal("599.99"),
  category: "Electronics",
  stock: 50,
  createdAt: new Date()
});

// Return Results:
// {
//   acknowledged: true,
//   insertedId: ObjectId('507f1f77bcf86cd799439011')
// }

Key Points Analysis:

  1. acknowledged: true indicates that the write operation has been acknowledged by the MongoDB server (subject to Write Concern)
  2. If writeConcern: { w: 0 }, then acknowledged is false, and insertedId is not returned.
  3. The value of insertedId depends on whether _id is specified manually—if not specified, an ObjectId is automatically generated.

(2) Analysis of Return Values

Concept Explanation: The return value of insertOne contains two key fields—acknowledged and insertedId. If acknowledged is true, it indicates that the write operation has been acknowledged by the MongoDB server (subject to Write Concern; if w: 0, then it is false). .insertedId is the _id value of the inserted document; it is returned regardless of whether _id is automatically generated or manually specified.

Return Field Type Description Notes
acknowledged Boolean Whether the write has been acknowledged false when w: 0
insertedId ObjectId/Any The _id of the inserted document Returns the specified value when specified manually
JAVASCRIPT
const result = db.products.insertOne({
  sku: "TEST-001",
  title: "Test Product"
});

print(result.acknowledged);   // true(Write confirmed)
print(result.insertedId);      // ObjectId('507f1f77bcf86cd799439012')
Field Type Description
acknowledged boolean true indicates that the write has been confirmed
insertedId ObjectId The _id of the inserted document

(3) _id is automatically generated

Concept Explanation: _id is the primary key of a MongoDB document, with a default type of ObjectId (12-byte binary). If _id is not specified manually, the MongoDB driver automatically generates it on the client side, ensuring it is unique before being written to the server. This design differs from MySQL’s auto-increment IDs—ObjectId does not rely on a centralized counter and inherently supports distributed environments.

How It Works: An ObjectId consists of a 4-byte timestamp + a 5-byte random value (machine + process) + a 3-byte incrementing counter. The timestamp portion ensures that ObjectIds are naturally sorted by insertion time; the random value guarantees uniqueness across processes; and the counter ensures uniqueness within a single second.

100%
graph LR
    A[Client-Side Generation ObjectId] --> B[Timestamp 4B<br/>Insertion Time]
    A --> C[Random value 5B<br/>Machine+Unique Process]
    A --> D[Counter 3B<br/>Increment within the same second]
    B --> E[Globally Unique<br/>Naturally Ordered<br/>Withdrawal Time]
_id Strategy Example Applicable Scenarios Ordering
Auto ObjectId ObjectId("...") General Scenarios (Default) ✅ Sort by Time
String Business Key "ORDER-2026-001" Order Number, SKU Depends on format
Auto-increment NumberInt(1) Legacy System Migration ✅ Sort by Number
Timestamp NumberLong(1700000000) Time-series data ✅ Sorted by time
UUID UUID("...") Cross-system unique ❌ Unordered
JAVASCRIPT
// === Not specified _id(Automatically Generated ObjectId)===
db.users.insertOne({
  name: "Alice",
  email: "alice@example.com"
});
// Automatically Generated _id: ObjectId('507f1f77bcf86cd799439011')

// === Specify manually _id ===
db.users.insertOne({
  _id: "user_001",       // String ID
  name: "Alice"
});

db.users.insertOne({
  _id: ObjectId(),       // Generated Manually ObjectId
  name: "Bob"
});

db.users.insertOne({
  _id: NumberLong(1700000000000),  // Timestamps as ID
  name: "Charlie"
});

(4) _id Uniqueness

Concept Explanation: MongoDB automatically creates a unique index on the _id field of each collection, which serves as the fundamental guarantee of data integrity. The unique feature of the _id unique index is that it cannot be deleted—even if dropIndexes() is executed, the _id index remains. When a document with a duplicate _id is inserted, MongoDB throws a E11000 duplicate key error error, and the entire insertion operation is rolled back.

Use Cases: In data migration and bulk import scenarios, _id conflicts are the most common source of errors. Understanding how to prevent and resolve these conflicts is key to ensuring stable operation in production environments. The recommended prevention strategy is to query the existing set of _id before importing, or to use the upsert schema as an alternative to insertOne.

Scenario Cause of Conflict Recommended Strategy
MySQL → MongoDB Migration Auto-increment IDs Conflicting with Existing Data Remove the old _id and let MongoDB generate it
Merging Data from Multiple Sources Different data sources have the same business key Add prefix: sourceA_ORDER-001
Incremental Sync Source data already exists in the destination updateOne + upsert
Bulk Import CSV/JSON with duplicate rows ordered: false Skip duplicates
JAVASCRIPT
// === _id Handling Repeated Errors ===
try {
  db.users.insertOne({
    _id: "user_001",     // Already exists
    name: "Alice Duplicate"
  });
} catch (err) {
  // E11000 duplicate key error collection: shopdb.users index: _id_
  print("❌ _id Already exists:" + err.message);
}

// === Usage upsert Handling Duplicates ===
db.users.updateOne(
  { _id: "user_001" },
  { $set: { name: "Alice Updated" } },
  { upsert: true }       // If it doesn't exist, insert it,If it exists, update it
);

▶ Example 1: Complete usage of insertOne

JAVASCRIPT
// === Inserting Different Types of Fields ===
db.products.insertOne({
  // String
  sku: "PHONE-X-256-BLK",
  title: "Smartphone X 256GB Black",

  // Numeric Types
  price: NumberDecimal("599.99"),       // Decimal128(Accurate)
  stock: NumberInt(50),                  // Int32
  viewCount: NumberLong(1000000),         // Long

  // Boolean
  isActive: true,
  isFeatured: false,

  // Date
  createdAt: new Date(),
  releaseDate: ISODate("2026-01-01"),

  // Array
  tags: ["5g", "amoled", "fast-charging"],
  colors: ["Black", "White", "Blue"],

  // Nested Documents
  specs: {
    screen: "6.5 inch OLED",
    battery: "4500mAh",
    camera: "108MP"
  },

  // Binary
  thumbnail: BinData(0, "iVBORw0KGgoAAAANSUhEUgAA..."),

  // Null
  discount: null
});

▶ Example 2: insertOne with different _id strategies

JAVASCRIPT
// === Strategy 1:Automatic ObjectId(Default)===
const r1 = db.users.insertOne({ name: "Alice", email: "alice@example.com" });
print(`Auto ObjectId: ${r1.insertedId}`);

// === Strategy 2:String Business Key ===
const r2 = db.orders.insertOne({
  _id: "ORD-20260701-0001",
  total: NumberDecimal("599.99"),
  status: "pending"
});
print(`Business key: ${r2.insertedId}`);

// === Strategy 3:Nested Documents + Array ===
const r3 = db.products.insertOne({
  _id: ObjectId(),
  sku: "PHONE-X-256-BLK",
  specs: { screen: "6.5 inch OLED", battery: "4500mAh" },
  tags: ["5g", "amoled"],
  price: NumberDecimal("599.99")
});
print(`Nested doc: ${r3.insertedId}`);

Output: Auto ObjectId: ObjectId('...') | Business key: ORD-20260701-0001 | Nested doc: ObjectId('...')


4. insertMany: Batch Insertion

Concept Description: insertMany—inserting multiple documents in a single operation—is the core method for batch data writing. Compared to the insertOne method, which processes documents one by one, insertMany combines multiple documents into a single network request sent to the server, significantly reducing network round-trip overhead and improving performance by 10 to 100 times.

How It Works: insertMany receives an array of documents and determines the execution strategy based on the ordered option. ordered: true (default) inserts items sequentially one by one and stops immediately upon encountering an error; ordered: false allows parallel insertion and skips failed items to continue execution. These two strategies have a significant impact on performance and data integrity.

100%
graph TB
    A[insertMany<br/>1000 Documents] --> B{ordered option}
    B -->|ordered: true| C[Sequential Insertion<br/>Item 1 -> Item 2 -> ...<br/>Stop on Error]
    B -->|ordered: false| D[Parallel Insertion<br/>Writing Multiple Rows Simultaneously<br/>Skip failed items]
    
    C --> C1[Performance:Intermediate<br/>Consistency:Strong]
    D --> D1[Performance:Higher<br/>Consistency:Weak]

    style D fill:#d4edda
Parameter Type Description
documents Array Array of documents (required, at least 1 entry)
ordered Boolean true Sequential execution (default), false Parallel execution
writeConcern Document Write Confirmation Level
Applicable Scenarios Non-Applicable Scenarios
Data Migration, Bulk Import Inserting a Single Document
Test Data Generation Writes Requiring Strict Transaction Ordering
Batch Log Writing Strong Dependencies Between Documents

(1) Basic Syntax

JAVASCRIPT
// === insertMany Basic Usage ===
db.products.insertMany([
  { sku: "PHONE-001", title: "Phone A", price: 599.99 },
  { sku: "PHONE-002", title: "Phone B", price: 699.99 },
  { sku: "PHONE-003", title: "Phone C", price: 799.99 }
]);

// Return Results:
// {
//   acknowledged: true,
//   insertedIds: {
//     '0': ObjectId('507f1f77bcf86cd799439011'),
//     '1': ObjectId('507f1f77bcf86cd799439012'),
//     '2': ObjectId('507f1f77bcf86cd799439013')
//   },
//   insertedCount: 3
// }

(2) The "ordered" option (crucial!)

Concept Explanation: ordered is the most critical option for insertMany. It determines how MongoDB handles errors during batch writes—whether to abort immediately or to skip the error and continue. Understanding ordered is crucial for data import in production environments.

Use Cases: For data migration and incremental synchronization scenarios, ordered: false is recommended because the source data may contain duplicates _id; skipping duplicates and continuing the import is more reasonable than having the entire batch fail. For financial transaction scenarios, ordered: true is recommended to ensure strict operational sequencing.

JAVASCRIPT
// === ordered: true(Default)— If it fails in the middle, stop ===
db.products.insertMany([
  { _id: 1, sku: "A" },
  { _id: 2, sku: "B" },
  { _id: 1, sku: "C" },    // ❌ _id Conflict
  { _id: 4, sku: "D" }     // ⚠️ Will not be inserted(Previous failure)
]);
// Error:E11000 duplicate key error
// Actual insertion:A, B(2 items),C and D Not inserted

// === ordered: false — Skip failure,Continue execution ===
db.products.insertMany([
  { _id: 1, sku: "A" },
  { _id: 2, sku: "B" },
  { _id: 1, sku: "C" },    // ❌ _id Conflict
  { _id: 4, sku: "D" }     // ✅ Still inserted
], { ordered: false });

// The error message lists all documents that failed to be indexed:
// BulkWriteError: 1 document(s) failed
// writeErrors: [
//   { index: 2, code: 11000, errmsg: 'duplicate key' }
// ]
// Actual insertion:A, B, D(3 items),C Not inserted

(3) Comparison of the "ordered" options

Dimension ordered: true ordered: false
Failure Type Entire batch failed Skip the failure and continue
Performance Moderate Faster (parallel)
Use Cases Strong consistency (e.g., fund transfers) Incremental import, logs
Error Message First item failed Details of all failures

▶ Example 3: A Complete Guide to Batch Insertion

JAVASCRIPT
// === Example of Batch Importing E-commerce Products ===
const products = [
  { sku: "LAPTOP-001", title: "Laptop Pro", price: NumberDecimal("1299.99"), category: "Electronics", stock: 20 },
  { sku: "LAPTOP-002", title: "Laptop Air", price: NumberDecimal("999.99"), category: "Electronics", stock: 30 },
  { sku: "PHONE-001", title: "Smartphone X", price: NumberDecimal("599.99"), category: "Electronics", stock: 50 },
  { sku: "BOOK-001", title: "JavaScript Guide", price: NumberDecimal("29.99"), category: "Books", stock: 200 },
  { sku: "BOOK-002", title: "MongoDB Tutorial", price: NumberDecimal("34.99"), category: "Books", stock: 150 }
];

// === Default Mode(Orderly)===
try {
  const result = db.products.insertMany(products);
  print(`✅ Insert ${result.insertedCount} Items`);
} catch (err) {
  print(`❌ Batch Failure:${err.message}`);
}

// === Fault-Tolerant Mode(Disorder)===
try {
  const result = db.products.insertMany(products, { ordered: false });
  print(`✅ Insert ${result.insertedCount} Items`);
} catch (err) {
  print(`⚠️ Partial failure:Success ${err.result.insertedCount} items,Failure ${err.writeErrors.length} items`);
  err.writeErrors.forEach(e => print(`  Index of Failures ${e.index}: ${e.errmsg}`));
}

5. Write Concern

Concept Explanation: Write Concern is a write safety mechanism in MongoDB that defines "when a write operation is considered successful." It controls the number of replica nodes that must acknowledge the write operation before a response is returned to the client. This represents a core trade-off between data durability and write performance—the higher the confirmation level, the greater the safety, but the greater the latency.

How It Works: In a replica set architecture, write operations first arrive at the Primary node and are then asynchronously replicated to the Secondary nodes. The w parameter of Write Concern determines how many node confirmations must be awaited. w: 1 waits only for the Primary node to confirm (fastest but carries a risk of data loss), w: "majority" waits for a majority of nodes to confirm (recommended for production environments), and j: true ensures that the data has been written to the disk journal.

100%
sequenceDiagram
    participant App as Client
    participant P as Primary
    participant S1 as Secondary 1
    participant S2 as Secondary 2

    App->>P: insertOne({ doc }, { w: "majority" })
    P->>P: Write to memory + Journal
    P->>S1: Copy oplog
    P->>S2: Copy oplog
    S1-->>P: Confirm Write
    S2-->>P: Confirm Write
    Note over P: majority Reached(2/3 Node)
    P-->>App: { acknowledged: true }
Dimension w: 0 w: 1 w: majority w: majority + j: true
Number of Confirmation Nodes No Wait Primary Majority of Nodes Majority of Nodes + Disk
Performance Fastest Fast Average Slow
Data Security Possible loss Data may be lost if the primary server goes down Virtually no loss Most secure
Recommended Scenarios Logging Development Production Finance

(1) What is Write Concern?

Write Concern describes the level of confirmation required for a write operation to be considered successful and determines when data is considered "saved."

100%
graph LR
    A[Client] -->|insertOne| B[mongod Receive]
    B --> C{Write Concern Layout}
    C -->|w: 1| D[Primary Write and Return]
    C -->|w: majority| E[After most nodes have confirmed, return]
    C -->|j: true| F[Returns only after writing to disk]

    style E fill:#d4edda
    style F fill:#d4edda

(2) Write Concern Parameters

Parameter Value Description
w 0 / 1 / "majority" / Number Number of nodes with write confirmation
j true / false Write journal to disk
wtimeout Number of milliseconds Timeout (default: wait indefinitely)

(3) Comparison of Write Concern Levels

JAVASCRIPT
// === w: 0 — Do not wait for confirmation(Fastest,May be missing)===
db.products.insertOne(
  { sku: "TEST-001", title: "Test" },
  { writeConcern: { w: 0 } }
);
// Return Now,Write success is not guaranteed

// === w: 1 — Primary Node Confirmation(Default)===
db.products.insertOne(
  { sku: "TEST-002", title: "Test" },
  { writeConcern: { w: 1 } }
);
// Primary Write and Return

// === w: "majority" — Confirmed by a majority of nodes(Safest)===
db.products.insertOne(
  { sku: "TEST-003", title: "Test" },
  { writeConcern: { w: "majority", j: true, wtimeout: 5000 } }
);
// The replica waits until most nodes have written to disk before returning(Recommended Production Environment)

(4) Comparison of Write Concern Configurations

Level Performance Data Security Use Cases
w: 0 ⚡⚡⚡ Extremely fast ❌ Prone to loss Logs, temporary data
w: 1 ⚡⚡ Fast ⚠️ May be lost Standalone development
w: majority ⚡ Medium ✅ Virtually no loss Recommended for production environments
w: majority, j: true ⚠️ Slower ✅✅ Safest Finance, critical data

▶ Example 4: Production-Level Write Concern Configuration

JAVASCRIPT
// === Cluster-Level Settings(Recommendations)===
db.adminCommand({
  setDefaultRWConcern: 1,
  defaultWriteConcern: { w: "majority", j: true, wtimeout: 10000 },
  defaultReadConcern: { level: "majority" }
});

// === Single-Write Specification ===
db.orders.insertOne(
  { userId: "user_001", total: 599.99, items: [...] },
  { writeConcern: { w: "majority", j: true, wtimeout: 5000 } }
);

// === mongoose Settings ===
const OrderSchema = new mongoose.Schema({
  userId: String,
  total: mongoose.Schema.Types.Decimal128,
  items: Array
}, {
  writeConcern: { w: 'majority', j: true, wtimeout: 5000 }
});

6. _id Conflict Resolution Strategy

Concept Explanation: _id is the unique identifier for a MongoDB document, and the _id field in each collection automatically creates a unique index. When the _id of an inserted document matches that of an existing document, MongoDB throws a E11000 duplicate key error error. In scenarios such as data migration, bulk imports, and merging from multiple sources, _id conflicts are one of the most common issues.

How It Works: Before writing a document, MongoDB first checks whether the _id field violates the unique index constraint. If a conflict occurs, the entire write operation is rolled back (single-document atomicity), and error code 11000 is returned. Understanding the differences between various conflict resolution strategies is critical for data integrity in production environments.

100%
graph TB
    A[_id Conflict E11000] --> B[Strategy Selection]
    B --> C[Ignore duplicates<br/>ordered: false]
    B --> D[Overwrite the old value<br/>replaceOne + upsert]
    B --> E[Partial Update<br/>updateOne + upsert]
    B --> F[Regenerate _id<br/>Remove _id Field]
    B --> G[Retry Mechanism<br/>Application-Layer Retry]

    style E fill:#d4edda
Strategy Syntax Data Integrity Use Cases
Skip duplicates ordered: false Keep old data Incremental import, log
Overwrite old values replaceOne + upsert Replace with new data Full sync
Partial Update updateOne + upsert Merge Old and New Data Incremental Update Fields
Ignore _id Delete _id field Insert all (new _id) Import without duplicates
Retry Mechanism Application-Level Retry Depends on the New _id Temporary Conflict

(1) Error Symptoms

JAVASCRIPT
// === _id Repeated Mistakes ===
db.users.insertOne({ _id: 1, name: "Alice" });
// { acknowledged: true, insertedId: 1 }

db.users.insertOne({ _id: 1, name: "Bob Duplicate" });
// E11000 duplicate key error collection: shopdb.users index: _id_ dup key: { _id: 1 }

(2) 5 Management Strategies

100%
graph TB
    A[_id Conflict] --> B[Strategy 1<br/>Skip duplicates]
    A --> C[Strategy 2<br/>Overwrite the old value]
    A --> D[Strategy 3<br/>upsert Automatic Selection]
    A --> E[Strategy 4<br/>Ignore _id Field]
    A --> F[Strategy 5<br/>Retry Mechanism]

    style D fill:#d4edda

(3) Strategy Implementation

JAVASCRIPT
// === Strategy 1:Usage ordered: false Skip duplicates ===
try {
  db.users.insertMany(
    [{ _id: 1, name: "Alice" }, { _id: 2, name: "Bob" }, { _id: 1, name: "Dup" }],
    { ordered: false }
  );
} catch (err) {
  print(`Skip ${err.writeErrors.length} Duplicate entry`);
}

// === Strategy 2:Usage replaceOne Coverage ===
db.users.replaceOne(
  { _id: 1 },
  { _id: 1, name: "Alice Updated", updatedAt: new Date() },
  { upsert: true }
);

// === Strategy 3:Usage updateOne + upsert ===
db.users.updateOne(
  { _id: 1 },
  { $set: { name: "Alice", email: "alice@example.com" } },
  { upsert: true }   // If it doesn't exist, insert it,If it exists, update it
);

// === Strategy 4:When inserting, make sure to MongoDB Automatically Generated _id ===
const docs = externalData.map(d => {
  const { _id, ...rest } = d;   // Deconstruct and remove _id
  return rest;                   // let MongoDB automatically generate _id
});
db.users.insertMany(docs);

// === Strategy 5:Retry Mechanism(Application Layer)===
async function insertWithRetry(doc, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await db.collection('users').insertOne(doc);
    } catch (err) {
      if (err.code === 11000 && i < maxRetries - 1) {
        // Generate a new one _id Retry
        doc._id = new ObjectId();
        continue;
      }
      throw err;
    }
  }
}

▶ Example 5: Bulk Import + Duplicate Removal Strategy

JAVASCRIPT
// === Scene:Import CSV Data,Part _id Already exists ===
const csvData = [
  { _id: "USER-001", name: "Alice", email: "alice@example.com" },
  { _id: "USER-002", name: "Bob", email: "bob@example.com" },
  { _id: "USER-001", name: "Alice Duplicate", email: "alice2@example.com" },
  { _id: "USER-003", name: "Charlie", email: "charlie@example.com" }
];

// === Plan A:Ignore duplicates,Insert new data only ===
const insertedIds = [];
const duplicates = [];

csvData.forEach(doc => {
  try {
    const result = db.users.insertOne(doc);
    insertedIds.push(result.insertedId);
  } catch (err) {
    if (err.code === 11000) {
      duplicates.push(doc._id);
    } else {
      throw err;
    }
  }
});

print(`✅ Insert ${insertedIds.length} new data entries`);
print(`⚠️ Skip ${duplicates.length} Duplicate entry:${duplicates.join(', ')}`);

// === Plan B:Duplicate Coverage,Update existing data ===
db.users.bulkWrite(
  csvData.map(doc => ({
    replaceOne: {
      filter: { _id: doc._id },
      replacement: doc,
      upsert: true
    }
  })),
  { ordered: false }
);

7. Performance Optimization for Bulk Inserts

Concept Explanation: Performance bottlenecks in bulk inserts primarily stem from three areas: network round-trip overhead, index update overhead, and disk I/O overhead. By understanding these three bottlenecks and optimizing them one by one, you can reduce the time required to import 1 million records from 60 minutes to less than 3 minutes.

How It Works: Processing documents one by one insertOne triggers one network request, one index update, and one disk write each time. insertMany combines multiple documents into a single network request, reducing overhead by two-thirds. bulkWrite Further supports mixed operation types (insert+update+delete), completing them in a single request. Temporarily removing unnecessary indexes before import can further improve performance by 5 to 10 times.

100%
graph LR
    A[100,000 data entries] --> B[item by item insertOne<br/>~60 minutes<br/>100,000 web requests]
    A --> C[insertMany 1000/batch<br/>~5 minutes<br/>1000 network requests]
    A --> D[bulkWrite + dropIndexes<br/>~3 minutes<br/>1000 requests + Updates Without Indexes]

    style D fill:#d4edda
Optimization Strategies Performance Improvements Risks Recommended Scenarios
insertMany Replaces insertOne 10–100x None All batch writes
ordered: false 1.5–3x May skip failed entries Fault-tolerant import
Temporarily delete indexes 5–10x Must be rebuilt after import Initial import
bulkWrite Replaces insertMany 1.2–1.5x None Mixed operation
w: 0 (No wait for confirmation) 2–5x Data may be lost Temporary data, logs

(1) Performance Comparison

100%
graph LR
    A[Insert 100,000 data entries] --> B[Insert one by one<br/>~60 minutes]
    A --> C[insertMany 1000/batch<br/>~5 minutes]
    A --> D[bulkWrite 1000/batch<br/>~3 minutes]

    style D fill:#d4edda

(2) Optimization Strategies

JAVASCRIPT
// === Optimization 1:Reasonable Batch Size ===
const BATCH_SIZE = 1000;  // Recommendations 500-5000

for (let i = 0; i < data.length; i += BATCH_SIZE) {
  const batch = data.slice(i, i + BATCH_SIZE);
  db.collection.insertMany(batch, { ordered: false });
}

// === Optimization 2:Usage bulkWrite Replace insertMany ===
await Collection.bulkWrite(
  data.map(doc => ({ insertOne: { document: doc } })),
  { ordered: false }
);

// === Optimization 3:Disable Index(During the import)===
// ⚠️ Use with caution:After the import is complete, remember to rebuild the indexes.
db.products.dropIndexes();
db.products.insertMany(data);
// Rebuild Index
db.products.createIndex({ sku: 1 }, { unique: true });

// === Optimization 4:Usage Write Concern 0(Extremely fast but unsafe)===
db.products.insertMany(data, { writeConcern: { w: 0 } });
// ⚠️ For temporary data only,Not recommended for production

// === Optimization 5:Usage mongoose bulkWrite ===
const result = await Product.bulkWrite(
  data.map(doc => ({
    insertOne: { document: doc }
  })),
  { ordered: false }
);

(3) Batch Size Selection

Data Size Recommended Batch Size Reason
< 100 KB 1,000–5,000 Low network overhead
100 KB - 1 MB 500–2000 Balance throughput and latency
> 1 MB 100–500 Avoid excessively large single requests
Very large document (nearly 16 MB) 1-10 The document itself is large

▶ Example 6: High-Performance Data Import Script

JAVASCRIPT
// === Import 100 Product Data for 10,000 Items(Optimized Version)===
const fs = require('fs');
const readline = require('readline');
const { MongoClient } = require('mongodb');

async function importLargeDataset() {
  const client = new MongoClient('mongodb://localhost:27017');
  await client.connect();
  const collection = client.db('shopdb').collection('products');

  // 1. Temporarily Delete an Index(Import Speed ↑5x)
  await collection.dropIndexes().catch(() => {});
  await collection.createIndex({ sku: 1 }, { unique: true }); // Preserve the unique index(Duplicate Prevention)

  // 2. Streaming Read CSV
  const fileStream = fs.createReadStream('products.csv');
  const rl = readline.createInterface({ input: fileStream });

  let buffer = [];
  const BATCH_SIZE = 2000;

  for await (const line of rl) {
    const [sku, title, price, category] = line.split(',');
    buffer.push({
      sku,
      title,
      price: price ? NumberDecimal(price) : null,
      category,
      createdAt: new Date()
    });

    if (buffer.length >= BATCH_SIZE) {
      try {
        await collection.insertMany(buffer, { ordered: false });
      } catch (err) {
        if (err.writeErrors) {
          console.warn(`⚠️ Skip ${err.writeErrors.length} Duplicate entry`);
        }
      }
      buffer = [];
    }
  }

  // 3. Insert the remaining data
  if (buffer.length > 0) {
    await collection.insertMany(buffer, { ordered: false });
  }

  // 4. Rebuild Index
  await collection.createIndex({ category: 1, price: 1 });
  await collection.createIndex({ title: 'text' });

  console.log(`✅ Import Complete`);
  await client.close();
}

importLargeDataset().catch(console.error);

8. Special Types of Insertions

Concept Explanation: MongoDB’s BSON format supports a much wider range of data types than JSON. When inserting documents, using these special types correctly is key to avoiding data precision loss and type errors. The three most common precision issues are: (1) JavaScript’s Number is a double-precision floating-point number, 0.1 + 0.2 ≠ 0.3; (2) JSON does not have a date type, so new Date() is converted to a string JSON.stringify(); (3) JSON does not support binary data, so images and files cannot be stored directly.

How It Works: Before sending an insert request, the MongoDB driver (including mongosh and the Node.js driver) first serializes the JavaScript object into BSON. During this process, the Date object is serialized as a BSON Date type (64-bit millisecond timestamp), NumberDecimal() is serialized as Decimal128 (128-bit high precision), and Buffer is serialized as BSON Binary. Understanding this serialization process is key to using these special types correctly.

100%
graph TB
    A[JavaScript Object] --> B[Driver Serialization]
    B --> C{Field Type Determination}
    C -->|Date Object| D[BSON Date<br/>64-bit Millisecond timestamp]
    C -->|NumberDecimal| E[BSON Decimal128<br/>128-bit High precision]
    C -->|Number Constants| F[BSON Double<br/>64-bit Floating-point]
    C -->|Buffer / BinData| G[BSON Binary<br/>Subtype + Byte Stream]
    C -->|ObjectId| H[BSON ObjectId<br/>12 Byte]
    C -->|null| I[BSON Null]
    
    style E fill:#d4edda
    style D fill:#d4edda
Type Syntax Precision/Range Typical Scenarios
Date new Date() / ISODate("...") Millisecond precision Timestamp, validity period
Decimal128 NumberDecimal("0.30") 34-digit decimal Amount, precise calculation
Int32 NumberInt(123) -2^31 ~ 2^31-1 Counting, Inventory
Long NumberLong(1700000000) -2^63 ~ 2^63-1 Timestamp ID, large integer
BinData BinData(0, "base64...") Any binary file Images, PDFs
ObjectId ObjectId() / new ObjectId() 12 bytes Document reference, primary key

(1) Insert Date

JAVASCRIPT
// === Current Time ===
db.logs.insertOne({ event: "login", timestamp: new Date() });

// === Specified time ===
db.logs.insertOne({
  event: "signup",
  timestamp: ISODate("2026-07-01T10:30:00Z")
});

// === Create from a string ===
db.logs.insertOne({
  event: "purchase",
  timestamp: new Date("2026-07-01")
});

(2) Insert ObjectId

JAVASCRIPT
// === Automatically Generated ===
db.users.insertOne({ name: "Alice" });

// === Create Manually ===
db.users.insertOne({
  _id: new ObjectId(),
  name: "Bob"
});

// === Create from 24-digit hex string ===
db.users.insertOne({
  _id: ObjectId("507f1f77bcf86cd799439011"),
  name: "Charlie"
});

// === Created from a timestamp(Used for range queries)===
const startOfDay = ObjectId.createFromTime(
  Math.floor(new Date('2026-07-01').getTime() / 1000)
);
db.orders.insertOne({
  _id: startOfDay,
  total: 999.99
});

(3) Inserting Nested Documents

JAVASCRIPT
// === Nested Objects ===
db.products.insertOne({
  sku: "PHONE-001",
  specs: {
    screen: { size: "6.5", type: "OLED" },
    battery: { capacity: "4500mAh", type: "Li-Po" }
  }
});

// === Array ===
db.products.insertOne({
  sku: "SHIRT-001",
  sizes: ["S", "M", "L", "XL"],
  colors: [
    { name: "Red", hex: "#FF0000" },
    { name: "Blue", hex: "#0000FF" }
  ]
});

▶ Example 7: Inserting a Composite Type

JAVASCRIPT
// === Order Documentation(Includes all special types)===
db.orders.insertOne({
  _id: ObjectId(),
  orderNumber: "ORD-20260701-0001",

  // String + Value
  userId: "user_001",
  total: NumberDecimal("1299.99"),
  tax: NumberDecimal("130.00"),

  // Array + Nested
  items: [
    { sku: "LAPTOP-001", qty: 1, price: NumberDecimal("1299.99") },
    { sku: "MOUSE-001", qty: 2, price: NumberDecimal("29.99") }
  ],

  // Status
  status: "pending",
  isPaid: false,

  // Date
  createdAt: new Date(),
  expectedDelivery: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days

  // Binary (PDF receipt)
  receiptPdf: BinData(0, "JVBERi0xLjQKJ..."),

  // Quote
  shippingAddressId: ObjectId("507f1f77bcf86cd799439011"),

  // Metadata
  metadata: {
    userAgent: "Mozilla/5.0...",
    ipAddress: "192.168.1.1"
  }
});

9. Troubleshooting Common Insertion Errors

Concept Explanation: Insert operations may fail for a variety of reasons—_id conflict (E11000), document validation failure (121), BSON document too large (16755), or invalid field name (2). Understanding error codes and handling strategies is essential for ensuring stable operation in a production environment. The basic principle of troubleshooting is: first check the error code → identify the cause of the error → select a handling strategy.

How It Works: MongoDB performs multi-layer validation before writing a document: BSON format validation → field name validation (no names starting with $, no .) → _id unique index validation → schema validation → document size validation (16MB) → Nested depth validation (100 levels). Failure at any level will prevent the write and return the corresponding error code.

Debugging Tips:

  1. Enable detailed logging: db.adminCommand({ setParameter: 1, logComponentVerbosity: { write: { verbosity: 2 } } })
  2. View the slow query log: db.system.profile.find().sort({ ts: -1 }).limit(5)
  3. Check document size: BSON.calculateObjectSize(doc) Returns the number of bytes
  4. Check Nesting Depth: Custom getDepth() Function

Production Environment Monitoring:

100%
graph TB
    A[insertOne Request] --> B{BSON Format Validation}
    B -->|Failure| B1[Error Code 2<br/>Invalid field name]
    B -->|Through| C{_id Single-Check Verification}
    C -->|Conflict| C1[Error Code 11000<br/>Duplicate Keys]
    C -->|Through| D{Schema Verification}
    D -->|Failure| D1[Error Code 121<br/>Verification Failed]
    D -->|Through| E{Document Size Verification}
    E -->|More than16MB| E1[Error Code 16755<br/>The document is too large]
    E -->|Through| F[Write successful ✅]

    style F fill:#d4edda
    style C1 fill:#f8d7da
    style D1 fill:#f8d7da
Error Code Meaning Root Cause Resolution Strategy
11000 _id is a duplicate A document with the same _id already exists Use upsert or ordered: false
121 Document validation failed Field value does not comply with schema rules Check schema validation rules
2 Field name error Field name starts with $ or contains . Rename the field
16755 BSON document too large Document exceeds the 16 MB limit Split the document or use GridFS
14 Write Concern Timeout Replica node response timeout Increase wtimeout or simplify w
50 Exceeds maximum BSON depth More than 100 levels of nesting Reduce the number of nesting levels

(1) Error Code Reference Table

Error Code Meaning Solution
11000 _id duplicate Use upsert or ordered: false
121 Document validation failed Check the schema validation rules
2 Invalid field name (e.g., starting with $) Rename the field
16755 BSON document is too large (>16MB) Split the document or use GridFS
14 Write Concern Timeout Increase wtimeout or simplify w
50 Exceeds max BSON depth Reduce nesting levels

(2) Debugging Tips

JAVASCRIPT
// === Enable detailed logging ===
db.adminCommand({ setParameter: 1, logComponentVerbosity: { write: { verbosity: 2 } } });

// === View the slow query log ===
db.system.profile.find().sort({ ts: -1 }).limit(5);

// === Check the document size ===
const doc = { /* your document */ };
print(`Document Size:${BSON.calculateObjectSize(doc)} bytes`);
print(`Nesting Depth:${getDepth(doc)}`);

(3) Performance Monitoring

JAVASCRIPT
// === View Current Database Operations ===
db.currentOp({ "op": "insert" });

// === Monitoring Write Performance ===
db.serverStatus().opcounters;
// {
//   insert: 12345,
//   query: 67890,
//   update: 2345,
//   delete: 100,
//   ...
// }

// === View Write Latency ===
db.serverStatus().opLatencies.writes;
// { latency: 12345, ops: 10000 }

❓ FAQ

Q How much of a performance difference is there between insertOne and insertMany?
A insertMany is 10 to 100 times faster than multiple insertOne calls because: (1) it reduces network round trips; (2) MongoDB processes them in batches on the server side; (3) it reduces the number of index updates. We recommend a batch size of 500 to 5,000 records.
Q Do I have to generate the _id myself?
A No, you don’t have to. If you don’t specify it, MongoDB automatically generates an ObjectId (timestamp + random value + counter) that is globally unique. Manually specifying the _id is appropriate for scenarios that require a business primary key (such as an order number).
Q Why does ordered: false offer better performance?
A When ordered: true, MongoDB inserts data sequentially and stops if an error is detected; when ordered: false, it inserts data in parallel and simply skips the failed insert if an error is detected, resulting in better performance. ordered: false is recommended for production environments.
Q Does the "majority" write concern always result in data loss?
A Not within the replica set. After a write to the primary, a majority of the secondaries must acknowledge the write before a response is returned. However, if a node goes down, writes may be slow or time out. This can be resolved by configuring a reasonable wtimeout (e.g., 5 seconds).
Q What is an appropriate batch size for bulk insertion?
A We recommend 500–5,000 records per batch, or adjust based on the data size (each batch < 16 MB). Batches that are too large can result in excessively long request times, while batches that are too small can increase network overhead.
Q How do I skip the _id field when inserting data?
A Remove the _id field at the application level (e.g., const { _id, ...rest } = doc) and let MongoDB generate it automatically. Alternatively, clear the existing _id field before importing.
Q Where are the performance bottlenecks during insertion?
A Common bottlenecks: (1) Unique index validation; (2) Write Concern wait (w: majority); (3) Replica set synchronization; (4) Disk I/O. Optimization method: First, drop all indexes (except the unique index), then rebuild them after the import.

📖 Summary


📝 Exercises

  1. Basic Exercise (⭐): Use insertOne to insert three product documents of different types (including Decimal128, Date, Array, and Object), and verify the returned insertedId.

  2. Basic Question (⭐): Use insertMany to insert 10 user documents at once, intentionally creating duplicate _id values, and compare the differences in results between ordered: true and ordered: false.

  3. Advanced Exercise (⭐⭐): Write a script to batch-insert 1,000 product documents (with randomly generated SKUs, titles, and prices), using ordered: false and the Write Concern w: majority, and record the insertion time.

  4. Advanced Problem (⭐⭐): Use bulkWrite to implement the logic "update if the _id already exists, otherwise insert" (upsert mode), processing 100 mixed records.

  5. Advanced Exercise (⭐⭐): Write a performance test script to compare the time taken for single-row inserts (1,000 insertOne calls) and batch inserts (10 insertMany calls, 100 rows per batch), and analyze the reasons for the performance differences.

  6. Challenge (⭐⭐⭐): Write a complete data migration tool that reads 1 million order records (including Decimal and DateTime fields) from MySQL, converts them to MongoDB BSON format, and imports them in bulk. The tool must support: (a) incremental synchronization; (b) retries upon failure; (c) progress display; (d) performance monitoring.

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏