Cheatsheet MongoDB
Base de dados NoSQL orientada a documentos (JSON/BSON)
MongoDB
Bases de Dados e Coleções
Listar bases de dados
show dbs // No driver Node.js: const dbs = await client.db().admin().listDatabases(); dbs.databases.forEach(d => console.log(d.name, d.sizeOnDisk));
show dbs lista todas as bases de dados no mongosh. Só mostra bases com pelo menos um documento. Bases vazias não aparecem.
Criar coleção com opções
// Coleção simples
db.createCollection("clientes")
// Coleção capped (tamanho fixo, tipo log)
db.createCollection("logs", {
capped: true,
size: 1048576, // 1 MB
max: 5000 // máx. documentos
})createCollection() cria explicitamente. Coleções capped têm tamanho fixo e sobrescrevem os mais antigos — ideais para logs. A maioria das coleções não precisa disto.
Selecionar / criar base
use loja
// A base é criada automaticamente
// ao inserir o primeiro documento
db.produtos.insertOne({ nome: "Teste" });use nome muda para a base indicada. Se não existir, é criada implicitamente ao primeiro insert. Não há comando CREATE DATABASE explícito.
Apagar coleção
db.clientes.drop()
// Apaga também todos os índices associados
// Retorna true se existia, false caso contrário
// Verificar antes:
if (db.getCollectionNames().includes("temp")) {
db.temp.drop();
}drop() elimina a coleção, os seus documentos e todos os índices. É irreversível. Para limpar dados mantendo a estrutura, usa deleteMany({}).
Base de dados atual
db // mostra o nome da base atual db.getName() // alternativa explícita // Estatísticas db.stats() db.stats().objects // total de documentos
db retorna a referência à base atual. db.stats() mostra estatísticas como tamanho, número de documentos e índices.
Apagar base de dados
// Apaga a base de dados atual db.dropDatabase() // Confirmação: show dbs // já não aparece
db.dropDatabase() elimina toda a base atual — dados, coleções e índices. Operação destrutiva e irreversível. Confirma sempre com db.getName() antes.
Listar coleções
show collections // Ou programaticamente: db.getCollectionNames() // Com detalhes: db.getCollectionInfos()
show collections lista as coleções da base atual. getCollectionNames() retorna um array. getCollectionInfos() inclui opções como capped e validator.
Renomear coleção
db.clientesAntigos.renameCollection("clientes")
// Com opção de sobrescrever destino:
db.temp.renameCollection("clientes", true)renameCollection() muda o nome da coleção. O segundo argumento true permite sobrescrever uma coleção existente com o mesmo nome. Índices são preservados.
Inserir Documentos
insertOne
const resultado = db.clientes.insertOne({
nome: "Ana",
idade: 30,
cidade: "Lisboa",
ativo: true
});
console.log(resultado.insertedId);insertOne() insere um único documento. Retorna insertedId com o ObjectId gerado. Se o campo _id não for indicado, o MongoDB cria automaticamente.
Arrays em documentos
db.clientes.insertOne({
nome: "Rui",
tags: ["vip", "newsletter"],
telefones: [
{ tipo: "pessoal", numero: "912345678" },
{ tipo: "trabalho", numero: "213456789" }
]
});Campos podem conter arrays de valores simples ou de objetos. Arrays de objetos são úteis para listas com metadata. Limite: documento até 16 MB.
insertMany
const resultado = db.clientes.insertMany([
{ nome: "Rui", idade: 25 },
{ nome: "Mia", idade: 40 },
{ nome: "Zé", idade: 35 }
]);
console.log(resultado.insertedCount); // 3
console.log(resultado.insertedIds);insertMany() insere múltiplos documentos num array. insertedCount confirma quantos foram inseridos. Por padrão, para ao primeiro erro (ordered).
Tipos de dados
db.tipos.insertOne({
texto: "string",
inteiro: NumberInt(42),
longo: NumberLong(9999999999),
double: 3.14,
booleano: true,
data: new Date(),
nulo: null,
objectId: new ObjectId(),
regex: /padrão/i
});O BSON suporta String, Int32, Int64, Double, Boolean, Date, null, ObjectId, Array, Object e Regex.
insertMany (unordered)
db.clientes.insertMany(
[
{ nome: "A", _id: 1 },
{ nome: "B", _id: 1 }, // duplicado!
{ nome: "C", _id: 3 }
],
{ ordered: false }
);
// Insere A e C, ignora BCom { ordered: false }, o MongoDB continua a inserir os restantes mesmo se um falhar. Mais rápido para bulk inserts. Os erros ficam em writeErrors.
_id personalizado
// Usar ID próprio em vez de ObjectId
db.clientes.insertOne({
_id: "ana@email.com",
nome: "Ana"
});
// Ou UUID
db.clientes.insertOne({
_id: UUID(),
nome: "Rui"
});O campo _id pode ser qualquer valor único (string, número, UUID). Se omitido, o MongoDB gera um ObjectId de 12 bytes automaticamente.
Documentos aninhados
db.clientes.insertOne({
nome: "Ana",
morada: {
rua: "Av. Liberdade",
numero: 42,
cidade: "Lisboa",
cp: "1000-001"
}
});O MongoDB suporta objetos aninhados (embedded documents). Ideal para dados acedidos em conjunto. Profundidade máxima: 100 níveis. Consulta com notação de ponto.
Validação de schema
db.createCollection("clientes", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["nome", "email"],
properties: {
nome: { bsonType: "string" },
idade: { bsonType: "int", minimum: 0 }
}
}
}
});$jsonSchema valida documentos na inserção/update. Campos em required são obrigatórios. bsonType define o tipo esperado. Rejeita documentos inválidos.
Consultas (find)
find (todos os documentos)
db.clientes.find()
// Com formatação legível:
db.clientes.find().pretty()
// No driver Node.js:
const docs = await db.collection("clientes").find({}).toArray();find() sem filtro retorna todos os documentos da coleção. Retorna um cursor — os dados são carregados em lotes, não todos de uma vez.
Ordenação (sort)
// Crescente por idade
db.clientes.find().sort({ idade: 1 })
// Decrescente por idade
db.clientes.find().sort({ idade: -1 })
// Múltiplos campos
db.clientes.find().sort({ cidade: 1, idade: -1 })sort() ordena os resultados. 1 = crescente, -1 = decrescente. Podes combinar campos. Sem índice, faz in-memory sort (limite 100 MB).
Filtro simples
// Igualdade exata
db.clientes.find({ cidade: "Lisboa" })
// Múltiplas condições (AND implícito)
db.clientes.find({ cidade: "Lisboa", ativo: true })
// Por _id
db.clientes.find({ _id: ObjectId("65f...") })Filtros usam igualdade por padrão. Múltiplos campos no mesmo objeto funcionam como AND. Para comparar por _id, usa ObjectId() como wrapper.
Limit e Skip (paginação)
// Primeiros 10
db.clientes.find().limit(10)
// Página 3 (skip 20, limit 10)
db.clientes.find().sort({ nome: 1 }).skip(20).limit(10)
// Ordem recomendada: sort → skip → limitlimit() restringe o número de resultados. skip() salta documentos (paginação). Para datasets grandes, prefere paginação por _id em vez de skip.
findOne
const cliente = db.clientes.findOne({ nome: "Ana" });
// Retorna o documento ou null
if (cliente) {
console.log(cliente.idade);
}findOne() retorna o primeiro documento que corresponde (ou null). Não retorna cursor — é direto. Útil quando sabes que existe apenas um resultado.
Contar documentos
// Total da coleção
db.clientes.countDocuments()
// Com filtro
db.clientes.countDocuments({ ativo: true })
// Estimativa rápida (usa metadata, não percorre)
db.clientes.estimatedDocumentCount()countDocuments() conta com precisão (percorre documentos). estimatedDocumentCount() é instantâneo mas aproximado — usa metadata da coleção.
Projeção (campos)
// Incluir só nome e cidade (excluir _id)
db.clientes.find({}, { nome: 1, cidade: 1, _id: 0 })
// Excluir campos específicos
db.clientes.find({}, { password: 0, notas: 0 })
// Campo aninhado
db.clientes.find({}, { "morada.cidade": 1 })O segundo argumento de find() é a projeção. 1 inclui, 0 exclui. O _id vem sempre por padrão — usa _id: 0 para omitir.
distinct
// Valores únicos de um campo
db.clientes.distinct("cidade")
// ["Lisboa", "Porto", "Braga"]
// Com filtro
db.clientes.distinct("cidade", { ativo: true })
// Em arrays, retorna elementos únicos
db.clientes.distinct("tags")distinct() retorna um array com os valores únicos de um campo. Aceita filtro como segundo argumento. Em campos array, retorna elementos individuais únicos.
Operadores de Consulta
Comparação ($gt, $lt, $ne)
{ idade: { $gt: 18 } } // maior que
{ idade: { $gte: 18 } } // maior ou igual
{ idade: { $lt: 65 } } // menor que
{ idade: { $lte: 65 } } // menor ou igual
{ idade: { $ne: 30 } } // diferente de
{ idade: { $eq: 30 } } // igual a (explícito)Operadores de comparação: $gt, $gte, $lt, $lte, $ne, $eq. Funcionam com números, strings e datas.
$exists e $type
// Campo existe
{ telefone: { $exists: true } }
// Campo não existe
{ fax: { $exists: false } }
// Verificar tipo BSON
{ idade: { $type: "int" } }
{ data: { $type: "date" } }$exists verifica presença do campo (mesmo se null). $type filtra pelo tipo BSON: "string", "int", "double", "bool", "date", "array", "object", "null".
$in e $nin
// Cidade está na lista
{ cidade: { $in: ["Lisboa", "Porto", "Braga"] } }
// Cidade NÃO está na lista
{ cidade: { $nin: ["Faro", "Évora"] } }
// Funciona com arrays: campo contém algum valor
{ tags: { $in: ["vip", "premium"] } }$in corresponde se o valor estiver na lista. $nin é o inverso. Em campos array, verifica se algum elemento coincide. Mais eficiente que múltiplos $or.
$regex (pesquisa de texto)
// Começa com "Ana" (case-insensitive)
{ nome: { $regex: "^Ana", $options: "i" } }
// Contém "silva"
{ nome: { $regex: "silva", $options: "i" } }
// Sintaxe alternativa
{ nome: /^ana/i }$regex permite padrões de expressão regular. $options: "i" ignora maiúsculas. Sem índice, faz collection scan. Prefixos (^) podem usar índice.
$and e $or
// OR: uma das condições
{ $or: [
{ idade: { $lt: 18 } },
{ vip: true }
]}
// AND explícito (necessário para mesmo campo)
{ $and: [
{ idade: { $gt: 18 } },
{ idade: { $lt: 65 } }
]}$or exige pelo menos uma condição verdadeira. $and explícito é necessário quando aplicas dois operadores ao mesmo campo (ex.: range com $gt e $lt).
Campos aninhados e arrays
// Campo aninhado (notação de ponto)
{ "morada.cidade": "Porto" }
// Array contém valor
{ tags: "vip" }
// Array na posição específica
{ notas: { $elemMatch: { $gt: 15, $lt: 20 } } }
// Tamanho do array
{ tags: { $size: 3 } }Notação de ponto acede a campos aninhados. Em arrays, a igualdade verifica se contém o valor. $elemMatch aplica múltiplas condições ao mesmo elemento. $size filtra pelo comprimento.
$not e $nor
// NOT: inverte uma condição
{ idade: { $not: { $gt: 65 } } }
// NOR: nenhuma condição pode ser verdadeira
{ $nor: [
{ cidade: "Lisboa" },
{ vip: true }
]}$not inverte um operador (inclui documentos sem o campo). $nor é o oposto de $or — nenhum pode corresponder. Cuidado: $not inclui docs onde o campo não existe.
$expr (expressões)
// Comparar dois campos do mesmo documento
{ $expr: { $gt: ["$gastos", "$orcamento"] } }
// Com agregação dentro de find
{ $expr: {
$and: [
{ $eq: ["$estado", "ativo"] },
{ $gte: ["$saldo", 100] }
]
}}$expr permite usar expressões de aggregation dentro de find(). Útil para comparar campos entre si. O prefixo $ referencia valores de campos.
Atualizar Documentos
updateOne ($set)
db.clientes.updateOne(
{ nome: "Ana" },
{ $set: { idade: 31, cidade: "Porto" } }
);
// Resultado: { matchedCount: 1, modifiedCount: 1 }updateOne() atualiza o primeiro documento que corresponde ao filtro. $set define novos valores para campos. Se o campo não existir, é criado.
$push e $pull (arrays)
// Adicionar ao array
db.clientes.updateOne(
{ _id: id },
{ $push: { tags: "premium" } }
);
// Remover do array
db.clientes.updateOne(
{ _id: id },
{ $pull: { tags: "antigo" } }
);
// Adicionar vários
{ $push: { tags: { $each: ["a", "b"] } } }$push adiciona elemento ao array (cria se não existir). $pull remove todos os elementos que correspondem. $each adiciona múltiplos de uma vez.
updateMany
db.clientes.updateMany(
{ cidade: "Lisboa" },
{ $set: { regiao: "Centro-Sul" } }
);
// Resultado: { matchedCount: 150, modifiedCount: 150 }updateMany() atualiza todos os documentos que correspondem ao filtro. Retorna matchedCount e modifiedCount. Usa filtro vazio {} para atualizar todos.
Upsert
db.clientes.updateOne(
{ email: "ana@mail.com" },
{
$set: { nome: "Ana", visitas: 1 },
$setOnInsert: { criadoEm: new Date() }
},
{ upsert: true }
);upsert: true cria o documento se nenhum corresponder ao filtro. $setOnInsert só é aplicado na criação (não no update). Combinação poderosa para "criar ou atualizar".
$inc (incrementar)
// Somar 10 aos pontos
db.clientes.updateOne(
{ _id: id },
{ $inc: { pontos: 10 } }
);
// Decrementar
db.clientes.updateOne(
{ _id: id },
{ $inc: { stock: -1 } }
);$inc soma um valor ao campo (cria com esse valor se não existir). Aceita negativos para decrementar. Operação atómica — segura em concorrência.
replaceOne
db.clientes.replaceOne(
{ _id: id },
{ nome: "Novo Nome", idade: 25 }
);
// Substitui TUDO (exceto _id)
// Campos não incluídos são removidos!replaceOne() substitui o documento inteiro pelo novo objeto. Campos não incluídos são perdidos. Usa updateOne() com $set para updates parciais.
$unset (remover campo)
// Remover um campo
db.clientes.updateOne(
{ _id: id },
{ $unset: { fax: "" } }
);
// Remover campo aninhado
db.clientes.updateOne(
{ _id: id },
{ $unset: { "morada.andar": "" } }
);$unset remove um campo do documento. O valor ("") é irrelevante. Funciona com campos aninhados via notação de ponto. Não remove o documento.
$rename e $mul
// Renomear campo
db.clientes.updateMany(
{},
{ $rename: { "tel": "telefone" } }
);
// Multiplicar valor
db.produtos.updateOne(
{ _id: id },
{ $mul: { preco: 1.1 } } // +10%
);$rename muda o nome de um campo em todos os documentos. $mul multiplica o valor numérico. Se o campo não existir, $mul cria com valor 0.
Eliminar Documentos
deleteOne
const resultado = db.clientes.deleteOne({ nome: "Ana" });
console.log(resultado.deletedCount); // 1
// Só apaga o PRIMEIRO que corresponde
// mesmo que vários tenham nome "Ana"deleteOne() remove apenas o primeiro documento que corresponde ao filtro. Retorna deletedCount. Para apagar por _id, é sempre único.
Apagar por _id
// Por ObjectId
db.clientes.deleteOne({ _id: ObjectId("65f3a...") });
// Múltiplos IDs
db.clientes.deleteMany({
_id: { $in: [ObjectId("..."), ObjectId("...")] }
});Apagar por _id é a forma mais eficiente — usa o índice primário automaticamente. ObjectId() converte a string. $in permite apagar vários de uma vez.
deleteMany
const resultado = db.clientes.deleteMany({ ativo: false });
console.log(resultado.deletedCount); // N
// Com condição complexa
db.clientes.deleteMany({
$and: [
{ ultimoLogin: { $lt: new Date("2023-01-01") } },
{ plano: "free" }
]
});deleteMany() remove todos os documentos que correspondem. Aceita filtros complexos com $and, $or, operadores de comparação, etc.
Bulk delete (bulkWrite)
db.clientes.bulkWrite([
{ deleteOne: { filter: { nome: "A" } } },
{ deleteOne: { filter: { nome: "B" } } },
{ deleteMany: { filter: { ativo: false } } }
]);bulkWrite() executa múltiplas operações (delete, insert, update) num único pedido. Mais eficiente que chamadas individuais. Reduz round-trips ao servidor.
Apagar todos os documentos
// Remove todos (mantém coleção e índices)
db.clientes.deleteMany({});
// Alternativa mais rápida (remove tudo):
db.clientes.drop();
db.createCollection("clientes");
// ⚠️ drop() também apaga índices!deleteMany({}) limpa dados mas mantém a coleção e os índices. drop() é mais rápido mas remove tudo — terás de recriar índices.
TTL (auto-expiração)
// Documentos expiram após 1 hora
db.sessoes.createIndex(
{ criadoEm: 1 },
{ expireAfterSeconds: 3600 }
);
// Inserir com data
db.sessoes.insertOne({
userId: "u1",
criadoEm: new Date()
});Índices TTL (Time-To-Live) apagam documentos automaticamente após X segundos. O MongoDB verifica a cada 60s. Ideal para sessões, tokens e dados temporários.
findOneAndDelete
const doc = db.clientes.findOneAndDelete(
{ email: "ana@mail.com" },
{ projection: { nome: 1, email: 1 } }
);
// Retorna o documento apagado (ou null)
console.log("Apagado:", doc.nome);findOneAndDelete() remove e retorna o documento numa operação atómica. Útil para filas de processamento. Aceita projection para limitar campos retornados.
Cuidados ao eliminar
// ❌ Perigoso: apaga TUDO sem filtro
db.clientes.deleteMany()
// ✅ Sempre com filtro específico
db.clientes.deleteMany({ _id: id })
// ✅ Verificar antes de apagar
const count = db.clientes.countDocuments({ ativo: false });
print(`Vai apagar ${count} documentos`);Sempre confirma o filtro antes de deleteMany(). Usa countDocuments() primeiro para verificar quantos serão afetados. Em produção, faz backup antes de operações em massa.
Aggregation Pipeline
Pipeline básico
db.clientes.aggregate([
{ $match: { ativo: true } },
{ $sort: { idade: -1 } },
{ $limit: 5 },
{ $project: { nome: 1, idade: 1, _id: 0 } }
]);O aggregate() processa documentos numa sequência de etapas (pipeline). Cada etapa transforma o resultado da anterior. Ordem típica: $match → $sort → $limit → $project.
$lookup (join)
db.pedidos.aggregate([
{ $lookup: {
from: "clientes",
localField: "clienteId",
foreignField: "_id",
as: "cliente"
}},
{ $unwind: "$cliente" }
]);$lookup faz um left outer join com outra coleção. from é a coleção destino, localField/foreignField são as chaves. Resultado é um array — usa $unwind para objeto único.
$lookup (JOIN)
// "JOIN" entre collections:
db.pedidos.aggregate([
{
$lookup: {
from: "clientes", // collection
localField: "cliente_id", // campo local
foreignField: "_id", // campo na outra
as: "cliente" // nome do resultado
}
},
{ $unwind: "$cliente" } // opcional: 1 objeto
]);
// Cada pedido fica com o
// documento do cliente embutido$lookup é o equivalente ao JOIN: liga documentos de duas collections por localField = foreignField. O resultado fica num array (as). Usa $unwind para transformar em objeto único.
$group (agrupar)
db.clientes.aggregate([
{ $group: {
_id: "$cidade",
total: { $sum: 1 },
idadeMedia: { $avg: "$idade" },
maisVelho: { $max: "$idade" }
}}
]);$group agrupa por _id (expressão de agrupamento). $sum: 1 conta. $avg, $min, $max calculam estatísticas. O prefixo $ referencia campos.
$addFields e $set
{ $addFields: {
total: { $multiply: ["$preco", "$quantidade"] },
iva: { $multiply: ["$preco", 0.23] },
dataFormatada: {
$dateToString: { format: "%d/%m/%Y", date: "$criado" }
}
}}$addFields (alias $set) adiciona campos sem remover os existentes. Útil para cálculos intermédios no pipeline. $dateToString formata datas.
$unwind
// Documento com array:
// { nome: "Ana", tags: ["a", "b"] }
db.produtos.aggregate([
{ $unwind: "$tags" }
]);
// Gera 1 documento por elemento:
// { nome: "Ana", tags: "a" }
// { nome: "Ana", tags: "b" }
// Com preserveNullAndEmptyArrays
// mantém docs com array vazio:
{ $unwind: {
path: "$tags",
preserveNullAndEmptyArrays: true
} }$unwind desnormaliza um array: cria um documento por cada elemento. Essencial antes de $group por valores de array. preserveNullAndEmptyArrays: true não descarta documentos sem o array.
$project (remodelar)
{ $project: {
nome: 1,
maiorIdade: { $gte: ["$idade", 18] },
nomeCompleto: { $concat: ["$nome", " ", "$apelido"] },
anoNascimento: { $subtract: [2025, "$idade"] }
}}$project remodela documentos: inclui, exclui, renomeia ou calcula campos. Expressões como $concat, $subtract, $gte criam campos derivados.
$bucket e $facet
// Agrupar em intervalos
{ $bucket: {
groupBy: "$idade",
boundaries: [0, 18, 35, 65, 120],
default: "outro",
output: { total: { $sum: 1 } }
}}
// Múltiplas agregações em paralelo
{ $facet: {
porCidade: [{ $group: { _id: "$cidade", n: { $sum: 1 } } }],
porIdade: [{ $group: { _id: "$idade", n: { $sum: 1 } } }]
}}$bucket agrupa em intervalos numéricos. $facet executa múltiplos pipelines em paralelo sobre os mesmos dados — retorna um objeto com cada resultado.
$unwind (desdobrar arrays)
// Antes: { nome: "Ana", tags: ["a", "b", "c"] }
// Depois: 3 documentos, um por tag
db.clientes.aggregate([
{ $unwind: "$tags" },
{ $group: { _id: "$tags", total: { $sum: 1 } } }
]);
// Conta ocorrências de cada tag$unwind cria um documento por cada elemento do array. Combinado com $group, conta frequências. preserveNullAndEmptyArrays: true mantém docs sem array.
Acumuladores disponíveis
// Em $group: $sum // soma valores (ou $sum: 1 para contar) $avg // média $min // mínimo $max // máximo $first // primeiro valor do grupo $last // último valor do grupo $push // array com todos os valores $addToSet // array com valores únicos $count // contagem (MongoDB 5.0+)
Acumuladores são usados dentro de $group. $push cria array com todos os valores; $addToSet só valores únicos. $count é atalho para $sum: 1.
Índices
Criar índice simples
// Índice ascendente
db.clientes.createIndex({ email: 1 });
// Índice descendente
db.clientes.createIndex({ criadoEm: -1 });
// Com nome personalizado
db.clientes.createIndex({ email: 1 }, { name: "idx_email" });createIndex() cria um índice para acelerar consultas. 1 = ascendente, -1 = descendente. Sem índice, o MongoDB faz collection scan (percorre tudo).
Listar e apagar índices
// Listar todos
db.clientes.getIndexes();
// Apagar por nome
db.clientes.dropIndex("email_1");
// Apagar todos (exceto _id)
db.clientes.dropIndexes();getIndexes() mostra nome, campos e opções de cada índice. dropIndex() remove pelo nome (padrão: campo_ordem). dropIndexes() remove todos exceto o _id.
Índice de texto
// Pesquisa full-text:
db.artigos.createIndex(
{ titulo: "text", corpo: "text" }
);
// Pesquisar:
db.artigos.find(
{ $text: { $search: "mongodb índices" } }
);
// Com score de relevância:
db.artigos.find(
{ $text: { $search: "mongodb" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } });
// Só 1 índice de texto
// por collectionÍndices text permitem pesquisa por palavras em vários campos. Usa $text + $search. $meta: "textScore" dá a relevância para ordenar. Limite: um por collection.
Índice único
db.clientes.createIndex(
{ email: 1 },
{ unique: true }
);
// Permite múltiplos null (sparse):
db.clientes.createIndex(
{ telefone: 1 },
{ unique: true, sparse: true }
);unique: true impede valores duplicados (erro code 11000). sparse: true ignora documentos sem o campo — permite múltiplos null. Essencial para emails, usernames.
Índice parcial
// Só indexa documentos ativos
db.clientes.createIndex(
{ email: 1 },
{
unique: true,
partialFilterExpression: { ativo: true }
}
);partialFilterExpression cria índice só sobre documentos que cumprem a condição. Mais pequeno e rápido que sparse. Útil quando só uma fração dos docs é consultada.
Índice TTL
// Apaga documentos automaticamente
// após X segundos:
db.sessoes.createIndex(
{ criadaEm: 1 },
{ expireAfterSeconds: 3600 } // 1 hora
);
// Documentos com criadaEm mais
// antiga que 1h são removidos
// por um processo em background
// Útil para: sessões, logs,
// tokens, caches temporários
// O campo TEM de ser DateÍndices TTL (time-to-live) apagam documentos automaticamente após expireAfterSeconds. Ideal para sessões, logs e tokens. O campo indexado tem de ser Date; a limpeza corre em background.
Índice composto
db.clientes.createIndex({ cidade: 1, idade: -1 });
// A ordem importa! Este índice serve para:
db.clientes.find({ cidade: "Lisboa" }).sort({ idade: -1 });
// Mas NÃO serve para:
db.clientes.find().sort({ idade: -1 }); // sem cidadeÍndices compostos cobrem múltiplos campos. A ordem segue a regra ESR: Equality → Sort → Range. Consultas devem usar o prefixo do índice para o aproveitar.
explain (análise de queries)
db.clientes.find({ cidade: "Porto" })
.explain("executionStats");
// Campos importantes:
// totalDocsExamined: 0 → usa índice ✅
// totalDocsExamined: 50000 → collection scan ❌
// executionTimeMillis: tempo em ms
// indexName: qual índice foi usadoexplain("executionStats") mostra como a consulta é executada. Se totalDocsExamined for muito maior que nReturned, falta um índice. Ferramenta essencial de otimização.
Índice de texto (text)
db.artigos.createIndex(
{ titulo: "text", conteudo: "text" }
);
// Pesquisar
db.artigos.find(
{ $text: { $search: "mongodb tutorial" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } });Índices text permitem pesquisa full-text. $text + $search faz a consulta. $meta: "textScore" dá relevância. Máximo 1 índice text por coleção.
Regra ESR (ordem de índices)
// Consulta:
db.pedidos.find({ estado: "enviado", total: { $gt: 100 } })
.sort({ data: -1 });
// Índice ideal (ESR):
db.pedidos.createIndex({
estado: 1, // Equality (primeiro)
data: -1, // Sort (segundo)
total: 1 // Range (último)
});Regra ESR: campos de igualdade primeiro, ordenação a seguir, range por último. Maximiza o uso do índice. Nem sempre é possível seguir a 100% — usa explain() para validar.
Dicas e Boas Práticas
mongosh (CLI)
// Ligar mongosh mongosh "mongodb://localhost:27017/loja" mongosh "mongodb+srv://user:pass@cluster.mongodb.net/db" // Executar ficheiro mongosh script.js mongosh --eval "db.clientes.countDocuments()"
mongosh é o shell moderno do MongoDB. Suporta JavaScript completo, autocomplete e --eval para comandos rápidos. Substitui o antigo mongo shell.
Transações multi-documento
const session = client.startSession();
session.startTransaction();
try {
await db.collection("contas").updateOne(
{ _id: "A" }, { $inc: { saldo: -50 } }, { session }
);
await db.collection("contas").updateOne(
{ _id: "B" }, { $inc: { saldo: 50 } }, { session }
);
await session.commitTransaction();
} catch (e) {
await session.abortTransaction();
}Transações garantem atomicidade em múltiplos documentos/collections. Requer replica set ou sharded cluster. Usa session em todas as operações. Mais lento que operações simples.
Backup e restore
# Backup completo (binário): mongodump --uri="mongodb://localhost:27017" # Só uma base de dados: mongodump --db=loja --out=/backups # Restore: mongorestore --uri="mongodb://localhost:27017" /backups # Exportar/importar JSON: mongoexport --db=loja --collection=clientes \ --out=clientes.json mongoimport --db=loja --collection=clientes \ --file=clientes.json # mongodump = binário (rápido) # mongoexport = JSON (legível)
mongodump/mongorestore fazem backup binário (rápido e completo). mongoexport/mongoimport usam JSON (legível, para migrações). Faz dumps regulares e testa o restore.
ObjectId
// Gerar novo
new ObjectId()
// Extrair data de criação
ObjectId("65f3a...").getTimestamp()
// ISODate("2024-03-14T...")
// Comparar
ObjectId("65f...").equals(outroId)ObjectId tem 12 bytes: timestamp (4) + máquina (3) + PID (2) + contador (3). getTimestamp() extrai a data de criação sem campo extra. Ordenável cronologicamente.
Performance (dicas)
// ✅ Indexar campos de filtro e sort
db.c.createIndex({ campo: 1 });
// ✅ Projeção: buscar só campos necessários
db.c.find({}, { nome: 1, _id: 0 });
// ✅ limit() para não carregar tudo
db.c.find().limit(20);
// ❌ Evitar regex sem índice
// ❌ Evitar skip() em datasets grandes
// ❌ Evitar documentos > 1 MBIndexa campos de find() e sort(). Usa projeção para reduzir dados transferidos. limit() evita carregar coleções inteiras. Monitoriza com explain().
Replica set e sharding
// Replica set = cópias dos dados
// (alta disponibilidade):
// 1 primário + N secundários
// failover automático
// Iniciar replica set:
rs.initiate()
rs.status()
rs.add("host2:27017")
// Sharding = dividir dados por
// máquinas (escala horizontal):
// shard key define a divisão
sh.enableSharding("loja")
sh.shardCollection(
"loja.pedidos", { cliente_id: 1 }
)
// Replica = disponibilidade
// Shard = capacidade/escalaReplica set replica dados em vários nós (failover automático, leituras nos secundários). Sharding divide os dados por máquinas usando uma shard key. Replica set para disponibilidade, sharding para escala horizontal.
Embed vs Reference
// EMBED (1:poucos, acedidos juntos)
{
nome: "Ana",
morada: { rua: "X", cidade: "Porto" }
}
// REFERENCE (1:muitos, acedidos separados)
{
nome: "Ana",
pedidos: [ObjectId("..."), ObjectId("...")]
}Embed para dados pequenos e acedidos em conjunto (1:1, 1:poucos). Reference para relações grandes ou acedidas separadamente (1:muitos). Limite: documento até 16 MB.
Replica Set (conceitos)
// Iniciar replica set (3 nós)
mongod --replSet rs0 --port 27017
mongod --replSet rs0 --port 27018
mongod --replSet rs0 --port 27019
// Inicializar
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019" }
]
});Um Replica Set tem um primary (escritas) e secondaries (leituras/failover). Garante alta disponibilidade. Necessário para transações e recomendado em produção.
Backup e restauro
// Backup completo mongodump --db loja --out /backup/ // Restauro mongorestore --db loja /backup/loja // Exportar/Importar (JSON) mongoexport --db loja --collection clientes --out clientes.json mongoimport --db loja --collection clientes --file clientes.json
mongodump/mongorestore fazem backup binário (BSON). mongoexport/mongoimport usam JSON/CSV — úteis para migrações e debugging. Agenda backups regulares.
Comandos de administração
// Estado do servidor
db.serverStatus()
// Operações em curso
db.currentOp()
// Matar operação lenta
db.killOp(opId)
// Profiling (registar queries lentas)
db.setProfilingLevel(1, { slowms: 100 });
db.getProfilingLevel();serverStatus() dá métricas do servidor. currentOp() mostra operações ativas. setProfilingLevel(1) regista queries acima de X ms na coleção system.profile.