MongoDB: Atualização do documento

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

A atualização de documentos é uma das operações de gravação mais comuns no MongoDB — dominar o modificador update é fundamental para modificar dados.

Este curso oferece uma análise aprofundada de updateOne, updateMany e replaceOne, de vários modificadores de atualização ($set, $inc, $push, $pull), do comportamento de upsert e das garantias de atomicidade.

1. O que você vai aprender


2. Uma história real sobre uma plataforma de comércio eletrônico

(1) Desafio: Problemas de concorrência na dedução de estoque

Alice é responsável pelo sistema de pedidos de uma empresa de comércio eletrônico, onde se deparou com um problema clássico de concorrência ao atualizar o estoque:

JAVASCRIPT
// ❌ Counterexample:Check First, Then Edit(Competitive Conditions)
app.post('/api/orders', async (req, res) => {
  const product = await Product.findOne({ sku: 'PHONE-001' });

  if (product.stock <= 0) {
    return res.status(400).json({ error: 'Out of stock' });
  }

  // ⚠️ Concurrency Issues Here:Both requests were read stock=1
  await Product.updateOne(
    { sku: 'PHONE-001' },
    { $inc: { stock: -1 } }
  );
  // Both requests were successfully deducted,As a result, the inventory became -1
});

(2) Soluções para atualizações atômicas no MongoDB

JAVASCRIPT
// ✅ Correct Example:Usage + Atomic Manipulation
app.post('/api/orders', async (req, res) => {
  const result = await Product.updateOne(
    { sku: 'PHONE-001', stock: { $gt: 0 } },  // Key:Conditional Filtering
    { $inc: { stock: -1 } }
  );

  if (result.modifiedCount === 0) {
    return res.status(400).json({ error: 'Out of stock' });
  }
  // Only the following was changed: 1 This document indicates success
});

(3) Receita

Dimensão Consultar e atualizar Atualização atômica
Segurança de concorrência ❌ Condições de corrida ✅ Operações atômicas
Desempenho ⚠️ Duas consultas ⚡ Uma operação
Complexidade do código Alta Baixa

3. updateOne: Atualizar um único documento

Explicação do conceito: updateOne é o método de atualização mais utilizado no MongoDB; ele identifica o primeiro documento com base nas condições de filtro e aplica a operação de atualização. É semelhante ao UPDATE ... SET ... WHERE ... do SQL, mas o MongoDB usa modificadores de atualização (como $set e $inc) para especificar o que deve ser alterado, em vez de substituir o documento inteiro. Esse design torna as atualizações parciais mais eficientes — apenas os campos alterados são modificados, em vez de reescrever o documento inteiro.

Como funciona: updateOne O fluxo de execução é o seguinte: Fase de correspondência (localização de documentos com base no filtro) → Fase de atualização (aplicação de modificadores de atualização) → Atualização do índice (caso os campos do índice sejam modificados) → Confirmação do Write Concern. Toda a operação é atômica para um único documento — não há nenhum estado intermediário em que “apenas metade dos campos seja atualizada”.

100%
sequenceDiagram
    participant App as Applications
    participant Mongo as MongoDB
    participant WT as WiredTiger

    App->>Mongo: updateOne({ sku: "PHONE-001" }, { $set: { price: 699 } })
    Mongo->>Mongo: Match filter (Using Indexes)
    Mongo->>Mongo: Applications $set Edit
    Mongo->>Mongo: Check whether the index needs to be updated
    Mongo->>WT: Save the modified document
    WT-->>Mongo: Confirm
    Mongo-->>App: { matchedCount: 1, modifiedCount: 1 }
Parâmetro Tipo Descrição
filter Documento Critérios de pesquisa (obrigatórios)
update Documento Operação de atualização (obrigatória; deve incluir um modificador)
options Documento upsert / writeConcern etc. (opcional)
Campo de retorno Significado Observações
matchedCount Número de documentos correspondentes Pode ser 0
modifiedCount Número real de documentos modificados 0 se o valor for o mesmo e não tiver mudado
upsertedCount Número de documentos inseridos por meio de upsert Pode ser apenas 1 quando upsert: true

(1) Sintaxe básica

JAVASCRIPT
// === updateOne Basic Usage ===
db.products.updateOne(
  { sku: "PHONE-001" },        // filter
  { $set: { price: 699.99 } }  // update
);

// Return Results:
// {
//   acknowledged: true,
//   matchedCount: 1,    // Number of matching documents
//   modifiedCount: 1,   // Number of documents modified
//   upsertedCount: 0,   // Number of inserted documents
//   upsertedId: null    // Inserted _id
// }

(2) Interpretação dos valores de retorno

JAVASCRIPT
const result = await Product.updateOne(
  { sku: 'PHONE-001' },
  { $set: { stock: 50 } }
);

result.acknowledged;   // true(Write confirmed)
result.matchedCount;    // 1(Found 1 matches)
result.modifiedCount;   // 1(Actual Changes 1 items)
result.upsertedCount;   // 0(Not inserted)
Campo Significado
matchedCount Número de documentos que correspondem ao filtro
modifiedCount Número real de documentos modificados
upsertedCount Número de documentos inseridos por meio de upsert
upsertedId _id inserido no documento

(3) Tratamento de casos sem correspondência

JAVASCRIPT
const result = await Product.updateOne(
  { sku: 'NOT_EXIST' },
  { $set: { stock: 0 } }
);
print(result.matchedCount);   // 0
print(result.modifiedCount);  // 0
// No errors,Do not modify

▶ Exemplo 1: colocando o updateOne em prática

JAVASCRIPT
// === Edit a Single Field ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $set: { price: 699.99 } }
);

// === Edit Multiple Fields ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  {
    $set: {
      price: 699.99,
      stock: 50,
      lastUpdated: new Date()
    }
  }
);

// === Updating Nested Fields ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $set: { "specs.battery": "5000mAh" } }
);

// === mongoose Equivalent Notation ===
const result = await Product.updateOne(
  { sku: 'PHONE-001' },
  { $set: { price: 699.99, lastUpdated: new Date() } }
);

4. updateMany: Atualização em lote

Explicação do conceito: updateMany identifica todos os documentos que atendem aos critérios e aplica a operação de atualização de maneira uniforme a todos eles. Ao contrário do updateOne, que modifica apenas o primeiro resultado encontrado, o updateMany pode atualizar dezenas de milhares de documentos de uma só vez. Esse é o método principal para modificações em lote (como descontos em todo o site, remoção em massa de listagens e correção de dados).

Como funciona: updateMany Primeiro, é executada uma consulta para identificar todos os documentos que atendem aos critérios; em seguida, a operação de atualização é aplicada a cada documento, um por um. O processo de atualização não é transacional — se houver falha no meio do processo, os documentos que já foram atualizados não serão revertidos. Portanto, ao realizar atualizações em massa, é necessário considerar a execução em lotes e o tratamento de erros.

Dimensão atualização de um atualização de vários
Intervalo de partidas Primeira partida Todas as partidas
Operações em lote ❌ Individual ✅ Em lote
Revertimento de transação ❌ Não suportado ❌ Não suportado
Casos de uso Edições individuais Descontos em lote, exclusões e reativações
Risco Baixo Moderado (impacto significativo devido a erro do usuário)

(1) Sintaxe básica

JAVASCRIPT
// === updateMany Basic Usage ===
db.products.updateMany(
  { category: 'Electronics' },   // filter(Multiple matches)
  { $set: { discount: 0.1 } }    // update(Batch Application)
);

// Return Results:
// {
//   acknowledged: true,
//   matchedCount: 250,     // Match 250 items
//   modifiedCount: 250,    // Edit 250 items
//   upsertedCount: 0
// }

(2) Observações sobre atualizações em lote

JAVASCRIPT
// ⚠️ updateMany Transaction rollback is not supported
// If it fails along the way,Changes that have already been made will not be rolled back.

// ⚠️ Bulk updates may lock the collection
// We recommend using batch size control:
const BATCH_SIZE = 1000;
let modified = 0;
let lastId = null;

while (true) {
  const result = await Product.updateMany(
    {
      category: 'Electronics',
      _id: { $gt: lastId }
    },
    { $set: { onSale: true } },
    { limit: BATCH_SIZE }  // mongoose option
  );
  if (result.modifiedCount === 0) break;
  modified += result.modifiedCount;
}

▶ Exemplo 2: Guia prático para atualizações em lote

JAVASCRIPT
// === Apply 10% off to all Electronics products ===
db.products.updateMany(
  { category: 'Electronics' },
  { $mul: { price: 0.9 } }
);

// === Remove all expired products from the shelves ===
db.products.updateMany(
  { expiryDate: { $lt: new Date() } },
  { $set: { isActive: false } }
);

// === To everyone 5 Add tags to products with star ratings ===
db.products.updateMany(
  { rating: { $gte: 4.8 } },
  { $addToSet: { tags: 'top-rated' } }
);

5. replaceOne: Substituir o documento inteiro

Explicação do conceito: A diferença fundamental entre replaceOne e updateOne é que updateOne atualiza os campos especificados no modificador, preservando os campos não especificados; replaceOne, por outro lado, substitui completamente o conteúdo do documento, e os campos não especificados serão excluídos. Essa é uma das operações mais perigosas no MongoDB, e o uso indevido pode resultar em perda de dados.

Casos de uso: Use replaceOne somente quando for necessário reescrever um documento por completo (por exemplo, para migração de dados ou atualizações no formato do documento). Na maioria dos casos, use updateOne + $set para modificar apenas os campos necessários.

Dimensão updateOne + $set replaceOne
Campo não especificado ✅ Manter ❌ Excluir
Atomicidade ✅ Atomicidade de um único documento ✅ Atomicidade de um único documento
Casos de uso Modificação de campos selecionados Reescrita de todo o documento
Risco Baixo Alto (perda de campo)

(1) Diferença em relação a updateOne

JAVASCRIPT
// === updateOne:Modify only the specified fields ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $set: { price: 699 } }
);
// Results:{ _id, sku, title, price: 699, stock, category, ... }(Keep the other fields)

// === replaceOne:Replace throughout the document ===
db.products.replaceOne(
  { sku: 'PHONE-001' },
  { sku: 'PHONE-001', title: 'New Phone', price: 799 }
);
// Results:{ _id, sku, title: 'New Phone', price: 799 }
// ⚠️ Other Fields(stock、category etc.)All Lost!

(2) Casos de uso para replaceOne

JAVASCRIPT
// ✅ Applicable:Completely rewrite the document
db.users.replaceOne(
  { _id: 'user_001' },
  {
    _id: 'user_001',
    name: 'Alice',
    email: 'alice@example.com',
    role: 'admin',
    updatedAt: new Date()
  }
);

// ❌ Not applicable:I just want to modify one field(use updateOne + $set)

6. Modificadores de atualização de campo

Explicação do conceito: Os modificadores de atualização constituem a sintaxe central das operações de atualização do MongoDB e definem como os campos dos documentos são modificados. Ao contrário do SET field = value do SQL, o MongoDB oferece um conjunto abrangente de modificadores — $set (definir valor), $unset (excluir campo), $inc (incrementar/decrementar), $mul (multiplicar), $rename (renomear), $min/$max (atualizações condicionais), $currentDate (hora atual) e $setOnInsert (definido apenas durante o upsert).

Como funciona: Os modificadores de atualização são aplicados de forma atômica no nível do documento — os efeitos de todos os modificadores são aplicados na íntegra ou não são aplicados de forma alguma. É possível usar vários modificadores em combinação (por exemplo, $set + $inc + $currentDate), mas não é possível aplicar vários modificadores ao mesmo campo.

100%
graph TB
    A[Update Modifier] --> B[Field Value Class<br/>$set/$unset/$inc/$mul]
    A --> C[Field Name Class<br/>$rename]
    A --> D[Conditional Update Class<br/>$min/$max]
    A --> E[Time-Related<br/>$currentDate]
    A --> F[upsertDedicated<br/>$setOnInsert]

    style A fill:#cce5ff
Modificador Função Exemplo Cria um campo?
$set Definir valor do campo { $set: { price: 699 } } Criar campo caso ele não exista
$unset Excluir campo { $unset: { discount: "" } } Ignorar se o campo não existir
$inc Incrementar/Decrementar o valor { $inc: { stock: -1 } } Começar em 0 se o campo não existir
$mul Multiplicação { $mul: { price: 0.9 } } Começar por 0 se o campo não existir
$rename Renomear campo { $rename: { "stock": "qty" } }
$min Selecione o menor valor { $min: { price: 500 } } Crie o campo caso ele não exista
$max Selecione o valor maior { $max: { price: 1000 } } Crie o campo caso ele não exista
$currentDate Definir a hora atual { $currentDate: { updatedAt: true } } Criar caso o campo não exista
$setOnInsert Definido apenas para upsert { $setOnInsert: { createdAt: new Date() } } Criar apenas na inserção

(1) $set Definir o valor de um campo

JAVASCRIPT
// === Set Fields ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $set: { stock: 50, isActive: true } }
);

// === Set Up Nested Fields ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $set: { "specs.battery": "5000mAh" } }
);

// === Setting Array Elements ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $set: { "tags.0": "5g", "tags.1": "amoled" } }
);

(2) $unset: Exclui um campo

JAVASCRIPT
// === Delete a Single Field ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $unset: { discount: "" } }
);

// === Delete Multiple Fields ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $unset: { discount: "", internalNotes: "" } }
);

// === Delete Nested Fields ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $unset: { "specs.battery": "" } }
);

(3) $inc Incremento

JAVASCRIPT
// === Inventory Write-Downs ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $inc: { stock: -1 } }
);

// === Page Views +1 ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $inc: { viewCount: 1 } }
);

// === Cumulative Score(Multiple fields)===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $inc: { stock: -1, soldCount: 1, viewCount: 1 } }
);

(4) Multiplicação com $mul

JAVASCRIPT
// === Apply 10% off ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $mul: { price: 0.9 } }
);

// === Prices Have Doubled ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $mul: { price: 2 } }
);

(5) $rename: Renomear um campo

JAVASCRIPT
// === Rename Field ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $rename: { "stock": "inventory" } }
);
// stock → inventory

// === Renaming Nested Fields ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $rename: { "specs.battery": "specs.batteryCapacity" } }
);

(6) $min / $max: Obter o valor mínimo/máximo

JAVASCRIPT
// === $min:Update only during on-duty hours ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $min: { price: 500 } }
);
// If the current price > 500,Change to 500;Otherwise, no change

// === $max:Update only when the value is greater ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $max: { price: 1000 } }
);

(7) $currentDate define a data atual

JAVASCRIPT
// === Set the Current Time ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $currentDate: { lastModified: true } }
);

// === Set to Date Type ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $currentDate: { lastModified: { $type: "date" } } }
);

(8) $setOnInsert: Definir um campo durante uma operação de upsert

JAVASCRIPT
// === Only at upsert Set a default value upon insertion ===
db.products.updateOne(
  { sku: 'NEW-001' },
  {
    $set: { price: 599 },
    $setOnInsert: { createdAt: new Date(), stock: 0 }
  },
  { upsert: true }
);
// If inserted:{ sku: 'NEW-001', price: 599, createdAt: ..., stock: 0 }
// If updated:{ sku: 'NEW-001', price: 599 }(Do not set createdAt、stock)

▶ Exemplo 3: Guia prático para atualizar campos compostos

JAVASCRIPT
// === Update the order status after the payment is successful ===
db.orders.updateOne(
  { _id: orderId },
  {
    $set: {
      status: 'paid',
      paidAt: new Date(),
      paymentMethod: 'credit_card'
    },
    $inc: { version: 1 },         // Optimistic Lock Version Number
    $currentDate: { updatedAt: true }
  }
);

// === Update the last login time after the user logs in ===
db.users.updateOne(
  { _id: userId },
  {
    $set: { lastLoginAt: new Date(), lastLoginIp: '192.168.1.1' },
    $inc: { loginCount: 1 }
  }
);

7. Modificadores de atualização de matrizes

Explicação do conceito: As matrizes são as estruturas de dados mais flexíveis nos documentos do MongoDB, mas atualizar elementos de uma matriz é mais complexo do que atualizar campos comuns. O MongoDB oferece modificadores dedicados para matrizes — $push (adicionar um elemento), $pull (excluir elementos correspondentes), $addToSet (adicionar sem duplicatas), $pop (excluir o primeiro e o último elementos), bem como os operadores de posicionamento $ e $[] para atualizar com precisão elementos específicos em uma matriz.

Como funciona: Os modificadores de matriz atuam sobre os próprios elementos da matriz, e não sobre o documento inteiro. A principal diferença entre $push e $addToSet é que $push adiciona elementos incondicionalmente (o que pode resultar em duplicatas), enquanto $addToSet verifica primeiro se um elemento já existe (para remover duplicatas). O operador de posicionamento $, quando usado com uma condição de filtro, localiza o “primeiro elemento da matriz que corresponde”; $[] atua sobre “todos os elementos da matriz”; e $[identifier] + arrayFilters realizam “atualizações em lote com base em condições”.

Filosofia de projeto: O MongoDB incentiva a incorporação de pequenas quantidades de dados associados em matrizes (como uma lista de avaliações de produtos ou tags de usuários), mas matrizes muito grandes (que excedam várias centenas de elementos) podem afetar o desempenho das consultas e atualizações. Para grandes quantidades de dados associados, recomenda-se o uso de coleções separadas com referências.

100%
graph TB
    A[Array Update Modifiers] --> B[Add an element<br/>$push / $addToSet]
    A --> C[Delete Element<br/>$pull / $pop]
    A --> D[Bulk Operations<br/>$each / $slice]
    A --> E[Location Update<br/>$ / $[] / $[filter]]

    style A fill:#cce5ff
Modificador Função Remoção de duplicatas Exemplo Frequência de uso
$push Adicionar elemento { $push: { tags: "new" } } ⭐⭐⭐
$addToSet Remoção e adição de duplicatas { $addToSet: { tags: "new" } } ⭐⭐
$pull Excluir elementos correspondentes { $pull: { tags: "old" } } ⭐⭐
$pop Remover o primeiro/último elemento { $pop: { tags: 1 } }
$each Adicionar em massa (em conjunto com $push) { $push: { tags: { $each: [...] } } } ⭐⭐
$slice Limitar o comprimento da matriz { $push: { tags: { $each: [...], $slice: -5 } } } ⭐⭐
$position Especificar posição de inserção { $push: { tags: { $each: [...], $position: 0 } } }

(1) $push: Adiciona um elemento a uma matriz

JAVASCRIPT
// === Add a Single Element ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $push: { tags: 'bestseller' } }
);

// === Add Multiple Elements($each)===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $push: { tags: { $each: ['5g', 'amoled', 'fast-charging'] } } }
);

// === Limit the array size($slice + $position)===
db.products.updateOne(
  { sku: 'PHONE-001' },
  {
    $push: {
      tags: {
        $each: ['new1', 'new2', 'new3'],
        $slice: -5,            // Keep only the last one 5 items
        $position: 0           // Insert from the beginning
      }
    }
  }
);

(2) O comando $pull exclui os elementos correspondentes

JAVASCRIPT
// === Delete a Specified Value ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $pull: { tags: 'old-tag' } }
);

// === Delete all elements that meet the criteria ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $pull: { tags: { $in: ['outdated1', 'outdated2'] } } }
);

(3) $addToSet: Adicionar elementos a um array removendo duplicatas

JAVASCRIPT
// === Add only elements that do not exist ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $addToSet: { tags: 'new-tag' } }
);
// If tags Included 'new-tag',Do not add again

// === Add multiple($each)===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $addToSet: { tags: { $each: ['tag1', 'tag2'] } } }
);

(4) $pop remove o primeiro ou o último elemento

JAVASCRIPT
// === Delete the last element ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $pop: { tags: 1 } }
);

// === Delete the first element ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $pop: { tags: -1 } }
);

(5) Localização e atualização de elementos de uma matriz

JAVASCRIPT
// === Update via Position Index ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $set: { "tags.0": "updated-first-tag" } }
);

// === Through $ Positioning Symbol Update(The first matching element found)===
db.products.updateOne(
  { sku: 'PHONE-001', "reviews.userId": 'user_001' },
  { $set: { "reviews.$.helpful": 10 } }
);
// Found userId='user_001' Comments on,Set it to helpful Field

// === Batch Update Array Elements ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $set: { "reviews.$[].status": "approved" } }
);
// All Comments status → approved

(6) $[] Atualizar todos os elementos

JAVASCRIPT
// === Update all array elements ===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $set: { "reviews.$[].status": "approved" } }
);

// === Updating Array Elements Based on Conditions(arrayFilters)===
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $set: { "reviews.$[lowRating].flagged": true } },
  {
    arrayFilters: [{ "lowRating.rating": { $lt: 2 } }]
  }
);
// Rating by tags only < 2 Comments on

▶ Exemplo 4: Atualizações práticas em matrizes

JAVASCRIPT
// === Scene:E-commerce Review System ===
// 1. Add a comment
db.products.updateOne(
  { sku: 'PHONE-001' },
  {
    $push: {
      reviews: {
        userId: 'user_001',
        rating: 5,
        content: 'Excellent phone!',
        createdAt: new Date(),
        helpful: 0
      }
    }
  }
);

// 2. Delete a specific comment from a user
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $pull: { reviews: { userId: 'user_001' } } }
);

// 3. Flag low-rated reviews
db.products.updateOne(
  { sku: 'PHONE-001' },
  { $set: { "reviews.$[r].flagged": true } },
  { arrayFilters: [{ "r.rating": { $lt: 2 } }] }
);

// 4. Limit the maximum number of comments 100 items
db.products.updateOne(
  { sku: 'PHONE-001' },
  {
    $push: {
      reviews: {
        $each: [newReview],
        $slice: -100
      }
    }
  }
);

8. A opção upsert

Explicação do conceito: UPSERT = UPDATE + INSERT. Trata-se de um modo de gravação exclusivo do MongoDB — se o documento existir, ele é atualizado; se não existir, é inserido. Esse modo é fundamental em cenários de “gravação idempotente”: o resultado permanece consistente, independentemente de quantas vezes a operação seja executada. Cenários típicos incluem: registros de login de usuários (criados no primeiro login de cada dia, com atualizações subsequentes); carrinhos de compras (criados ao adicionar o primeiro item, com atualizações subsequentes na quantidade); e configurações (criadas na configuração inicial, com modificações subsequentes nos valores).

Como funciona: Quando upsert: true é definido, o MongoDB primeiro tenta encontrar documentos que correspondam ao filtro. Se for encontrado um documento correspondente, ele aplica o modificador de atualização (exatamente como um updateOne comum). Se nenhum documento correspondente for encontrado, ele combina as condições de igualdade do filtro com $set/$setOnInsert do modificador de atualização para inserir um novo documento. $setOnInsert só tem efeito durante a inserção e é ignorado durante as atualizações — essa é a melhor maneira de definir valores padrão.

100%
graph TB
    A[updateOne + upsert: true] --> B{filter Matching Documents?}
    B -->|Yes| C[Apply $set update modifier]
    B -->|No| D[Merge filter conditions + $set + $setOnInsert]
    D --> E[Insert a New Document]
    C --> F[Back matchedCount=1<br/>upsertedCount=0]
    E --> G[Back matchedCount=0<br/>upsertedCount=1<br/>upsertedId=ObjectId]

    style C fill:#d4edda
    style E fill:#fff3cd
Comportamento do upsert contagem de correspondências contagem de modificações contagem de inserções ID da inserção
Localizar e modificar 1 0 ou 1 0 nulo
Não encontrado, inserir 0 0 1 ObjectId(...)
Não encontrado, sem upsert 0 0 0 null

(1) O que é um “upsert”?

upsert = atualização + inserção; se o registro existir, atualize-o; se não existir, insira-o.

100%
graph TB
    A[updateOne + upsert] --> B{The document exists?}
    B -->|Yes| C[Execute $set update]
    B -->|No| D[Insert a New Document<br/>Apply $set + filter fields]

    style C fill:#d4edda
    style D fill:#fff3cd

(2) Comportamento de “upsert”

JAVASCRIPT
// === upsert: false(Default)===
const result1 = await Product.updateOne(
  { sku: 'NEW-001' },
  { $set: { price: 599 } }
);
print(result1.matchedCount);   // 0(No matches found)
print(result1.modifiedCount);  // 0
print(result1.upsertedCount);  // 0

// === upsert: true ===
const result2 = await Product.updateOne(
  { sku: 'NEW-001' },
  { $set: { price: 599 } },
  { upsert: true }
);
print(result2.upsertedCount);  // 1(Insert 1 items)
print(result2.upsertedId);     // ObjectId('...')

(3) $setOnInsert é definido apenas no momento da inserção

JAVASCRIPT
// === Complete upsert Pattern ===
db.products.updateOne(
  { sku: 'NEW-001' },
  {
    $set: { price: 599, updatedAt: new Date() },
    $setOnInsert: { createdAt: new Date(), stock: 0, viewCount: 0 }
  },
  { upsert: true }
);

// === Insert if not present:{ sku: 'NEW-001', price: 599, updatedAt: ..., createdAt: ..., stock: 0, viewCount: 0 }
// === Update if it exists:{ sku: 'NEW-001', price: 599, updatedAt: ..., createdAt: <Old value> }

▶ Exemplo 5: Um guia prático sobre UPSERT

JAVASCRIPT
// === User Login History upsert ===
db.user_logins.updateOne(
  {
    userId: 'user_001',
    date: '2026-07-01'
  },
  {
    $set: { lastLoginAt: new Date() },
    $inc: { loginCount: 1 },
    $setOnInsert: { firstLoginAt: new Date() }
  },
  { upsert: true }
);
// Create a record for the first login of each day,Future Updates

// === Shopping Cart upsert ===
db.carts.updateOne(
  { userId: 'user_001' },
  {
    $set: { updatedAt: new Date() },
    $inc: { totalItems: 2 }
  },
  { upsert: true }
);

9. Melhores práticas para operações de atualização

Explicação do conceito: As melhores práticas para operações de atualização giram em torno de três princípios fundamentais: atomicidade (evitar condições de corrida), desempenho (reduzir idas e voltas na rede e tempos de retenção de bloqueios) e segurança (prevenir operações acidentais). Dentre esses, a atomicidade é o mais crítico — as operações de documento único do MongoDB são inerentemente atômicas, mas o padrão “consultar e depois atualizar” compromete essa garantia de atomicidade.

Como funciona: O MongoDB garante a atomicidade para operações de gravação em um único documento — uma operação updateOne ou é totalmente bem-sucedida ou falha completamente; não há um estado intermediário em que a atualização seja concluída apenas parcialmente. No entanto, operações entre documentos não garantem automaticamente a atomicidade (transações entre múltiplos documentos requerem a versão 4.0 ou posterior). Portanto, ao projetar um modelo de dados, você deve tentar colocar os dados relacionados no mesmo documento para aproveitar a atomicidade de um único documento.

100%
graph TB
    A[Best Practices for Updates] --> B[Atomicity<br/>filter + Atomic Modifiers]
    A --> C[Performance<br/>bulkWrite + Index]
    A --> D[Safety<br/>Return Value Checking + Version Control]

    B --> B1[✅ Recommendations: filter Conditional Filtering<br/>{ sku, stock: { $gt: 0 } }]
    B --> B2[❌ Avoid: Check First, Then Edit<br/>findOne + updateOne]

    style B1 fill:#d4edda
    style B2 fill:#f8d7da
Práticas Melhores práticas Anti-padrões
Segurança de concorrência filtro + operações atômicas findOne + updateOne
Atualização em lote bulkWrite + ordered: false Loop updateOne
Controle de versão $inc: { __v: 1 } Sem número de versão
Tratamento de erros Verificar matchedCount/modifiedCount Ignorar o valor de retorno

(1) Garantia de atomicidade

JAVASCRIPT
// ✅ Safety:filter + Atomic Manipulation
const result = await Product.updateOne(
  { sku: 'PHONE-001', stock: { $gt: 0 } },
  { $inc: { stock: -1 } }
);

// ❌ Unsafe:Check First, Then Edit(Competitive Conditions)
const product = await Product.findOne({ sku: 'PHONE-001' });
if (product.stock > 0) {
  await Product.updateOne(
    { sku: 'PHONE-001' },
    { $inc: { stock: -1 } }
  );
}

(2) Otimização de desempenho

Visão geral do conceito: A otimização de desempenho para operações de atualização gira em torno de três estratégias principais: reduzir as idas e voltas na rede (usando bulkWrite em vez de chamadas repetidas de updateOne), filtrar por campos de índice (para evitar varreduras completas da tabela) e evitar reescritas desnecessárias de documentos (atualizando apenas os campos que foram alterados). Dentre elas, bulkWrite proporciona a melhoria de desempenho mais significativa — enquanto 100 operações individuais de updateOne levam aproximadamente 10 segundos, uma única operação de bulkWrite leva apenas 0,1 segundo.

Como funciona: Cada chamada ao updateOne envolve uma viagem completa de ida e volta pela rede — o cliente envia uma solicitação → o servidor localiza o documento → o aplicativo é atualizado → o resultado é retornado. O bulkWrite combina mais de 100 operações em uma única solicitação de rede; o servidor executa todas as operações em ordem e retorna os resultados em um único lote. Além disso, o mecanismo de armazenamento WiredTiger utiliza o mecanismo MVCC ao atualizar documentos — se o tamanho do documento aumentar após a atualização e não houver espaço suficiente no local original, o documento é movido para um novo local, acionando atualizações em todas as entradas do índice. Portanto, minimizar alterações no tamanho do documento (como substituir $inc por $set, o que reescreve todo o campo numérico) também traz benefícios de desempenho.

Estratégia de otimização Melhoria de desempenho Alterações no código Nível de recomendação
bulkWrite Alternativa ao loop updateOne 10–100x Médio ⭐⭐⭐
Filtragem por campo de índice 10–1.000x Baixa ⭐⭐⭐
$inc Substituir e reescrever campos numéricos 1,5–2x Baixo ⭐⭐
Controle do tamanho do lote (1.000/lote) 1,5–3x Baixo ⭐⭐
Variação no tamanho do documento 1,2–1,5x Baixa
JAVASCRIPT
// === Optimization 1:Batch updates instead of multiple individual updates ===
// ❌ Slow: 100 times updateOne
for (const item of items) {
  await Product.updateOne({ sku: item.sku }, { $inc: { stock: -item.qty } });
}

// ✅ Fast: 1 times bulkWrite
await Product.bulkWrite(
  items.map(item => ({
    updateOne: {
      filter: { sku: item.sku, stock: { $gte: item.qty } },
      update: { $inc: { stock: -item.qty, soldCount: item.qty } }
    }
  })),
  { ordered: false }
);

// === Optimization 2:Filter by Index Field ===
// ✅ Indexed:db.products.updateOne({ sku: 'PHONE-001' }, ...)
// ⚠️ No index:db.products.updateOne({ title: 'Phone' }, ...)

(3) Tratamento de erros

JAVASCRIPT
// === UpdateResult Processing ===
async function updateProductStock(sku, qty) {
  const result = await Product.updateOne(
    { sku, stock: { $gte: qty } },
    { $inc: { stock: -qty, soldCount: qty } }
  );

  if (result.matchedCount === 0) {
    throw new Error(`Out of stock or item not available: ${sku}`);
  }

  if (result.modifiedCount === 0) {
    throw new Error('Update Failed');
  }

  return result;
}

❓ Perguntas Frequentes

P: Qual é a diferença de desempenho entre updateOne e updateMany? R: updateMany atualiza vários documentos em uma única operação, o que é eficiente, mas bloqueia mais documentos. Para atualizações em lote, recomendamos usar o bulkWrite com o ordered: false, que é mais flexível do que o updateMany.

P: O $set gera um erro se um campo não existir? R: Não. O $set cria campos automaticamente (incluindo campos aninhados). Chamar o $unset em um campo inexistente também não causa nenhum problema.

P: Como o _id é gerado durante uma operação UPSERT? R: Ele utiliza automaticamente o valor do campo _id declarado no filtro; se o filtro não incluir o _id, o MongoDB gera automaticamente um ObjectId.

P: O que devo fazer se houver perda de precisão com números de ponto flutuante $inc? R: Os números de ponto flutuante $inc podem apresentar problemas de precisão. Recomendamos usar o tipo Decimal128 (mongoose.Types.Decimal128) para cálculos precisos.

P: Como localizo elementos ao atualizar campos de matriz? R: Use o localizador $ (para encontrar o primeiro elemento correspondente) ou $[identifier] + arrayFilters (para atualizar vários elementos com base em condições).

P: Quando devo usar replaceOne em vez de updateOne? R: Use updateOne + $set se quiser modificar apenas alguns campos; use replaceOne para reescrever o documento inteiro. O replaceOne não preserva os campos que não forem especificados; portanto, use-o com cautela.


📖 Resumo


📝 Exercícios

  1. Exercício básico (⭐): Use updateOne para alterar o preço do produto, o estoque e a data da última atualização.
  2. Exercício básico (⭐): Use $push para adicionar três tags a um produto e use $addToSet para testar a remoção de duplicatas.
  3. Exercício avançado (⭐⭐): Use bulkWrite para implementar a dedução do estoque por pedido (dedução atômica de vários itens) e lidar com situações em que o estoque é insuficiente.
  4. Problema avançado (⭐⭐): Use upsert para implementar estatísticas diárias de login de usuários (crie o valor na primeira ocorrência e, em seguida, incremente-o nas ocorrências subsequentes).
  5. Desafio (⭐⭐⭐): Implemente um recurso de fusão de carrinhos de compras para combinar os itens do carrinho temporário com o carrinho de compras do usuário, lidando com itens duplicados (por meio da agregação de quantidades).
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%