MongoDB: Excluindo documentos e primeiros passos com o…
Última atualização: 2026-08-26
A exclusão de documentos é uma operação fundamental na limpeza de dados — este curso também apresenta exercícios práticos com Node.js e Mongoose.
Domine as operações deleteOne/deleteMany, a integração entre Node.js e Mongoose, a definição de esquemas e as operações CRUD de modelos.
1. O que você vai aprender
- deleteOne / deleteMany: Excluir documentos
- O método
findOneAndDeleteretorna o documento excluído de forma atômica droppara criar edropDatabasepara excluir um banco de dados- Node.js + Mongoose: Conectando-se ao MongoDB
- Definindo esquemas do Mongoose e criando modelos
- Operações CRUD no Mongoose (Criar/Ler/Atualizar/Excluir)
2. A história real de um engenheiro full-stack
(1) Desafio: A limpeza de dados expirados exige scripts complexos
Bob precisa limpar regularmente os pedidos vencidos e as contas de usuário desativadas na plataforma de comércio eletrônico:
// ❌ Counterexample:Query multiple times, then delete(Slow + Race Condition)
const expiredOrders = await Order.find({ expiryDate: { $lt: new Date() } });
for (const order of expiredOrders) {
await Order.deleteOne({ _id: order._id });
}
// N Sub-network round trip,N Second deletion operation
(2) Solução para exclusões em lote no MongoDB + Mongoose
// ✅ Correct Example:Bulk Deletion in One Go
const result = await Order.deleteMany({
expiryDate: { $lt: new Date() }
});
// Delete all expired orders in a single operation
// mongoose Schema Definition
const OrderSchema = new mongoose.Schema({
userId: { type: mongoose.Schema.Types.ObjectId, required: true },
items: [{ sku: String, qty: Number, price: mongoose.Schema.Types.Decimal128 }],
total: { type: mongoose.Schema.Types.Decimal128, required: true },
status: { type: String, enum: ['pending', 'paid', 'shipped', 'delivered'], default: 'pending' },
expiryDate: Date,
createdAt: { type: Date, default: Date.now }
}, { timestamps: true });
const Order = mongoose.model('Order', OrderSchema);
graph LR
A[Delete Operation] --> B[deleteOne<br/>Delete a single item]
A --> C[deleteMany<br/>Bulk Delete]
A --> D[findOneAndDelete<br/>Atom Returns]
A --> E[drop<br/>Delete Set]
A --> F[dropDatabase<br/>Delete the database]
style D fill:#d4edda
3. deleteOne: Excluir um único documento
Explicação do conceito: deleteOne Exclui o primeiro documento que atender aos critérios de filtragem; esse é o método de exclusão mais básico no MongoDB. A exclusão é irreversível — não há um mecanismo de “lixo eletrônico”, e os documentos excluídos não podem ser restaurados diretamente (a menos que haja um backup ou um oplog disponível). Portanto, as operações de exclusão devem ser realizadas com extrema cautela em ambientes de produção.
Como funciona: deleteOne O fluxo de execução é o seguinte: Fase de correspondência (localizar o primeiro documento com base no filtro) → Fase de exclusão (remover o documento da coleção) → Atualização do índice (excluir as entradas relevantes do índice) → Confirmação da Write Concern. Toda a operação é atômica para um único documento. Após a exclusão, o espaço em disco não é liberado imediatamente, mas é marcado como espaço reutilizável.
sequenceDiagram
participant App as Applications
participant Mongo as MongoDB
participant WT as WiredTiger
App->>Mongo: deleteOne({ sku: "PHONE-001" })
Mongo->>Mongo: Match filter (Index Scan)
Mongo->>WT: Delete Document + Update Index
WT-->>Mongo: Confirm Deletion
Mongo-->>App: { acknowledged: true, deletedCount: 1 }
| Parâmetro | Tipo | Descrição |
|---|---|---|
filter |
Documento | Critérios de pesquisa (obrigatórios) |
options |
Documento | writeConcern etc. (opcional) |
| Campo de retorno | Tipo | Descrição |
|---|---|---|
acknowledged |
Booleano | Se a gravação foi confirmada |
deletedCount |
Número | Número de documentos excluídos (0 ou 1) |
// === deleteOne Basic Usage ===
db.products.deleteOne({ sku: 'PHONE-001' });
// Return Results:
// { acknowledged: true, deletedCount: 1 }
| Campo de retorno | Significado |
|---|---|
acknowledged |
Isso já foi confirmado? |
deletedCount |
Número de documentos excluídos (0 ou 1) |
▶ Exemplo 1: deleteOne em ação
// === Delete Specified _id the document ===
db.users.deleteOne({ _id: ObjectId('507f1f77bcf86cd799439011') });
// === Delete Based on Specified Criteria(First match)===
db.logs.deleteOne({ level: 'debug' });
// === mongoose Equivalent ===
const result = await Product.deleteOne({ sku: 'PHONE-001' });
console.log(result.deletedCount); // 1
4. deleteMany: Exclusão em lote
Descrição do conceito: deleteMany exclui todos os documentos que atendem aos critérios de filtragem e é o método principal para a limpeza em lote de dados. Ao contrário do deleteOne, que exclui apenas o primeiro documento correspondente, o deleteMany pode excluir dezenas de milhares de documentos de uma só vez. Casos de uso comuns incluem a limpeza de registros expirados, a exclusão de dados de usuários desativados e a remoção de dados de teste.
Como funciona: deleteMany Primeiro, ele recupera todos os documentos correspondentes e, em seguida, os exclui um por um. O processo de exclusão não é transacional — se falhar no meio do caminho, os documentos que já tiverem sido excluídos não serão restaurados. Para exclusões em massa, recomenda-se executá-las em lotes para evitar o bloqueio da coleção por um período prolongado.
| Dimensão | deleteOne | deleteMany |
|---|---|---|
| Intervalo de partidas | Primeira partida | Todas as partidas |
| Número de exclusões | 0 ou 1 | 0 a N |
| Casos de uso | Excluir um único item | Limpeza em massa |
| Risco | Baixo | Moderado (impacto significativo devido a erro do usuário) |
// === deleteMany Basic Usage ===
db.products.deleteMany({ category: 'Discontinued' });
// === Delete all expired orders ===
db.orders.deleteMany({
expiryDate: { $lt: new Date() }
});
// === Delete Multiple Documents That Meet Specific Criteria ===
db.logs.deleteMany({
level: { $in: ['debug', 'info'] },
createdAt: { $lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }
});
▶ Exemplo 2: deleteMany na prática
// === Cleanup 30 Entries from a few days ago ===
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
const result = await Log.deleteMany({ createdAt: { $lt: thirtyDaysAgo } });
console.log(`Deleted ${result.deletedCount} old logs`);
// === Delete the session of a logged-out user ===
await Session.deleteMany({ userId: deletedUserId });
// === Delete all documents from the entire collection(Use with caution!)===
db.products.deleteMany({});
// ⚠️ This will delete products All documents in the collection
5. Retorno atômico do findOneAndDelete
Descrição do conceito: findOneAndDelete é um método especial de exclusão que retorna o conteúdo do documento excluído ao mesmo tempo em que o exclui. Isso resolve a condição de corrida associada à abordagem “primeiro consultar, depois excluir” — a abordagem tradicional exige primeiro findOne recuperar o documento e, em seguida, deleteOne excluí-lo, período durante o qual o documento pode ser modificado ou excluído por outras operações. findOneAndDelete combina a consulta e a exclusão em uma única operação atômica.
Como funciona: findOneAndDelete realiza uma operação atômica no nível do documento: localiza o documento correspondente → registra o conteúdo do documento → exclui o documento → retorna o conteúdo registrado. Por padrão, retorna o estado do documento antes da exclusão; a opção projection pode ser usada para controlar quais campos são retornados.
graph TB
A[Need to delete and retrieve a document] --> B{Method Selection}
B --> C[❌ Check First, Then Delete<br/>findOne + deleteOne<br/>Competitive Conditions Risk]
B --> D[✅ findOneAndDelete<br/>Atomic Manipulation<br/>No risk of competition]
B --> E[✅ findOneAndDelete + sort<br/>Atomic Manipulation + Order<br/>FIFO Queue]
style D fill:#d4edda
style E fill:#d4edda
| Vantagem | Descrição |
|---|---|
| Atomicidade | A consulta e a exclusão são realizadas em uma única operação, evitando condições de corrida |
| Recuperar documento | Recuperar diretamente o documento excluído, sem a necessidade de uma consulta secundária |
| Suporte à ordenação | Permite o consumo ordenado quando usado com a opção sort |
| Casos de uso | Enfileiramento de tarefas, consumo de mensagens, dedução de estoque |
// === findOneAndDelete Atomic Manipulation ===
const deletedDoc = db.products.findOneAndDelete({ sku: 'PHONE-001' });
// Restore Deleted Documents(Status before default deletion)
console.log(deletedDoc);
// { _id: ..., sku: 'PHONE-001', title: 'Phone', price: 599, ... }
// === Return if it does not exist null ===
const result = db.products.findOneAndDelete({ sku: 'NOT_EXIST' });
console.log(result); // null
| Vantagem | Descrição |
|---|---|
| Atomicidade | A consulta e a exclusão são realizadas em uma única operação, evitando condições de corrida |
| Recuperar documento | Recuperar diretamente o documento excluído, sem a necessidade de uma consulta secundária |
| Casos de uso | Enfileiramento de tarefas, consumo de mensagens, dedução de estoque |
▶ Exemplo 3: findOneAndDelete em ação
// === Scene:Message Queue(FIFO)===
const message = await Queue.findOneAndDelete(
{ status: 'pending' },
{ sort: { createdAt: 1 } } // Consume the oldest ones first
);
// === Scene:Claim a Mission ===
const task = await Task.findOneAndDelete({
status: 'available',
assignee: null
});
if (task) {
console.log(`Claimed task: ${task._id}`);
}
// === mongoose Equivalent ===
const message = await Queue.findOneAndDelete(
{ status: 'pending' },
{ sort: { createdAt: 1 } }
);
6. drop Collection e dropDatabase
Explicação conceitual: drop e dropDatabase são as operações de exclusão mais completas — drop exclui uma coleção inteira (incluindo todos os documentos e índices), e dropDatabase exclui todo o banco de dados (incluindo todas as coleções). Ao contrário de deleteMany({}), a operação drop não apenas exclui os dados, mas também exclui os metadados da coleção (definições de índice, regras de validação de esquema, configurações de limite, etc.).
Análise comparativa:
| Dimensão | deleteMany({}) | drop() | dropDatabase() |
|---|---|---|---|
| Escopo da exclusão | Todos os documentos da coleção | A coleção inteira | O banco de dados inteiro |
| Manter índice | ✅ Manter | ❌ Excluir tudo | ❌ Excluir tudo |
| Manter a configuração “capped” | ✅ Manter | ❌ Excluir | ❌ Excluir |
| Manter a validação do esquema | ✅ Manter | ❌ Excluir | ❌ Excluir |
| Velocidade | Lenta (exclui um por um) | Rápida (libera espaço imediatamente) | Rápida |
| Recuperabilidade | Pode ser recuperado por meio do oplog | Extremamente difícil de recuperar | Extremamente difícil de recuperar |
// === Delete Set ===
db.products.drop();
// true(Success)or false(The set does not exist)
// === Delete the database ===
db.dropDatabase();
// { "dropped" : "shopdb", "ok" : 1 }
// === Use with caution: Delete all data from the entire collection but keep the collection itself ===
db.products.deleteMany({});
// Equivalent but preserves the set structure(Index、capped Settings)
▶ Exemplo 4: Escolhendo entre drop e deleteMany
// Scene:Cleaning Up Temporary Test Sets
// ✅ Recommendations:drop(Delete Set+Index,Clean)
db.test_results.drop();
// ✅ Preserve the collection structure:deleteMany(Clear the document only)
db.user_sessions.deleteMany({});
7. Node.js + Mongoose: Conectando-se ao MongoDB
Visão geral do conceito: O Mongoose é o ODM (Modelagem de Objetos e Documentos) do MongoDB mais popular no ecossistema Node.js, oferecendo recursos avançados, como definição de esquema, validação de dados, middleware e consultas com junção. Esta seção começa com a conexão ao MongoDB e apresenta, gradualmente, os conceitos fundamentais do Mongoose.
Como funciona: O processo pelo qual o Mongoose se conecta ao MongoDB consiste nas seguintes etapas: criação de uma instância de conexão → estabelecimento de uma conexão TCP → autenticação (se necessário) → seleção de um banco de dados → inicialização do pool de conexões → acionamento do evento connected. Por padrão, o Mongoose mantém um pool de conexões (normalmente de 5 a 100 conexões) e reutiliza as conexões para evitar o estabelecimento e o fechamento frequentes de conexões TCP.
sequenceDiagram
participant App as Node.js Applications
participant Mongoose as mongoose
participant Mongo as MongoDB
App->>Mongoose: mongoose.connect(uri)
Mongoose->>Mongo: Establish TCP Connect
Mongo-->>Mongoose: Connection Confirmation
Mongoose->>Mongo: Certification(If you need)
Mongo-->>Mongoose: Authentication Successful
Mongoose->>Mongoose: Initialize the connection pool
Mongoose-->>App: Trigger 'connected' Event
Note over App,Mongoose: Connection Ready,Executable CRUD
| Método de conexão | Formato da URI | Casos de uso |
|---|---|---|
| Autônomo local | mongodb://localhost:27017/shopdb |
Ambiente de desenvolvimento |
| Atlas Cloud | mongodb+srv://user:pass@cluster0.mongodb.net/mydb |
Produção/Equipe |
| Conjunto de instâncias | mongodb://host1,host2,host3/shopdb?replicaSet=rs0 |
Ambiente de produção |
| Docker | mongodb://192.168.1.100:27017/shopdb |
Implantação em contêineres |
(1) Instalar o Mongoose
npm install mongoose --save
(2) Conectando-se ao MongoDB
// === Basic Connections ===
const mongoose = require('mongoose');
async function connectDB() {
await mongoose.connect('mongodb://localhost:27017/shopdb');
console.log('✅ MongoDB connected');
}
connectDB().catch(err => console.error('❌ Connection error:', err));
(3) Opções de conexão
Explicação do conceito: As opções de conexão do Mongoose controlam parâmetros-chave que determinam o comportamento da conexão — como tempo limite, tamanho do pool de conexões e métodos de autenticação. Essas opções devem ser configuradas adequadamente em ambientes de produção; caso contrário, podem resultar em vazamentos de conexão, falhas por tempo limite ou erros de autenticação.
Descrições dos parâmetros principais:
| Parâmetro | Valor padrão | Descrição | Recomendação para produção |
|---|---|---|---|
serverSelectionTimeoutMS |
30000 | Tempo limite para seleção do servidor (milissegundos) | 5000 |
socketTimeoutMS |
30.000 | Tempo limite do soquete | 45.000 |
maxPoolSize |
100 | Tamanho máximo do pool de conexões | 50–100 |
minPoolSize |
0 | Tamanho mínimo do pool de conexões | 5 |
heartbeatFrequencyMS |
10.000 | Frequência de monitoramento da frequência cardíaca | 10.000 |
retryWrites |
true | Nova tentativa automática de gravação | true |
authSource |
admin | Banco de dados de autenticação | Conforme configurado |
Princípio do pool de conexões: O Mongoose mantém um pool de conexões TCP para evitar a sobrecarga causada pela criação e destruição frequentes de conexões. Cada solicitação simultânea retira uma conexão do pool e a devolve assim que a solicitação é concluída. maxPoolSize Controle o número máximo de conexões simultâneas — definir esse valor muito baixo fará com que as solicitações fiquem em fila, enquanto defini-lo muito alto consumirá recursos excessivos do servidor.
// === Complete Connection Configuration ===
await mongoose.connect('mongodb://localhost:27017/shopdb', {
// Server selection timeout
serverSelectionTimeoutMS: 5000,
// Socket Timeout
socketTimeoutMS: 45000,
// Connection Pool Size
maxPoolSize: 50,
minPoolSize: 5,
// Automatic Reconnection
autoReconnect: true,
// Certification(If enabled)
user: 'admin',
pass: 'password',
// Certification Database
authSource: 'admin'
});
(4) Conecte-se ao Atlas
// === Atlas Concatenate Strings ===
await mongoose.connect(
'mongodb+srv://user:pass@cluster0.mongodb.net/mydb?retryWrites=true&w=majority'
);
// === With environment variables ===
require('dotenv').config();
await mongoose.connect(process.env.MONGODB_URI);
▶ Exemplo 5: Gerenciamento completo de conexões
// db.js
const mongoose = require('mongoose');
const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/shopdb', {
serverSelectionTimeoutMS: 5000,
maxPoolSize: 50
});
console.log(`✅ MongoDB connected: ${conn.connection.host}`);
// Listening for Connection Events
mongoose.connection.on('error', (err) => console.error('❌ MongoDB error:', err));
mongoose.connection.on('disconnected', () => console.warn('⚠️ MongoDB disconnected'));
mongoose.connection.on('reconnected', () => console.log('🔄 MongoDB reconnected'));
} catch (err) {
console.error('❌ Connection failed:', err.message);
process.exit(1);
}
};
const disconnectDB = async () => {
await mongoose.disconnect();
console.log('MongoDB disconnected');
};
module.exports = { connectDB, disconnectDB, mongoose };
8. Definição do esquema do Mongoose
Explicação do conceito: Um esquema é um conceito fundamental no Mongoose — ele define os tipos de campo de um documento, as regras de validação, os valores padrão, os índices e muito mais. Embora o próprio MongoDB seja sem esquema, o Mongoose oferece restrições de esquema na camada de aplicação para evitar dados incorretos e erros de tipo. Uma vez definido, um esquema é compilado em um modelo, que serve como interface para interagir com o banco de dados.
Como funciona: Esquema → Modelo → Documento é a arquitetura de três camadas do Mongoose. O Esquema define a estrutura (tipos de campos e validação); o Modelo é o resultado compilado do Esquema (correspondente a uma coleção); e o Documento é uma instância do Modelo (correspondente a um documento). O Esquema não interage diretamente com o banco de dados; as operações CRUD só podem ser realizadas por meio do Modelo.
Opções do Schema: O segundo parâmetro do construtor do Schema controla o comportamento global — timestamps: true adiciona automaticamente createdAt/updatedAt, strict: true ignora campos não declarados e versionKey: false remove a chave de versão __v.
graph LR
A[Schema<br/>Define Field Types+Verification] -->|mongoose.model| B[Model<br/>Interfaces for Operation Sets]
B -->|new Model| C[Document<br/>An Example Document]
B -->|Model.find| D[Search Results<br/>Document Array]
style A fill:#cce5ff
style B fill:#d4edda
| Opções de campos do esquema | Tipo | Descrição | Exemplo |
|---|---|---|---|
type |
Construtor | Tipo de campo | String, Number, Date |
required |
Booleano/Matriz | Obrigatório | [true, 'Email is required'] |
default |
Qualquer/Função | Valor padrão | Date.now, 0, true |
unique |
Booleano | Se deve ser criado um índice único | true |
index |
Booleano/Objeto | Criar índice | true, { sparse: true } |
enum |
Matriz | Lista de valores permitidos | ['pending', 'paid'] |
min / max |
Número | Intervalo de valores | min: 0, max: 999999 |
minlength / maxlength |
Número | Intervalo de comprimento da sequência | minlength: 3 |
match |
RegExp | Validação de expressões regulares | /^.+@.+$/ |
select |
Booleano | Se a consulta padrão retorna | false (por exemplo, passwordHash) |
validate |
Função | Função de validação personalizada | v => v.length >= 8 |
get / set |
Função | Getter/setter virtual | get: v => v.toString() |
(1) Esquema básico
// === Schema Defining the User Model ===
const UserSchema = new mongoose.Schema({
// Field Definitions
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true
},
username: {
type: String,
required: true,
unique: true,
minlength: 3,
maxlength: 30
},
passwordHash: {
type: String,
required: true,
select: false // The default query returns no results.
},
age: {
type: Number,
min: 0,
max: 150
},
role: {
type: String,
enum: ['customer', 'admin', 'moderator'],
default: 'customer'
},
isActive: {
type: Boolean,
default: true
}
}, {
// Schema Options
timestamps: true, // Auto-add createdAt/updatedAt
collection: 'users', // Explicitly Specify the Set Name
strict: true, // Strict Mode(Do not save undeclared fields)
versionKey: false // Disable __v
});
(2) Tipos de esquema
const ProductSchema = new mongoose.Schema({
// String
sku: String,
// Numbers
stock: Number,
price: mongoose.Schema.Types.Decimal128,
// Date
releaseDate: Date,
// Boolean
isActive: Boolean,
// Array
tags: [String],
// Nested Documents
specs: {
screen: String,
battery: String
},
// Buffer(Binary)
thumbnail: Buffer,
// ObjectId Quote
categoryId: mongoose.Schema.Types.ObjectId,
// Mixed Type(Any)
metadata: mongoose.Schema.Types.Mixed,
// Map(Key-value pairs)
translations: {
type: Map,
of: String
}
});
▶ Exemplo 6: Projeto abrangente de esquema
const ProductSchema = new mongoose.Schema({
sku: {
type: String,
required: [true, 'SKU is required'],
unique: true,
index: true,
match: /^[A-Z0-9-]+$/
},
title: {
type: String,
required: true,
trim: true,
maxlength: 200
},
description: {
type: String,
maxlength: 5000
},
price: {
type: mongoose.Schema.Types.Decimal128,
required: true,
min: 0,
get: v => v ? v.toString() : v // Serialize to a string
},
category: {
type: String,
enum: ['Electronics', 'Books', 'Clothing', 'Home'],
required: true,
index: true
},
tags: [String],
attributes: {
type: Map,
of: mongoose.Schema.Types.Mixed,
default: {}
},
stock: {
type: Number,
default: 0,
min: 0
},
rating: {
type: Number,
default: 0,
min: 0,
max: 5
},
isActive: {
type: Boolean,
default: true,
index: true
}
}, {
timestamps: true,
toJSON: { virtuals: true, getters: true },
toObject: { virtuals: true }
});
// Virtual Fields
ProductSchema.virtual('isInStock').get(function() {
return this.stock > 0;
});
// Index
ProductSchema.index({ category: 1, price: 1 });
ProductSchema.index({ title: 'text', description: 'text' });
const Product = mongoose.model('Product', ProductSchema);
9. Operações CRUD no Mongoose
Explicação do conceito: CRUD (Criar/Ler/Atualizar/Excluir) é o padrão básico para operações em bancos de dados. O Mongoose oferece dois estilos de CRUD — métodos estáticos do modelo (como Model.create() e Model.find()) e métodos de instância do documento (como doc.save() e doc.remove()). Os métodos estáticos operam diretamente no banco de dados, enquanto os métodos de instância primeiro modificam o objeto na memória e, em seguida, sincronizam as alterações com o banco de dados.
Análise comparativa:
| Dimensão | Métodos estáticos do modelo | Métodos de instância do documento |
|---|---|---|
| Método de chamada | Model.create(data) |
new Model(data); doc.save() |
| Verificação do gatilho | ✅ | ✅ |
| Middleware acionado | Parcial | ✅ Todos |
| Valor de retorno | Documento ou objeto de resultado | Documento |
| Casos de uso | CRUD simples | Lógica de negócios complexa |
graph TB
A[mongoose CRUD] --> B[Create<br/>create() / save()]
A --> C[Read<br/>find() / findOne() / findById()]
A --> D[Update<br/>updateOne() / findByIdAndUpdate()]
A --> E[Delete<br/>deleteOne() / findByIdAndDelete()]
style A fill:#cce5ff
(1) Criar
// === model.create() Create a Single Document ===
const user = await User.create({
email: 'alice@example.com',
username: 'alice_chen',
passwordHash: 'hashed_password',
age: 28
});
console.log(user._id); // ObjectId
// === Create Multiple Documents ===
const users = await User.create([
{ email: 'bob@example.com', username: 'bob' },
{ email: 'charlie@example.com', username: 'charlie' }
]);
// === new + save Pattern ===
const user = new User({
email: 'alice@example.com',
username: 'alice_chen'
});
await user.save();
(2) Ler
// === find Search for multiple ===
const users = await User.find({ isActive: true });
// === findOne Query a single ===
const user = await User.findOne({ email: 'alice@example.com' });
// === findById Through _id Search ===
const user = await User.findById('507f1f77bcf86cd799439011');
// === Chain Query ===
const products = await Product.find({ category: 'Electronics' })
.select('sku title price')
.sort({ price: 1 })
.limit(20)
.lean();
(3) Atualização
// === findByIdAndUpdate ===
const user = await User.findByIdAndUpdate(
userId,
{ $set: { lastLoginAt: new Date() } },
{ new: true, runValidators: true } // Return to the updated document
);
// === updateOne ===
const result = await User.updateOne(
{ email: 'alice@example.com' },
{ $set: { age: 29 } }
);
// === save() Replace the entire document ===
const user = await User.findById(userId);
user.age = 30;
await user.save();
(4) Excluir
// === findByIdAndDelete ===
const user = await User.findByIdAndDelete(userId);
// === deleteOne ===
const result = await User.deleteOne({ email: 'alice@example.com' });
// === deleteMany ===
const result = await User.deleteMany({ isActive: false });
▶ Exemplo 7: Um exercício prático completo de CRUD
// === 1. Create a User ===
const alice = await User.create({
email: 'alice@example.com',
username: 'alice_chen',
passwordHash: await bcrypt.hash('password123', 10),
age: 28
});
// === 2. Query User ===
const users = await User.find({ age: { $gte: 18 } })
.select('email username age')
.lean();
// === 3. Update User ===
await User.updateOne(
{ _id: alice._id },
{ $set: { lastLoginAt: new Date() }, $inc: { loginCount: 1 } }
);
// === 4. Delete Test User ===
await User.deleteMany({ email: { $regex: '@test\\.com$' } });
10. Resolução de erros comuns
Visão geral do conceito: Os quatro tipos mais comuns de erros encontrados ao desenvolver com Node.js e Mongoose são: erros de conexão (ECONNREFUSED), conflitos de índice único (E11000), falhas na validação do esquema (ValidationError) e falhas na conversão de tipos (CastError). Compreender as causas fundamentais desses erros e saber como lidar com eles é uma habilidade essencial no desenvolvimento com Mongoose.
Estratégia de tratamento de erros: Erros de conexão exigem um mecanismo de repetição da tentativa; conflitos de índices únicos exigem um UPSERT ou validação no front-end; falhas na validação do esquema exigem uma validação aprimorada dos formulários; e erros de conversão (CastErrors) exigem a verificação do formato do ObjectId. Todos os erros devem ser interceptados na camada de aplicação e retornar mensagens de erro fáceis de entender — não exponha o rastreamento da pilha de erros bruto do MongoDB aos usuários do front-end.
graph TB
A[mongoose Error] --> B[Connection Error<br/>ECONNREFUSED]
A --> C[Unique Index Conflict<br/>E11000]
A --> D[Verification Failed<br/>ValidationError]
A --> E[Type conversion failed<br/>CastError]
B --> B1[Inspection mongod Enable or Disable<br/>Check the connection string]
C --> C1[Usage upsert<br/>Front-End Uniqueness Validation]
D --> D1[Improve Schema Verification<br/>Front-End Form Validation]
E --> E1[Inspection ObjectId Format<br/>Verify Parameter Types]
| Erro | Causa | Solução |
|---|---|---|
MongooseServerSelectionError: connect ECONNREFUSED |
O MongoDB não está em execução | brew services start mongodb-community@7.0 |
MongoError: E11000 duplicate key error |
Conflito de índice exclusivo | Verificar se há valores duplicados nos campos |
ValidationError: Path 'email' is required |
Faltam campos obrigatórios | Preencha os campos obrigatórios |
CastError: Cast to ObjectId failed |
Erro de formato do _id | Verifique o formato da string do ObjectId |
❓ Perguntas Frequentes
P: O que é melhor,
deleteOneoufindOneAndDelete? R: UsefindOneAndDeletese precisar recuperar o documento excluído; usedeleteOnese quiser apenas excluí-lo. Ambos apresentam desempenho comparável.
P: É necessário fechar as conexões do Mongoose? R: Sim. Chame
mongoose.disconnect()oumongoose.connection.close()quando o processo for encerrado para garantir que todas as conexões sejam fechadas corretamente.
P: Se um campo for alterado no esquema, é necessário migrar o banco de dados? R: Não. O esquema do Mongoose é definido na camada de aplicação e não afeta o banco de dados. O MongoDB não possui esquema, portanto, novos campos são adicionados automaticamente. No entanto, os documentos antigos podem não conter os novos campos, de modo que a camada de aplicação deve lidar com essa situação
undefined.
P: Qual é o impacto no desempenho do
lean()? R: Olean()ignora a hidratação de documentos do Mongoose e retorna um objeto JavaScript puro. Isso resulta em um aumento de desempenho de 3 a 5 vezes, mas você perde o acesso aos métodos de documentos do Mongoose (comosave()epopulate()). É adequado para APIs de consulta puras.
P: Qual é a diferença entre
Model.createenew Model + save? R: Ambos acionam a validação do esquema e o middleware. A sintaxeModel.create()é mais concisa, enquantonew + saveé mais adequada para cenários que exigem operações passo a passo.
📖 Resumo
- deleteOne exclui um único documento; deleteMany exclui vários documentos
- findOneAndDelete: Uma operação atômica que retorna o documento excluído; adequada para filas e recuperação de tarefas
dropexclui uma coleção;dropDatabaseexclui um banco de dados- O mongoose connect se conecta ao MongoDB e é compatível tanto com o Atlas quanto com instâncias locais
- Esquema: define tipos de dados, validações, valores padrão e índices
- CRUD do modelo: criar / buscar / buscarUm / buscarPorIDeAtualizar / buscarPorIDeExcluir
- Métodos de documentos do Mongoose: save() / lean() / populate() / toJSON()
📝 Exercícios
- Pergunta básica (⭐): Use
deleteOnepara excluir o produto com o SKU 'TEST-001'. - Questões básicas (⭐): Defina o modelo
Productusando o Mongoose Schema (incluindosku,title,price,categoryestock) e execute as operaçõescreate,find,updateedelete. - Problema avançado (⭐⭐): Escreva um script em Node.js para limpar periodicamente os pedidos vencidos com mais de 30 dias (usando
deleteMany). - Problema avançado (⭐⭐): Implemente uma fila de mensagens: use
findOneAndDeletecomsort: { createdAt: 1 }para implementar o consumo FIFO. - Desafio (⭐⭐⭐): Implemente um recurso de cadastro de usuários usando Mongoose, Schema e middleware (incluindo validação da exclusividade do e-mail, hash de senha com bcrypt e registro de data e hora).