MongoDB: Inserção de documentos

Última atualização: 2026-08-26

A inserção de documentos é o primeiro passo para gravar dados no MongoDB — dominar insertOne e insertMany é a base da manipulação de dados.

Este curso oferece uma análise aprofundada de vários métodos para inserção de documentos, tratamento de erros, otimização de desempenho e o mecanismo Write Concern.

1. O que você vai aprender


2. A história real de um engenheiro de dados

(1) Problema: As importações em lote de 1 milhão de registros costumam falhar

Alice é engenheira de dados em uma empresa de comércio eletrônico e precisa migrar 1 milhão de registros de produtos do MySQL para o MongoDB:

“Usei o insertMany para importar 1 milhão de registros de produtos, mas toda a importação falhou no 500.000º registro devido a um _id duplicado, o que me fez perder quatro horas. É tudo ou nada — isso é um desastre para uma migração incremental.”

Os problemas que ela enfrenta:

Questão Impacto
ordenado: true (padrão) Se um item do lote falhar, todo o lote será considerado reprovado
Ausência de preocupação com gravação Perda de dados devido a falha no servidor
_conflito de ID Falha devido à duplicação de IDs autoincrementais no MySQL
Inserção em massa 1 milhão de inserções individuais levaram 1 hora

(2) Uma solução utilizando o MongoDB e 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) Receita

Dimensão Inserção em uma única linha Inserção em massa + ordenada: falso
Desempenho 1 milhão de registros ~60 minutos 1 milhão de registros ~3 minutos
Tolerância a falhas Uma única falha resulta em perda Uma falha parcial permite a continuação
Segurança de dados Suscetível a perdas Persistência com preocupação de gravação
Complexidade do código Simples Moderada

3. insertOne: Inserir um único documento

Explicação do conceito: insertOne é o método de gravação mais básico no MongoDB, utilizado para inserir um documento em uma coleção. Cada documento no MongoDB é armazenado no formato BSON e recebe automaticamente uma chave primária _id exclusiva. Ao contrário do INSERT INTO em bancos de dados relacionais, o insertOne não requer uma estrutura de tabela predefinida, e os documentos podem conter qualquer combinação de campos.

Como funciona: Quando um cliente inicia uma solicitação insertOne, o servidor MongoDB executa as seguintes etapas: valida o formato BSON → verifica a exclusividade de _id → grava no mecanismo de armazenamento WiredTiger → aplica a Write Concern → retorna insertedId. Todo o processo de gravação é atômico para um único documento.

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
Parâmetro Tipo Descrição
document Documento Documento a ser inserido (obrigatório)
writeConcern Documento Nível de confirmação de gravação (opcional)
Cenários aplicáveis Cenários não aplicáveis
Criação de um único registro (cadastro de usuário) Importação em massa de dados
É necessário recuperar insertedId Gravação em massa de mais de 1.000 registros
Documentos com aninhamento complexo Importação com remoção de dados duplicados

(1) Sintaxe básica

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')
// }

Análise dos pontos-chave:

  1. acknowledged: true indica que a operação de gravação foi confirmada pelo servidor MongoDB (sujeito à Write Concern)
  2. Se writeConcern: { w: 0 }, então acknowledged é false, e insertedId não é retornado.
  3. O valor de insertedId depende de _id ter sido especificado manualmente; caso contrário, um ObjectId é gerado automaticamente.

(2) Análise dos valores de retorno

Explicação do conceito: O valor de retorno de insertOne contém dois campos-chave — acknowledged e insertedId. Se acknowledged for true, isso indica que a operação de gravação foi confirmada pelo servidor MongoDB (sujeito à Write Concern; se for w: 0, então será false). .insertedId é o valor _id do documento inserido; ele é retornado independentemente de _id ter sido gerado automaticamente ou especificado manualmente.

Campo de retorno Tipo Descrição Observações
acknowledged Booleano Indica se a gravação foi confirmada falso quando w: 0
insertedId ObjectId/Any O _id do documento inserido Retorna o valor especificado quando definido manualmente
JAVASCRIPT
const result = db.products.insertOne({
  sku: "TEST-001",
  title: "Test Product"
});

print(result.acknowledged);   // true(Write confirmed)
print(result.insertedId);      // ObjectId('507f1f77bcf86cd799439012')
Campo Tipo Descrição
acknowledged booleano “true” indica que a gravação foi confirmada
insertedId ObjectId O _id do documento inserido

(3) O _id é gerado automaticamente

Explicação do conceito: _id é a chave primária de um documento do MongoDB, com tipo padrão ObjectId (binário de 12 bytes). Se _id não for especificado manualmente, o driver do MongoDB o gera automaticamente no lado do cliente, garantindo que seja único antes de ser gravado no servidor. Esse design difere dos IDs de autoincremento do MySQL — o ObjectId não depende de um contador centralizado e, por natureza, é compatível com ambientes distribuídos.

Como funciona: Um ObjectId é composto por um timestamp de 4 bytes + um valor aleatório de 5 bytes (máquina + processo) + um contador incremental de 3 bytes. A parte do timestamp garante que os ObjectIds sejam ordenados naturalmente por data e hora de inserção; o valor aleatório garante a exclusividade entre processos; e o contador garante a exclusividade dentro de um único segundo.

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 Estratégia Exemplo Cenários aplicáveis Ordenação
Auto ObjectId ObjectId("...") Cenários gerais (padrão) ✅ Ordenar por hora
Chave comercial de string "ORDER-2026-001" Número do pedido, SKU Depende do formato
Autoincremento NumberInt(1) Migração de sistemas legados ✅ Ordenar por número
Carimbo de data/hora NumberLong(1700000000) Dados de séries temporais ✅ Classificados por hora
UUID UUID("...") Único entre sistemas ❌ Sem ordem
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) Exclusividade do _id

Explicação do conceito: O MongoDB cria automaticamente um índice único no campo _id de cada coleção, o que serve como garantia fundamental da integridade dos dados. A característica exclusiva do índice único _id é que ele não pode ser excluído — mesmo que dropIndexes() seja executado, o índice _id permanece. Quando um documento com um _id duplicado é inserido, o MongoDB gera um erro E11000 duplicate key error, e toda a operação de inserção é revertida.

Casos de uso: Em cenários de migração de dados e importação em massa, os conflitos de _id são a fonte mais comum de erros. Entender como prevenir e resolver esses conflitos é fundamental para garantir uma operação estável em ambientes de produção. A estratégia de prevenção recomendada é consultar o conjunto existente de _id antes da importação ou utilizar o esquema upsert como alternativa ao insertOne.

Cenário Causa do conflito Estratégia recomendada
Migração do MySQL para o MongoDB Conflito entre IDs autoincrementais e dados existentes Remova o antigo _id e deixe o MongoDB gerá-lo
Combinação de dados de várias fontes Diferentes fontes de dados possuem a mesma chave de negócios Adicionar prefixo: sourceA_ORDER-001
Sincronização incremental Os dados de origem já existem no destino updateOne + upsert
Importação em massa CSV/JSON com linhas duplicadas ordered: false Ignorar duplicatas
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
);

▶ Exemplo 1: Uso completo de 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
});

▶ Exemplo 2: insertOne com diferentes estratégias _id

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}`);

Saída: Auto ObjectId: ObjectId('...') | Chave de negócios: ORD-20260701-0001 | Documento aninhado: ObjectId('...')


4. insertMany: Inserção em lote

Descrição do conceito: insertMany — inserção de vários documentos em uma única operação — é o método principal para gravação de dados em lote. Em comparação com o método insertOne, que processa os documentos um por um, o insertMany agrupa vários documentos em uma única solicitação de rede enviada ao servidor, reduzindo significativamente a sobrecarga de ida e volta da rede e melhorando o desempenho em 10 a 100 vezes.

Como funciona: insertMany recebe uma matriz de documentos e determina a estratégia de execução com base na opção ordered. ordered: true (padrão) insere os itens sequencialmente, um por um, e interrompe imediatamente ao encontrar um erro; ordered: false permite a inserção paralela e ignora os itens com falha para continuar a execução. Essas duas estratégias têm um impacto significativo no desempenho e na integridade dos dados.

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
Parâmetro Tipo Descrição
documents Matriz Matriz de documentos (obrigatório, pelo menos 1 entrada)
ordered Booleano true Execução sequencial (padrão), false Execução paralela
writeConcern Documento Nível de confirmação de gravação
Cenários aplicáveis Cenários não aplicáveis
Migração de dados, importação em massa Inserção de um único documento
Geração de dados de teste Gravações que exigem ordenação estrita de transações
Gravação de logs em lote Fortes dependências entre documentos

(1) Sintaxe básica

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) A opção “ordenada” (fundamental!)

Explicação do conceito: ordered é a opção mais importante para insertMany. Ela determina como o MongoDB lida com erros durante gravações em lote — se deve interromper imediatamente ou ignorar o erro e continuar. Compreender ordered é fundamental para a importação de dados em ambientes de produção.

Casos de uso: Para cenários de migração de dados e sincronização incremental, recomenda-se ordered: false, pois os dados de origem podem conter duplicatas _id; ignorar as duplicatas e continuar a importação é mais razoável do que ter todo o lote reprovado. Para cenários de transações financeiras, recomenda-se ordered: true para garantir um sequenciamento operacional rigoroso.

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) Comparação das opções “ordenadas”

Dimensão ordenado: verdadeiro ordenado: falso
Tipo de falha Falha em todo o lote Ignorar a falha e continuar
Desempenho Moderado Mais rápido (paralelo)
Casos de uso Consistência forte (por exemplo, transferências de fundos) Importação incremental, registros
Mensagem de erro Primeiro item com falha Detalhes de todas as falhas

▶ Exemplo 3: Um guia completo sobre inserção em lote

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. Preocupação com a redação

Explicação do conceito: O Write Concern é um mecanismo de segurança de gravação no MongoDB que define “quando uma operação de gravação é considerada bem-sucedida”. Ele controla o número de nós de réplica que devem confirmar a operação de gravação antes que uma resposta seja retornada ao cliente. Isso representa um equilíbrio fundamental entre a durabilidade dos dados e o desempenho de gravação — quanto maior o nível de confirmação, maior a segurança, mas maior também a latência.

Como funciona: Em uma arquitetura de conjunto de réplicas, as operações de gravação chegam primeiro ao nó primário e, em seguida, são replicadas de forma assíncrona para os nós secundários. O parâmetro w da Write Concern determina quantas confirmações de nó devem ser aguardadas. w: 1 aguarda apenas a confirmação do nó primário (é a opção mais rápida, mas apresenta risco de perda de dados), w: "majority" aguarda a confirmação da maioria dos nós (recomendado para ambientes de produção) e j: true garante que os dados tenham sido gravados no diário do disco.

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 }
Dimensão w: 0 w: 1 w: maioria w: maioria + j: verdadeiro
Número de nós de confirmação Sem espera Primário Maioria dos nós Maioria dos nós + disco
Desempenho Mais rápido Rápido Médio Lento
Segurança de dados Possível perda Os dados podem ser perdidos se o servidor principal ficar fora do ar Praticamente nenhuma perda O mais seguro
Cenários recomendados Registro Desenvolvimento Produção Finanças

(1) O que é “Write Concern”?

O Write Concern descreve o nível de confirmação necessário para que uma operação de gravação seja considerada bem-sucedida e determina quando os dados são considerados “salvos”.

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) Parâmetros de preocupação de gravação

Parâmetro Valor Descrição
w 0 / 1 / "majority" / Número Número de nós com confirmação de gravação
j true / false Gravar o diário no disco
wtimeout Número de milissegundos Tempo limite (padrão: esperar indefinidamente)

(3) Comparação dos níveis de preocupação com gravação

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) Comparação das configurações de Write Concern

Nível Desempenho Segurança de dados Casos de uso
w: 0 ⚡⚡⚡ Extremamente rápido ❌ Suscetível a perdas Registros, dados temporários
w: 1 ⚡⚡ Rápido ⚠️ Pode se perder Desenvolvimento independente
w: majority ⚡ Médio ✅ Praticamente sem perda Recomendado para ambientes de produção
w: majority, j: true ⚠️ Mais lento ✅✅ Mais seguro Finanças, dados críticos

▶ Exemplo 4: Configuração de Write Concern em ambiente de produção

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. Estratégia de resolução de conflitos de _id

Explicação do conceito: _id é o identificador exclusivo de um documento do MongoDB, e o campo _id em cada coleção cria automaticamente um índice exclusivo. Quando o _id de um documento inserido coincide com o de um documento já existente, o MongoDB gera um erro E11000 duplicate key error. Em cenários como migração de dados, importações em massa e fusão de várias fontes, os conflitos de _id são um dos problemas mais comuns.

Como funciona: Antes de gravar um documento, o MongoDB verifica primeiro se o campo _id viola a restrição do índice único. Se ocorrer um conflito, toda a operação de gravação é revertida (atomicidade de documento único), e é retornado o código de erro 11000. Compreender as diferenças entre as diversas estratégias de resolução de conflitos é fundamental para a integridade dos dados em ambientes de produção.

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
Estratégia Sintaxe Integridade dos dados Casos de uso
Ignorar duplicatas ordered: false Manter dados antigos Importação incremental, registro
Sobrescrever valores antigos replaceOne + upsert Substituir por novos dados Sincronização completa
Atualização parcial updateOne + upsert Combinar dados antigos e novos Campos de atualização incremental
Ignorar _id Excluir o campo _id Inserir tudo (novo _id) Importar sem duplicatas
Mecanismo de repetição Repetição no nível do aplicativo Depende do novo _id Conflito temporário

(1) Sintomas de erro

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 estratégias de gestão

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) Implementação da estratégia

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;
    }
  }
}

▶ Exemplo 5: Importação em massa + estratégia de remoção de duplicatas

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. Otimização de desempenho para inserções em massa

Explicação do conceito: Os gargalos de desempenho em inserções em massa decorrem principalmente de três áreas: sobrecarga da viagem de ida e volta da rede, sobrecarga da atualização de índices e sobrecarga de E/S de disco. Ao compreender esses três gargalos e otimizá-los um por um, é possível reduzir o tempo necessário para importar 1 milhão de registros de 60 minutos para menos de 3 minutos.

Como funciona: O processamento de documentos um por um insertOne gera, a cada vez, uma solicitação de rede, uma atualização de índice e uma gravação em disco. insertMany combina vários documentos em uma única solicitação de rede, reduzindo a sobrecarga em dois terços. bulkWrite Além disso, suporta tipos de operações mistas (inserção + atualização + exclusão), concluindo-as em uma única solicitação. Remover temporariamente índices desnecessários antes da importação pode melhorar ainda mais o desempenho em 5 a 10 vezes.

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
Estratégias de otimização Melhorias de desempenho Riscos Cenários recomendados
insertMany Substitui insertOne 10–100x Nenhuma Todas as gravações em lote
ordered: false 1,5–3x Pode ignorar entradas com erro Importação tolerante a falhas
Excluir índices temporariamente 5–10x É necessário reconstruí-los após a importação Importação inicial
bulkWrite Substitui insertMany 1,2–1,5x Nenhuma Operação mista
w: 0 (Sem espera por confirmação) 2–5x Pode haver perda de dados Dados temporários, registros

(1) Comparação de desempenho

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) Estratégias de otimização

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) Seleção do tamanho do lote

Tamanho dos dados Tamanho recomendado do lote Motivo
< 100 KB 1.000–5.000 Baixa sobrecarga de rede
100 KB - 1 MB 500–2000 Equilíbrio entre taxa de transferência e latência
> 1 MB 100–500 Evite solicitações individuais excessivamente grandes
Documento muito grande (quase 16 MB) 1-10 O próprio documento é grande

▶ Exemplo 6: Script de importação de dados de alto desempenho

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. Tipos especiais de inserções

Explicação do conceito: O formato BSON do MongoDB suporta uma variedade muito maior de tipos de dados do que o JSON. Ao inserir documentos, o uso correto desses tipos especiais é fundamental para evitar perda de precisão dos dados e erros de tipo. Os três problemas de precisão mais comuns são: (1) O Number do JavaScript é um número de ponto flutuante de precisão dupla, 0.1 + 0.2 ≠ 0.3; (2) o JSON não possui um tipo de data, portanto new Date() é convertido em uma string JSON.stringify(); (3) o JSON não suporta dados binários, portanto imagens e arquivos não podem ser armazenados diretamente.

Como funciona: Antes de enviar uma solicitação de inserção, o driver do MongoDB (incluindo o mongosh e o driver do Node.js) primeiro serializa o objeto JavaScript em BSON. Durante esse processo, o objeto Date é serializado como um tipo Date do BSON (carimbo de data/hora de 64 bits em milissegundos), NumberDecimal() é serializado como Decimal128 (alta precisão de 128 bits) e Buffer é serializado como BSON Binary. Compreender esse processo de serialização é fundamental para usar esses tipos especiais corretamente.

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
Tipo Sintaxe Precisão/Intervalo Cenários típicos
Date new Date() / ISODate("...") Precisão em milissegundos Carimbo de data/hora, período de validade
Decimal128 NumberDecimal("0.30") 34 dígitos decimais Valor, cálculo preciso
Int32 NumberInt(123) -2³¹ ~ 2³¹-1 Contagem, Inventário
Long NumberLong(1700000000) -2^63 ~ 2^63-1 ID do carimbo de data/hora, inteiro grande
BinData BinData(0, "base64...") Qualquer arquivo binário Imagens, PDFs
ObjectId ObjectId() / new ObjectId() 12 bytes Referência do documento, chave primária

(1) Inserir data

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) Inserir 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) Inserção de documentos aninhados

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" }
  ]
});

▶ Exemplo 7: Inserção de um tipo composto

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. Solução de problemas comuns relacionados à inserção

Explicação do conceito: As operações de inserção podem falhar por diversos motivos — conflito _id (E11000), falha na validação do documento (121), documento BSON muito grande (16755) ou nome de campo inválido (2). Compreender os códigos de erro e as estratégias de tratamento é essencial para garantir uma operação estável em um ambiente de produção. O princípio básico da resolução de problemas é: primeiro, verifique o código de erro → identifique a causa do erro → selecione uma estratégia de tratamento.

Como funciona: O MongoDB realiza uma validação em várias camadas antes de gravar um documento: validação do formato BSON → validação do nome do campo (não são permitidos nomes que comecem com $, nem .) → validação do índice único _id → validação do esquema → validação do tamanho do documento (16 MB) → validação da profundidade de aninhamento (100 níveis). Uma falha em qualquer nível impedirá a gravação e retornará o código de erro correspondente.

Dicas de depuração:

  1. Ativar registro detalhado: db.adminCommand({ setParameter: 1, logComponentVerbosity: { write: { verbosity: 2 } } })
  2. Visualizar o log de consultas lentas: db.system.profile.find().sort({ ts: -1 }).limit(5)
  3. Verificar o tamanho do documento: BSON.calculateObjectSize(doc) Retorna o número de bytes
  4. Verificar a profundidade de aninhamento: Função personalizada getDepth()

Monitoramento do ambiente de produção:

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
Código de erro Significado Causa principal Estratégia de resolução
11000 _id é uma duplicata Já existe um documento com o mesmo _id Use upsert ou ordered: false
121 Falha na validação do documento O valor do campo não está em conformidade com as regras do esquema Verifique as regras de validação do esquema
2 Erro no nome do campo O nome do campo começa com $ ou contém . Renomeie o campo
16755 Documento BSON muito grande O documento excede o limite de 16 MB Divida o documento ou use o GridFS
14 Tempo limite de Write Concern Tempo limite de resposta do nó réplica Aumente o wtimeout ou simplifique o w
50 Excede a profundidade máxima do BSON Mais de 100 níveis de aninhamento Reduza o número de níveis de aninhamento

(1) Tabela de referência de códigos de erro

Código de erro Significado Solução
11000 _id duplicado Use upsert ou ordered: false
121 Falha na validação do documento Verifique as regras de validação do esquema
2 Nome de campo inválido (por exemplo, que comece com $) Renomeie o campo
16755 O documento BSON é muito grande (>16 MB) Divida o documento ou use o GridFS
14 Tempo limite de Write Concern Aumente o wtimeout ou simplifique o w
50 Excederam a profundidade máxima do BSON Reduzir os níveis de aninhamento

(2) Dicas de depuração

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) Monitoramento de desempenho

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 }

❓ Perguntas Frequentes

P: Qual é a diferença de desempenho entre insertOne e insertMany? R: insertMany é de 10 a 100 vezes mais rápido do que várias chamadas de insertOne porque: (1) reduz as idas e voltas na rede; (2) o MongoDB as processa em lotes no lado do servidor; (3) reduz o número de atualizações de índice. Recomendamos um tamanho de lote de 500 a 5.000 registros.

P: Preciso gerar o _id manualmente? R: Não, não é necessário. Se você não o especificar, o MongoDB gera automaticamente um ObjectId (carimbo de data/hora + valor aleatório + contador) que é globalmente único. Especificar manualmente o _id é adequado para cenários que exigem uma chave primária de negócios (como um número de pedido).

P: Por que o ordered: false oferece melhor desempenho? R: No ordered: true, o MongoDB insere os dados sequencialmente e interrompe o processo se for detectado um erro; no ordered: false, ele insere os dados em paralelo e simplesmente ignora a inserção com falha se for detectado um erro, resultando em melhor desempenho. ordered: false é recomendado para ambientes de produção.

P: A preocupação com a “maioria” nas gravações sempre resulta em perda de dados? R: Não dentro do conjunto de réplicas. Após uma gravação no primário, a maioria dos secundários deve confirmar a gravação antes que uma resposta seja retornada. No entanto, se um nó ficar fora do ar, as gravações podem ficar lentas ou atingir o tempo limite. Isso pode ser resolvido configurando um wtimeout razoável (por exemplo, 5 segundos).

P: Qual é o tamanho adequado de um lote para inserção em massa? R: Recomendamos 500 a 5.000 registros por lote, ou ajustar de acordo com o tamanho dos dados (cada lote < 16 MB). Lotes muito grandes podem resultar em tempos de solicitação excessivamente longos, enquanto lotes muito pequenos podem aumentar a sobrecarga da rede.

P: Como faço para ignorar o campo _id ao inserir dados? R: Remova o campo _id no nível do aplicativo (por exemplo, const { _id, ...rest } = doc) e deixe que o MongoDB o gere automaticamente. Como alternativa, limpe o campo _id existente antes da importação.

P: Onde estão os gargalos de desempenho durante a inserção? R: Gargalos comuns: (1) Validação do índice único; (2) Espera por Write Concern (w: maioria); (3) Sincronização do conjunto de réplicas; (4) E/S de disco. Método de otimização: primeiro, remova todos os índices (exceto o índice único) e, em seguida, reconstrua-os após a importação.


📖 Resumo


📝 Exercícios

  1. Exercício básico (⭐): Use insertOne para inserir três documentos de produtos de tipos diferentes (incluindo Decimal128, Date, Array e Object) e verifique o insertedId retornado.

  2. Questão básica (⭐): Use insertMany para inserir 10 documentos de usuário de uma só vez, criando intencionalmente valores duplicados de _id, e compare as diferenças nos resultados entre ordered: true e ordered: false.

  3. Exercício avançado (⭐⭐): Escreva um script para inserir em lote 1.000 documentos de produtos (com SKUs, títulos e preços gerados aleatoriamente), utilizando ordered: false e Write Concern w: majority, e registre o tempo de inserção.

  4. Problema avançado (⭐⭐): Use bulkWrite para implementar a lógica “atualizar se _id já existir; caso contrário, inserir” (modo upsert), processando 100 registros mistos.

  5. Exercício avançado (⭐⭐): Escreva um script de teste de desempenho para comparar o tempo gasto em inserções de uma única linha (1.000 chamadas insertOne) e inserções em lote (10 chamadas insertMany, 100 linhas por lote) e analise as razões para as diferenças de desempenho.

  6. Desafio (⭐⭐⭐): Escreva uma ferramenta completa de migração de dados que leia 1 milhão de registros de pedidos (incluindo campos Decimal e DateTime) do MySQL, os converta para o formato BSON do MongoDB e os importe em massa. A ferramenta deve oferecer suporte a: (a) sincronização incremental; (b) novas tentativas em caso de falha; (c) exibição do andamento; (d) monitoramento de desempenho.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%