Mongoshell (mongosh) | MongoDB | Banco de dados
- Mongoshell (mongosh) | MongoDB | Banco de dados
Documentação: Instalação do Mongosh (Windows e Linux) + Principais Comandos
O que é o Mongosh
O mongosh (MongoDB Shell) é o terminal oficial do MongoDB para conectar em servidores MongoDB e executar consultas, rotinas administrativas e comandos de banco.
1) Pré-requisitos
Para usar o mongosh, você precisa:
- Um servidor MongoDB disponível (local ou remoto)
- Rede liberada para conexão (porta padrão: 27017)
- Credenciais (se o ambiente exigir autenticação)
2) Instalação do Mongosh no Windows
2.1) Instalar via instalador oficial (MSI)
- Acesse a página oficial de downloads do MongoDB
- Baixe o MongoDB Shell (mongosh) para Windows (
.msi) - Execute o instalador
- Abra o PowerShell e valide:
mongosh --version
2.2) Ajustar PATH (se o comando não funcionar)
Se o Windows retornar que o comando não existe:
-
Localize o executável do mongosh, exemplo:
C:\Program Files\MongoDB\mongosh\bin\mongosh.exe
-
Vá em:
- Configurações avançadas do sistema → Variáveis de Ambiente
-
Adicione a pasta do
mongosh.exena variável Path -
Feche e abra o terminal novamente e teste:
mongosh --version
3) Instalação do Mongosh no Linux
3.1) Ubuntu/Debian (repositório)
- Atualize os pacotes:
sudo apt update
- Instale dependências comuns:
sudo apt install -y wget gnupg
- Instale o mongosh via repositório do MongoDB (recomendado no Linux)
Os comandos exatos podem variar pela sua versão do sistema operacional.
- Valide a instalação:
mongosh --version
3.2) Instalação via pacote .deb (alternativa)
- Baixe o pacote do mongosh para Debian/Ubuntu (
.deb) - Instale com:
sudo dpkg -i mongosh*.deb
sudo apt -f install -y
- Valide:
mongosh --version
3.3) RHEL/CentOS/Fedora (via .rpm)
- Baixe o pacote do mongosh (
.rpm) - Instale com:
sudo rpm -i mongosh*.rpm
ou:
sudo dnf install -y mongosh*.rpm
- Valide:
mongosh --version
4) Conexões no Mongosh
4.1) Conectar localmente (padrão)
mongosh
4.2) Conectar em host e porta
mongosh "mongodb://localhost:27017"
4.3) Conectar com usuário e senha
mongosh "mongodb://USUARIO:SENHA@HOST:27017"
4.4) Conectar em um database específico
mongosh "mongodb://localhost:27017/meu_database"
4.5) Conectar com authSource (usuário no admin)
mongosh "mongodb://USUARIO:SENHA@HOST:27017/meu_database?authSource=admin"
4.6) Conectar no MongoDB Atlas (SRV)
mongosh "mongodb+srv://USUARIO:SENHA@cluster0.xxxxx.mongodb.net/meu_database"
5) Comandos Principais do Mongosh
5.1) Ajuda e utilitários
- Ajuda geral do shell:
help
- Ajuda do database atual:
db.help()
- Ajuda de uma collection específica:
db.<collection_name>.help()
- Ver versão do MongoDB (servidor conectado):
db.version()
- Sair do mongosh:
exit
6) Navegação entre databases
6.1) Listar bancos disponíveis
show databases
6.2) Ver qual database está em uso
db
6.3) Trocar/criar database
use <database_name>
Exemplo:
use admin
6.4) Apagar o database atual
db.dropDatabase()
⚠️ Remove o banco inteiro.
7) Collections
7.1) Listar collections do database atual
show collections
7.2) Criar collection manualmente
db.createCollection("<collection_name>")
Exemplo:
db.createCollection("users")
7.3) Renomear collection
db.<collection_name>.renameCollection("<new_collection_name>")
Exemplo:
db.users.renameCollection("usuarios")
7.4) Apagar collection
db.<collection_name>.drop()
Exemplo:
db.usuarios.drop()
8) CRUD (Create, Read, Update, Delete)
8.1) Inserir documentos
Inserir um documento (insertOne)
O comando db.users.insertOne({}) é aceitável e irá criar um id automatico
db.<collection_name>.insertOne({ <document> })
Exemplo:
db.users.insertOne({ name: "John", age: 30 })
Inserir vários documentos (insertMany)
db.collection.insertMany(
[ <documento1>, <documento2>, ... ],
{
ordered: <boolean>,
writeConcern: <document>,
bypassDocumentValidation: <boolean>,
comment: <any>
}
)
| Opção | Tipo | Padrão | Para que serve |
|---|---|---|---|
ordered | boolean | true | Define se o MongoDB deve inserir em ordem e parar no primeiro erro |
writeConcern | document | configuração da conexão/collection | Define o nível de confirmação da escrita |
bypassDocumentValidation | boolean | false | Ignora validações de schema da collection |
comment | qualquer valor BSON | — | Adiciona comentário para rastrear a operação em logs/profiler |
Exemplo:
db.users.insertMany([
{ name: "Alice", age: 25 },
{ name: "Bob", age: 35 }
])
- OBS: Para obter os ids retornados, utilize a propriedade do resultado chamada
insertdIds
db.orders.insertMany([
{ customer: "John Smith", product: "Book" },
{ customer: "Jane Doe", product: "Laptop" }
]).insertedIds
Inserir por bulk-write
db.sales.bulkWrite([
// 1. Inserir um documento
{
insertOne: {
document: {
item: "notebook",
quantity: 5,
price: 3500,
category: "electronics"
}
}
},
// 2. Inserir outro documento
{
insertOne: {
document: {
item: "mouse",
quantity: 20,
price: 50,
category: "electronics"
}
}
},
// 3. Atualizar um documento existente
{
updateOne: {
filter: { item: "mouse" },
update: {
$inc: { quantity: 10 }, // aumenta quantidade
$set: { updatedAt: new Date() }
}
}
},
// 4. Atualizar vários documentos
{
updateMany: {
filter: { category: "electronics" },
update: {
$mul: { price: 1.1 } // aumenta preço em 10%
}
}
},
// 5. Deletar um documento
{
deleteOne: {
filter: { item: "old-keyboard" }
}
},
// 6. Deletar vários documentos
{
deleteMany: {
filter: { quantity: { $lte: 0 } }
}
},
// 7. Replace (substitui completamente o documento)
{
replaceOne: {
filter: { item: "notebook" },
replacement: {
item: "notebook",
quantity: 8,
price: 3400,
category: "electronics",
replaced: true
}
}
}
],
{
ordered: false // executa em paralelo (melhor performance)
});
8.2) Consultar documentos
Buscar um documento (findOne)
db.collection.findOne(
<filtro>,
<projecao>,
{
sort: <document>,
maxTimeMS: <number>,
collation: <document>,
comment: <any>,
readConcern: <document>
}
)
O findOne() NÃO retorna um cursor, ele traz o documento direto (ou null).
Por isso, projeção e ordenação vão no segundo parâmetro (Objeto de Opções).
| Opção | Tipo | Padrão | Para que serve |
|---|---|---|---|
filtro | document | {} | Define quais documentos podem ser encontrados |
projecao | document | retorna todos os campos | Define quais campos devem aparecer ou ser ocultados no resultado |
sort | document | ordem natural da collection | Define a ordenação antes de escolher o primeiro documento |
maxTimeMS | number | sem limite explícito | Define tempo máximo de execução em milissegundos |
collation | document | collation padrão da collection | Define regras de comparação de strings, como case-insensitive e idioma |
comment | qualquer valor BSON | — | Adiciona comentário para rastrear a operação em logs/profiler |
readConcern | document | configuração da conexão | Define o nível de consistência/leitura esperado na operação |
Exemplo:
db.users.findOne(
{ status: "ACTIVE" },
{ name: 1, email: 1, _id: 0 },
{
sort: { createdAt: -1 },
maxTimeMS: 1000,
comment: "buscar_usuario_ativo_mais_recente"
}
)
Esse comando busca um único usuário ativo, retornando apenas name e email, ordenando pelo createdAt mais recente antes de escolher o primeiro documento.
O MongoDB trata findOne como uma operação de leitura dentro das operações CRUD.
Buscar vários documentos (find)
find com valores em um array segue a seguinte lógica:
- ordem igual
- tamanho igual
- valores iguais ❗ Ordem importa
db.<collection_name>.find({ <query> })
Exemplo:
db.users.find({ age: { $gt: 25 } })
Projeção (retornar apenas campos específicos)
db.<collection_name>.find(
{ <query> },
{ campo1: 1, campo2: 1, _id: 0 }
)
Exemplo:
db.users.find(
{ age: { $gt: 18 } },
{ name: 1, age: 1, _id: 0 }
)
Ordenação (sort)
db.<collection_name>.find().sort({ <field>: 1 })
Exemplo (ascendente):
db.users.find().sort({ age: 1 })
Exemplo (descendente):
db.users.find().sort({ age: -1 })
Limitar resultados (limit)
db.<collection_name>.find().limit(<n>)
Exemplo:
db.users.find().limit(5)
Paginação (skip + limit)
db.<collection_name>.find().skip(<n>).limit(<n>)
Exemplo:
db.users.find().skip(10).limit(5)
Contar documentos (recomendado)
db.<collection_name>.countDocuments()
Com filtro:
db.<collection_name>.countDocuments({ <query> })
Exemplo:
db.users.countDocuments({ age: { $gt: 25 } })
Tabelas de operadores | mongoshell
- Operadores de Data (Aggregation)
| Operador | Descrição | Exemplo |
|---|---|---|
$year | Retorna o ano da data | { $project: { ano: { $year: "$data" } } } |
$month | Retorna o mês (1–12) | { $project: { mes: { $month: "$data" } } } |
$dayOfMonth | Retorna o dia do mês | { $project: { dia: { $dayOfMonth: "$data" } } } |
$dayOfWeek | Retorna o dia da semana (1–7) | { $project: { diaSemana: { $dayOfWeek: "$data" } } } |
$dayOfYear | Retorna o dia do ano | { $project: { diaAno: { $dayOfYear: "$data" } } } |
$hour | Retorna a hora | { $project: { hora: { $hour: "$data" } } } |
$minute | Retorna os minutos | { $project: { min: { $minute: "$data" } } } |
$second | Retorna os segundos | { $project: { seg: { $second: "$data" } } } |
$millisecond | Retorna milissegundos | { $project: { ms: { $millisecond: "$data" } } } |
$dateAdd | Soma tempo a uma data | { $addFields: { nova: { $dateAdd: { startDate: "$data", unit: "day", amount: 5 } } } } |
$dateSubtract | Subtrai tempo de uma data | { $addFields: { nova: { $dateSubtract: { startDate: "$data", unit: "day", amount: 5 } } } } |
$dateDiff | Diferença entre datas | { $addFields: { dias: { $dateDiff: { startDate: "$inicio", endDate: "$fim", unit: "day" } } } } |
$dateTrunc | Trunca a data (ex: início do dia/mês) | { $addFields: { trunc: { $dateTrunc: { date: "$data", unit: "month" } } } } |
$toDate | Converte para tipo Date | { $addFields: { data: { $toDate: "$stringData" } } } |
$dateFromString | Converte string para Date | { $addFields: { data: { $dateFromString: { dateString: "$dataStr" } } } } |
$dateToString | Converte Date para string | { $project: { data: { $dateToString: { format: "%Y-%m-%d", date: "$data" } } } } |
$dateFromParts | Cria uma data a partir de partes | { $addFields: { data: { $dateFromParts: { year: 2026, month: 4, day: 23 } } } } |
$dateToParts | Quebra a data em partes | { $project: { partes: { $dateToParts: { date: "$data" } } } } |
$isoWeek | Retorna semana ISO | { $project: { semana: { $isoWeek: "$data" } } } |
$isoDayOfWeek | Dia da semana ISO (1–7) | { $project: { dia: { $isoDayOfWeek: "$data" } } } |
$isoWeekYear | Ano ISO | { $project: { anoIso: { $isoWeekYear: "$data" } } } |
- Operadores de Comparação
| Operador | Descrição | Exemplo |
|---|---|---|
$eq | Igual | { idade: { $eq: 18 } } |
$ne | Diferente | { idade: { $ne: 18 } } |
$gt | Maior que | { idade: { $gt: 18 } } |
$gte | Maior ou igual | { idade: { $gte: 18 } } |
$lt | Menor que | { idade: { $lt: 18 } } |
$lte | Menor ou igual | { idade: { $lte: 18 } } |
$in | Valor dentro de array | { status: { $in: ["A", "B"] } } |
$nin | Valor fora de array | { status: { $nin: ["A", "B"] } } |
O uso do operador $in nem sempre é necessário, se houver um array e você quiser validar se tem apenas um valor lá dentro, você pode ir direto na seguinte query:
ONDE tags É UM CAMPO ARRAY
db.products.find({
"category": { "$in": ["Electronics", "Clothing"] },
"tags": "bestseller"
})
- obs: **o `$in` valida se pelo menos existe um dos valores informados**
- Operadores Lógicos
| Operador | Descrição | Exemplo |
|---|---|---|
$and | E lógico | { $and: [ { idade: { $gt: 18 } }, { ativo: true } ] } |
$or | Ou lógico | { $or: [ { idade: { $lt: 18 } }, { vip: true } ] } |
$not | Nega condição | { idade: { $not: { $gt: 18 } } } |
$nor | Nenhuma condição verdadeira | { $nor: [ { idade: 18 }, { ativo: false } ] } |
Para fazer um between no Mongo: db.getCollection("movies").find({ runtime: { $gte: 90, $lte: 120 }})
- Operadores de Elemento
| Operador | Descrição | Exemplo |
|---|---|---|
$exists | Campo existe | { email: { $exists: true } } |
$type | Tipo do campo | { idade: { $type: "int" } } |
- Operadores de Avaliação
| Operador | Descrição | Exemplo | Observação |
|---|---|---|---|
$regex | Expressão regular | { nome: { $regex: "^A" } } | Busca por padrão de texto simples. Pode usar índice em alguns casos, principalmente prefixado. |
$expr | Permite usar expressões de agregação dentro do find | { $expr: { $gt: ["$saldo", "$limite"] } } | Útil para comparar campos do mesmo documento. |
$mod | Busca por resto de divisão | { idade: { $mod: [5, 0] } } | Retorna idades divisíveis por 5. |
$text | Busca textual tradicional do MongoDB | { $text: { $search: "mongodb" } } | Usa text index comum, criado com createIndex({ campo: "text" }). |
$where | Executa JavaScript customizado | { $where: "this.idade > 18" } | Evite. Pode ser lento e perigoso. Prefira operadores nativos. |
- Diferença entre
$texte$search
| Recurso | Onde usa | Índice usado | Exemplo | Quando usar |
|---|---|---|---|---|
$text | Dentro do find() ou $match | Text index comum do MongoDB | db.posts.find({ $text: { $search: "mongodb" } }) | Busca textual simples. |
$search | Dentro do aggregate() | Atlas Search index | db.posts.aggregate([{ $search: { text: { query: "mongodb", path: "title" } } }]) | Busca avançada no Atlas: relevância, autocomplete, fuzzy, highlight, facets etc. |
$regex | Dentro do find() | Índice comum, dependendo do padrão | db.posts.find({ title: { $regex: "^mongo" } }) | Busca por padrão específico, não full-text search. |
Para usar o $text e sua propriedade $search. Primeiro deve criar um index
db.posts.createIndex({ title: "text", content: "text" })
$expr permite usar expressões no filtro da query, como comparar campo com campo ou campo com cálculo.
Exemplo: { $expr: { $gt: ["$saldo", "$limite"] } } -> Saldo é maior que o limite
- Operadores de Array
| Operador | Tipo | Descrição | Exemplo |
|---|---|---|---|
$all | Query Operator | Retorna documentos cujo array contém todos os valores especificados no all. A ordem dos elementos não importa. | { tags: { $all: ["js", "node"] } } |
$elemMatch | Query Operator | Retorna documentos onde pelo menos um elemento do array satisfaz múltiplas condições simultaneamente. - Quando você precisa validar múltiplas condições no mesmo elemento de um array. | { notas: { $elemMatch: { $gt: 7, $lt: 9 } } } |
$size | Query Operator | Filtra documentos cujo array possui exatamente o tamanho informado. | { tags: { $size: 3 } } |
$unwind | Aggregation Stage | Desconstrói um array, criando um novo documento para cada elemento do array. | db.movies.aggregate([{ $unwind: "$genres" }]) |
- Observações sobre o
$unwind
| Situação | Resultado |
|---|---|
| Array vazio | ❌ some |
| Campo inexistente | ❌ some |
null | ❌ some |
| Não-array | ✅ vira 1 doc |
Com preserveNullAndEmptyArrays: true | ✅ mantém |
| Valores duplicados | ✅ mantém |
{
_id: 1,
lname: 'Smith',
tech_stack: [ 'sql', 'git', 'python', 'django' ],
fname: 'Bob'
},
{
_id: 2,
fname: 'Michael',
lname: 'Doe',
tech_stack: [ 'git', 'python', 'linux', 'flask' ]
}
{ $unwind: { path: '$tech_stack' }}
// vira 8 documentos
- Operadores de Atualização
| Operador | Descrição | Exemplo |
|---|---|---|
$set | Define/atualiza valor - cria o campo se ele não existir. | { $set: { nome: "Carlos" } } |
$unset | Remove campos do documento. Funciona com campos aninhados, pode ser usado em updates e aggregation (stage), e é equivalente a $project quando usado para excluir campos | { $unset: { idade: "" } } -> pode ser usado "" e 1 |
$inc | Incrementa - você passa o valor que deve ser incrementado no valor atual do campo | { $inc: { saldo: 100 } } |
$mul | Multiplica - você passa o valor que deve ser multiplicado no valor atual do campo | { $mul: { saldo: 2 } } |
$rename | Renomeia campo | { $rename: { nome: "nomeCompleto" } } |
$setOnInsert | Define apenas no insert (upsert) | { $setOnInsert: { criadoEm: new Date() } } |
$min | Atualiza se valor atual for maior | { $min: { idade: 18 } } |
$max | Atualiza se valor atual for menor | { $max: { idade: 65 } } |
$currentDate | Atualiza com data atual | { $currentDate: { atualizadoEm: true } } |
Sobre o $unset
| Afirmação | Correta | Explicação |
|---|---|---|
| Não lança erro se o campo não existir | Sim | $unset ignora campos inexistentes, sem falhar |
Pode ser usado com aggregate e updateMany | Sim | Funciona tanto em pipelines quanto em operações de update |
Pode ser equivalente ao $project ao remover/excluir campo | Sim | $project pode excluir campos (0), similar ao efeito do $unset |
| Remove/exclui campos dos documentos | Sim | Função principal do $unset é remover campos |
Sobre o $setOnInsert
db.inventory.updateOne(
{ item: "notebook" },
{ $inc: { quantity: 10 }, $setOnInsert: { price: 5.99 } },
{ upsert: true }
)
Esse comando faz o seguinte, altera o documento e incrementa 10 na quantidade, caso o campo não exista, eu especifico um preço que eu quero caso precise inserir o campo quantidade
Sobre o upsert
No UpdateOne, o upsert: true garante isto:
Se nenhum documento for encontrado pelo filtro, o MongoDB cria um novo documento usando a combinação do filtro + os operadores de update aplicáveis. Ex:
db.users.updateOne(
{ email: "ana@email.com" },
{
$set: {
nome: "Ana",
ativo: true
}
},
{ upsert: true }
)
- Se já existir, atualiza para:
{ email: "ana@email.com", nome: "Ana", ativo: true }
- Se não:
{_id: ObjectId("..."), email: "ana@email.com", nome: "Ana", ativo: true}
- Operadores de Array (Update)
| Operador | Descrição | Exemplo |
|---|---|---|
$push | Adiciona no array (pode duplicar valores) | { $push: { tags: "novo" } } |
$addToSet | Adiciona se não existir | { $addToSet: { tags: "novo" } } |
$pull | Remove do array | { $pull: { tags: "antigo" } } |
$pop | Remove primeiro ou último. Use 1 ou -1 | { $pop: { tags: 1 } } |
$pullAll | Remove múltiplos valores | { $pullAll: { tags: ["a", "b"] } } |
$each | Modificador para múltiplos valores | { $push: { tags: { $each: ["a", "b"] } } } |
$position | Define posição no push | { $push: { tags: { $each: ["x"], $position: 0 } } } |
$slice | Limita tamanho do array | { $push: { tags: { $each: ["x"], $slice: 5 } } } |
$ | Atualiza o primeiro elemento que corresponde | { $set: { "tags.$": "novo" } } |
$[] | Atualiza todos os elementos do array | { $set: { "tags.$[]": "novo" } } |
$[<id>] | Atualiza elementos com filtro | { $set: { "tags.$[elem]": "novo" } } + arrayFilters |
$slice
Update (controle de tamanho do array):
db.posts.updateOne(
{ _id: 1 },
{
$push: {
comments: {
$each: [novoComentario],
$slice: -100
}
}
}
)
- adiciona o novo comentário
- mantém apenas os últimos 100
- evita crescimento infinito do array
Find (projeção de array):
db.posts.find(
{},
{ comments: { $slice: 3 } }
)
Formas do $slice no find:
$slice: 3→ primeiros 3$slice: -3→ últimos 3$slice: [1, 2]→ pula 1, pega 2
⚠️ Só funciona na projeção, não no filtro
- Operadores de Condição
| Operador | Descrição | Exemplo |
|---|---|---|
$cond | Executa uma condição estilo if / else | { $cond: [ { $gte: ["$nota", 7] }, "Aprovado", "Reprovado" ] } |
$ifNull | Retorna um valor alternativo quando o campo é null ou não existe | { $ifNull: ["$apelido", "$nome"] } |
$switch | Executa múltiplas condições, parecido com switch/case ou if/else if | { $switch: { branches: [...], default: "Outro" } } |
-
Pode ser usado dentro de
$project,$addFields,$set,$group, entre outros stages. -
Operadores de Projeção
| Operador | Descrição | Exemplo |
|---|---|---|
$ | Primeiro elemento que bate na query | { "notas.$": 1 } |
$elemMatch | Projeta elemento específico | { notas: { $elemMatch: { $gt: 7 } } } |
$meta | Metadado (ex: score texto) | { score: { $meta: "textScore" } } |
- Operadores Match
| Operador | Descrição | Exemplo |
|---|---|---|
$match | Filtra documentos no pipeline (equivalente ao WHERE). Deve ser usado o mais cedo possível no pipeline. Possui sintaxe semelhante ao find() e pode ser usado múltiplas vezes. | { $match: { status: "A" } } |
$elemMatch | Garante que múltiplas condições sejam aplicadas no mesmo item do array | { notas: { $elemMatch: { $gt: 7, $lt: 10 } } } |
-
Outros
- Distinct:
db.getCollection("movies").distinct("languages")
- Distinct:
8.3) Atualizar documentos
Atualizar um documento (updateOne)
db.<collection_name>.updateOne(
{ <query> },
{ $set: { <field>: <value> } }
)
Exemplo:
db.users.updateOne(
{ name: "John" },
{ $set: { age: 31 } }
)
Cuidade, usar o updateOne sem o $set faz com que atualize o documento inteiro. Sem $set vira Replacement update.
Sem o set antes de atualizar:
db.users.updateOne(
{ _id: 1 },
{ name: "Carlos" }
)
{
_id: 1,
name: "João",
age: 30,
email: "joao@email.com",
active: true
}
Depois de atualizar sem o set
{
_id: 1,
name: "Carlos",
}
o _id é preservado
Métodos find and modify
| Método | Status | Operação | Retorno padrão | Opção para retornar atualizado | Uso recomendado |
|---|---|---|---|---|---|
findAndModify | ❌ Legado | find + update/delete | Documento antes da alteração | new: true | Evitar (compatibilidade apenas) |
findOneAndUpdate | ✅ Atual | find + update | Documento antes | returnDocument: "after" | ✔️ Principal uso |
findOneAndReplace | ✅ Atual | find + replace | Documento antes | returnDocument: "after" | ✔️ Substituição total |
findOneAndDelete | ✅ Atual | find + delete | Documento deletado | ❌ Não aplicável | ✔️ Remoção controlada |
- Exemplo
findAndModify
db.medications.findAndModify({ query: { status: "in-stock" }, update: { $set: { status: "high-demand" }, $inc: { stock: 10 } }})
Repare que é necessário especificar a palavra query e update o que nos métodos mais atuais não é necessário. Esse comando é genêrico e aceita várias operações, como:
{
query: ..., // qual documento encontrar
sort: ..., // se encontrar vários, qual escolher
update: ..., // como atualizar
remove: ..., // ou remover
new: ..., // retornar antes ou depois
upsert: ...
}
db.orders.findAndModify({
query: { status: "Pending" },
update: { $set: { status: "Completed" } }
})
O método findAndModify realiza a busca e a alteração atomicamente evitando concorrência. De outra forma nós teriamos que realizar o find() e depois o update()
A propriedade new significa:
new: false → retorna o documento antes da alteraçãonew: true → retorna o documento depois da alteração
CUIDADO
db.orders.findAndModify({
query: { _id: 1 },
update: { status: "completed", quantity: 60 },
new: true
});
-
Dessa forma, o update não está usando nenhum operador como o
$sete por isso da forma que está ele vai transformar o documente deixando apenas os dois campos que está dentro do update. Exemplo: -
Antes
{
_id: 1,
customer: "João",
status: "pending",
quantity: 10,
total: 500
}
- Depois
{
_id: 1,
status: "completed",
quantity: 60
}
Atualizar vários documentos (updateMany)
db.collection.updateMany(
<filter>,
<update>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string>,
let: <document>,
comment: <any>
}
)
db.students.updateMany(
{},
{ $inc: { "exams.$[elem].score": 5 } },
{ arrayFilters: [ { "elem.score": { $lt: 90 } } ] }
);
// {} -> Busca todos os elementos
// { $inc: { "exams.$[elem].score": 5 } } -> $[elem] em cada elemento do array
// { arrayFilters: [ { "elem.score": { $lt: 90 } } ] } -> array filter
| Opção | Tipo | Padrão | Para que serve |
|---|---|---|---|
upsert | boolean | false | Se não encontrar documento, cria um novo |
writeConcern | document | configuração da conexão/collection | Define o nível de confirmação da escrita |
collation | document | — | Define regras de comparação de string, como case-insensitive |
arrayFilters | array | — | Filtra quais itens de um array serão atualizados |
hint | document/string | — | Força o uso de um índice específico |
let | document | — | Declara variáveis para usar na operação |
comment | qualquer valor BSON | — | Adiciona comentário para profiler/logs |
Exemplo:
db.users.updateMany(
{ active: { $exists: false } },
{ $set: { active: true } }
)
Substituir documento inteiro (replaceOne)
db.collection.replaceOne(
<filtro>,
<novo_documento>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
hint: <document|string>,
let: <document>,
sort: <document>
}
)
| Opção | Tipo | Padrão | Para que serve |
|---|---|---|---|
filtro | document | obrigatório | Define qual documento será substituído |
novo_documento | document | obrigatório | Novo documento que substituirá o documento encontrado |
upsert | boolean | false | Cria um novo documento se nenhum documento bater com o filtro |
writeConcern | document | configuração da conexão/collection | Define o nível de confirmação da escrita |
collation | document | collation padrão da collection | Define regras de comparação de strings, como idioma e case-insensitive |
hint | document ou string | — | Força o uso de um índice específico |
let | document | — | Define variáveis que podem ser usadas na operação |
sort | document | ordem natural da collection | Define qual documento substituir quando mais de um documento bater no filtro |
Exemplo:
db.users.replaceOne(
{ name: "John" },
{ name: "John", age: 31, active: true },
{
upsert: false,
writeConcern: { w: "majority" },
comment: "substituir_usuario_john"
}
)
Ponto importante:
db.users.replaceOne(
{ name: "John" },
{ age: 31 }
)
Nesse caso, o documento antigo será substituído por:
{
_id: ObjectId("..."),
age: 31
}
Ou seja: campos antigos como name, email, active, etc. serão removidos se não estiverem no novo documento.
findOneAndDelete
O findOneAndDelete busca um documento, remove esse documento da collection e retorna o documento que foi removido.
Ele é útil quando você precisa deletar e ainda saber exatamente qual documento foi excluído.
db.<collection_name>.findOneAndDelete(
{ <filtro> },
{
projection: <document>,
sort: <document>,
maxTimeMS: <number>,
collation: <document>
}
)
| Opção | Tipo | Padrão | Para que serve |
|---|---|---|---|
filtro | document | obrigatório | Define qual documento será encontrado e removido |
projection | document | todos os campos | Define quais campos retornar do documento removido |
sort | document | ordem natural | Define qual documento remover se vários baterem com o filtro |
maxTimeMS | number | sem limite explícito | Define tempo máximo de execução em milissegundos |
collation | document | collation padrão da collection | Define regras de comparação de strings, como idioma e case-insensitive |
Exemplo:
db.users.findOneAndDelete(
{ name: "John" }
)
Nesse caso, o MongoDB:
- procura o primeiro usuário com
name: "John"; - remove esse documento;
- retorna o documento removido.
Exemplo com sort
db.sessions.findOneAndDelete(
{ userId: 1, status: "expired" },
{
sort: { createdAt: 1 }
}
)
Esse exemplo remove a sessão expirada mais antiga do usuário.
Exemplo com projection
db.users.findOneAndDelete(
{ email: "john@email.com" },
{
projection: {
name: 1,
email: 1,
_id: 0
}
}
)
Remove o documento, mas retorna apenas name e email.
C# Driver
var filter = Builders<User>.Filter.Eq(x => x.Name, "John");
var deletedUser = await collection.FindOneAndDeleteAsync(filter);
Com sort:
var filter = Builders<Session>.Filter.And(
Builders<Session>.Filter.Eq(x => x.UserId, 1),
Builders<Session>.Filter.Eq(x => x.Status, "expired")
);
var options = new FindOneAndDeleteOptions<Session>
{
Sort = Builders<Session>.Sort.Ascending(x => x.CreatedAt)
};
var deletedSession = await collection.FindOneAndDeleteAsync(filter, options);
Diferença rápida
| Método | Remove documento? | Retorna documento removido? | Uso principal |
|---|---|---|---|
deleteOne | Sim | Não | Remover um documento |
deleteMany | Sim | Não | Remover vários documentos |
findOneAndDelete | Sim | Sim | Remover e obter o documento removido |
findOneAndUpdate
O findOneAndUpdate busca um documento, aplica uma atualização e pode retornar o documento antes ou depois da alteração.
Ele é útil quando você precisa fazer uma atualização e já obter o documento atualizado na mesma operação.
db.<collection_name>.findOneAndUpdate(
{ <filtro> },
{ <update> },
{
returnDocument: "before" | "after",
upsert: <boolean>,
projection: <document>,
sort: <document>
}
)
| Opção | Tipo | Padrão | Para que serve |
|---|---|---|---|
filtro | document | obrigatório | Define qual documento será encontrado |
update | document | obrigatório | Define a alteração a ser aplicada |
returnDocument | string | "before" | Define se retorna o documento antes ou depois da alteração |
upsert | boolean | false | Cria o documento se nenhum for encontrado |
projection | document | todos os campos | Define quais campos retornar |
sort | document | ordem natural | Define qual documento atualizar se vários baterem com o filtro |
Exemplo:
db.users.findOneAndUpdate(
{ name: "John" },
{
$set: {
age: 31,
active: true
}
},
{
returnDocument: "after"
}
)
Nesse caso, o MongoDB:
- procura o usuário com
name: "John"; - atualiza
ageeactive; - retorna o documento depois da alteração.
Exemplo com upsert
db.users.findOneAndUpdate(
{ email: "john@email.com" },
{
$set: {
name: "John",
active: true
}
},
{
upsert: true,
returnDocument: "after"
}
)
Se não existir usuário com esse email, o MongoDB cria um novo documento.
C# Driver
var filter = Builders<User>.Filter.Eq(x => x.Name, "John");
var update = Builders<User>.Update
.Set(x => x.Age, 31)
.Set(x => x.Active, true);
var options = new FindOneAndUpdateOptions<User>
{
ReturnDocument = ReturnDocument.After
};
var user = await collection.FindOneAndUpdateAsync(
filter,
update,
options
);
Diferença rápida
| Método | Retorna documento? | Uso principal |
|---|---|---|
updateOne | Não | Atualizar um documento |
findOneAndUpdate | Sim | Atualizar e obter o documento |
replaceOne | Não | Substituir o documento inteiro |
findOneAndReplace | Sim | Substituir e obter o documento |
8.4) Remover documentos
Remover um documento (deleteOne)
db.<collection_name>.deleteOne({ <query> })
Exemplo:
db.users.deleteOne({ name: "Alice" })
Remover vários documentos (deleteMany)
db.<collection_name>.deleteMany({ <query> })
Exemplo:
db.users.deleteMany({ active: false })
// deletando documentos onde o array contém um valor
db.users.deleteMany({ tags: "inactive" })
// deletando documentos com condição dentro de array (objetos)
db.orders.deleteMany({
items: { $elemMatch: { product: "mouse", quantity: { $lte: 0 } } }
})
db.orders.deleteMany(
{ "items.product": "Mouse" }
)
Quando o documento é deletado o index associado ao documento é removido imediatamente
9) Indexação
9.1) Criar índice
db.<collection_name>.createIndex({ <field>: <1 | -1> })
Exemplo:
db.users.createIndex({ name: 1 })
9.2) Listar índices
db.<collection_name>.getIndexes()
Exemplo:
db.users.getIndexes()
9.3) Remover índice
db.<collection_name>.dropIndex("<index_name>")
Exemplo:
db.users.dropIndex("name_1")
10) Aggregation (Pipeline)
O Aggregation Framework do MongoDB permite processar dados através de um pipeline composto por múltiplos estágios. Cada estágio recebe documentos, transforma e passa para o próximo.
Estrutura do Pipeline
aggregate
O aggregate executa um pipeline de agregação, ou seja, uma sequência de etapas para filtrar, transformar, agrupar, ordenar e calcular dados dentro do MongoDB.
Ele é parecido com um SELECT mais avançado do SQL, podendo fazer operações como WHERE, GROUP BY, ORDER BY, JOIN, projeções e cálculos.
db.<collection_name>.aggregate(
[
{ <stage1> },
{ <stage2> },
{ <stage3> }
],
{
allowDiskUse: <boolean>,
maxTimeMS: <number>,
collation: <document>,
comment: <any>
}
)
| Opção | Tipo | Padrão | Para que serve |
|---|---|---|---|
pipeline | array | obrigatório | Lista de estágios executados em sequência |
allowDiskUse | boolean | false | Permite usar disco em operações pesadas, como sort/group grandes |
maxTimeMS | number | sem limite explícito | Define tempo máximo de execução em milissegundos |
collation | document | collation padrão da collection | Define regras de comparação de strings |
comment | qualquer valor BSON | — | Adiciona comentário para rastrear a operação em logs/profiler |
Exemplo básico
db.orders.aggregate([
{
$match: {
status: "paid"
}
},
{
$group: {
_id: "$customerId",
total: { $sum: "$amount" },
quantity: { $sum: 1 }
}
},
{
$sort: {
total: -1
}
}
])
Nesse caso, o MongoDB:
- filtra pedidos com
status: "paid"; - agrupa por
customerId; - soma o valor dos pedidos em
total; - conta quantos pedidos existem por cliente;
- ordena do maior total para o menor.
Principais estágios
| Estágio | Para que serve | Exemplo |
|---|---|---|
$match | Filtra documentos | { $match: { active: true } } |
$project | Define/remodela campos retornados | { $project: { name: 1, email: 1 } } |
$addFields / $set | Adiciona ou recalcula campos | { $addFields: { total: { $sum: "$items.price" } } } |
$group | Agrupa documentos e calcula métricas | { $group: { _id: "$status", total: { $sum: 1 } } } |
$sort | Ordena documentos | { $sort: { createdAt: -1 } } |
$limit | Limita quantidade de resultados | { $limit: 10 } |
$skip | Pula documentos | { $skip: 20 } |
$unwind | Desmonta array em vários documentos | { $unwind: "$items" } |
$lookup | Faz join com outra collection | { $lookup: { from: "users", localField: "userId", foreignField: "_id", as: "user" } } |
$out | Salva resultado em outra collection | { $out: "resultado" } |
$merge | Insere/atualiza em outra collection | { $merge: "resumo_vendas" } |
Exemplo com $match e $project
db.users.aggregate([
{
$match: {
active: true
}
},
{
$project: {
_id: 0,
name: 1,
email: 1
}
}
])
Retorna apenas usuários ativos, mostrando somente name e email.
Exemplo com $unwind
db.orders.aggregate([
{
$unwind: "$items"
},
{
$project: {
customerId: 1,
product: "$items.name",
price: "$items.price"
}
}
])
Se um pedido tem vários itens, o $unwind transforma cada item em um documento separado no resultado.
C# Driver
var result = await collection.Aggregate()
.Match(x => x.Status == "paid")
.Group(
x => x.CustomerId,
g => new
{
CustomerId = g.Key,
Total = g.Sum(x => x.Amount),
Quantity = g.Count()
}
)
.SortByDescending(x => x.Total)
.ToListAsync();
Também pode ser feito com BsonDocument:
var pipeline = new[]
{
new BsonDocument("$match", new BsonDocument("status", "paid")),
new BsonDocument("$group", new BsonDocument
{
{ "_id", "$customerId" },
{ "total", new BsonDocument("$sum", "$amount") },
{ "quantity", new BsonDocument("$sum", 1) }
}),
new BsonDocument("$sort", new BsonDocument("total", -1))
};
var result = await collection.Aggregate<BsonDocument>(pipeline).ToListAsync();
Diferença rápida
| Método | Uso principal |
|---|---|
find | Buscar documentos simples |
findOne | Buscar um único documento |
aggregate | Filtrar, transformar, agrupar e calcular dados |
countDocuments | Contar documentos |
distinct | Retornar valores únicos de um campo |
Resumo
Use aggregate quando o find não for suficiente.
Exemplos de uso:
db.orders.aggregate([
{ $match: { status: "paid" } },
{ $group: { _id: "$customerId", total: { $sum: "$amount" } } }
])
O pipeline sempre executa de cima para baixo: a saída de um estágio vira a entrada do próximo.
Fluxo
[documentos] → stage → stage → stage → resultado
Filtragem e Seleção
$match→ filtra documentos (equivalente ao WHERE)$limit→ limita quantidade$skip→ ignora documentos$sample→ retorna documentos aleatórios :{ $sample: { size: 10 } }
db.users.aggregate([
{ $match: { age: { $gt: 18 } } },
{ $limit: 5 }
])
Transformação de Dados
$project→ seleciona ou transforma campos$addFields/$set→ adiciona campos$unset→ remove campos$replaceRoot/$replaceWith→ substitui documento
| Operador | O que faz | Status |
|---|---|---|
$addFields | Adiciona ou sobrescreve campos | Original |
$set | Mesmo comportamento do $addFields | Alias (recomendado moderno) |
- Se o campo já existe → sobrescreve
- Se o campo não existe → cria
db.players.aggregate([
{
$addFields: {
total_score: { $sum: "$scores" },
avg_score: { $avg: "$scores" }
}
},
{
$addFields: {
total_score_with_bonus: { $add: ["$total_score", "$bonus"] }
}
}
])
- Podemos ter mais de um
addFields, no caso acima o segundoaddFieldsdepende do primeiro- Referência dos campos já existente se usa o
$
- Referência dos campos já existente se usa o
db.users.aggregate([
{ $project: { name: 1, idade: "$age" } }
])
Agrupamento e Métricas
$group→ agrupa documentos (tipo GROUP BY)$count→ conta documentos$sortByCount→ agrupa + conta automaticamentedb.produtos.aggregate([
{ $sortByCount: "$categoria" }
])
//saída possível
/*
[
{ _id: "Eletrônicos", count: 10 },
{ _id: "Roupas", count: 7 },
{ _id: "Livros", count: 3 }
]
*/
db.orders.aggregate([
{
$group: {
_id: "$status",
total: { $sum: 1 }
}
}
])
Ordenação
$sort→ ordena documentos
db.users.aggregate([
{ $sort: { age: -1 } }
])
$sortcom campos de array
Podemo usar da seguinte forma
db.collection.find().sort({ "campoArray": 1 })
ou acessando o valor dos elementos dentro do array. Exemplo: 0,1,2,3,4,5
db.collection.find().sort({ "campoArray.0": 1 })
Arrays
$unwind→ transforma cada item do array em um documento
db.users.aggregate([
{ $unwind: "$hobbies" }
])
Relacionamentos
MongoDB suporta relacionamento via aggregation, mas não é o padrão principal (prefira embed quando possível).
$lookup (join simples)
Em um sharded collection o estágio $lookup pode causar tráfego significativo de rede e atrasos no processamento se não for usado com cuidado em coleções com sharding.
Faz join entre coleções (similar a LEFT JOIN):
db.orders.aggregate([
{
$lookup: {
from: "users",
localField: "userId",
foreignField: "_id",
as: "user"
}
}
])
- Retorna array
- Use
$unwindse quiser objeto - Melhor usar com índice no campo relacionado
- Pode usar
pipelinepara filtros mais complexos
$graphLookup (join recursivo)
Usado para hierarquia/grafos (ex: organograma, categorias):
db.employees.aggregate([
{
$graphLookup: {
from: "employees",
startWith: "$_id",
connectFromField: "_id",
connectToField: "managerId",
as: "subordinates"
}
}
])
- Faz busca recursiva
- Use
maxDepthpara limitar - Use só quando realmente precisar (mais pesado)
Resumo rápido
$lookup→ relacionamento simples entre coleções$graphLookup→ relacionamento recursivo (árvore/grafo)- Evite excesso → pode indicar modelagem ruim
Saída de Dados
$out→ salva resultado em coleção$merge→ insere/atualiza em coleção destino
`$out
`
db.users.aggregate([
{ $match: { active: true } },
{ $out: "active_users" }
])
Também é possível enviar a saída para outro banco:
db.getSiblingDB("mdb").coll.aggregate([
{
$out: {
db: "test",
coll: "results"
}
}
])
$merge
O $merge é um estágio do aggregate usado para gravar o resultado do pipeline em outra collection.
Ele pode:
| Ação | Descrição |
|---|---|
| Inserir | Cria documentos que ainda não existem no destino |
| Atualizar | Atualiza documentos já existentes |
| Substituir | Substitui documentos existentes |
| Mesclar | Junta campos novos com documentos existentes |
| Ignorar | Ignora documentos que já existem |
| Falhar | Gera erro em caso de conflito |
Sintaxe
db.<collection_name>.aggregate([
{ <stage1> },
{ <stage2> },
{
$merge: {
into: "<collection_destino>",
on: "<campo_chave>",
whenMatched: "<acao_quando_encontrar>",
whenNotMatched: "<acao_quando_nao_encontrar>"
}
}
])
| Campo | Para que serve |
|---|---|
into | Collection onde o resultado será gravado |
on | Campo usado para comparar documento de origem com destino |
whenMatched | O que fazer quando já existe documento com a mesma chave |
whenNotMatched | O que fazer quando não existe documento com a mesma chave |
Exemplo básico
Collection orders:
db.orders.insertMany([
{ _id: 1, customerId: 10, status: "paid", amount: 100 },
{ _id: 2, customerId: 10, status: "paid", amount: 200 },
{ _id: 3, customerId: 20, status: "paid", amount: 50 }
])
Agregação com $merge:
db.orders.aggregate([
{
$match: {
status: "paid"
}
},
{
$group: {
_id: "$customerId",
totalAmount: { $sum: "$amount" },
totalOrders: { $sum: 1 }
}
},
{
$merge: {
into: "customer_summary",
on: "_id",
whenMatched: "merge",
whenNotMatched: "insert"
}
}
])
Resultado gravado em customer_summary:
[
{
_id: 10,
totalAmount: 300,
totalOrders: 2
},
{
_id: 20,
totalAmount: 50,
totalOrders: 1
}
]
Opções do whenMatched
| Valor | Comportamento |
|---|---|
replace | Substitui o documento existente pelo novo |
merge | Mescla os campos do novo documento com o existente |
keepExisting | Mantém o documento existente e ignora o novo |
fail | Gera erro se já existir documento com a mesma chave |
pipeline | Usa um pipeline customizado para atualizar o documento |
Opções do whenNotMatched
| Valor | Comportamento |
|---|---|
insert | Insere o documento se não existir |
discard | Descarta o documento se não existir |
fail | Gera erro se não existir documento correspondente |
Exemplo com replace
db.orders.aggregate([
{
$group: {
_id: "$customerId",
totalAmount: { $sum: "$amount" }
}
},
{
$merge: {
into: "customer_summary",
on: "_id",
whenMatched: "replace",
whenNotMatched: "insert"
}
}
])
Se já existir um documento em customer_summary com o mesmo _id, ele será substituído inteiro pelo novo resultado.
Exemplo com merge
db.orders.aggregate([
{
$group: {
_id: "$customerId",
totalAmount: { $sum: "$amount" }
}
},
{
$merge: {
into: "customer_summary",
on: "_id",
whenMatched: "merge",
whenNotMatched: "insert"
}
}
])
Se já existir:
{
_id: 10,
name: "Adolfo",
totalAmount: 100
}
E o pipeline gerar:
{
_id: 10,
totalAmount: 300
}
Resultado com merge:
{
_id: 10,
name: "Adolfo",
totalAmount: 300
}
O campo name foi mantido, e totalAmount foi atualizado.
Exemplo com keepExisting
db.orders.aggregate([
{
$group: {
_id: "$customerId",
totalAmount: { $sum: "$amount" }
}
},
{
$merge: {
into: "customer_summary",
on: "_id",
whenMatched: "keepExisting",
whenNotMatched: "insert"
}
}
])
Se o documento já existir no destino, ele não será alterado.
Exemplo com discard
db.orders.aggregate([
{
$group: {
_id: "$customerId",
totalAmount: { $sum: "$amount" }
}
},
{
$merge: {
into: "customer_summary",
on: "_id",
whenMatched: "merge",
whenNotMatched: "discard"
}
}
])
Se o documento não existir no destino, ele será descartado.
Exemplo usando collection em outro database
db.orders.aggregate([
{
$group: {
_id: "$customerId",
totalAmount: { $sum: "$amount" }
}
},
{
$merge: {
into: {
db: "reports",
coll: "customer_summary"
},
on: "_id",
whenMatched: "merge",
whenNotMatched: "insert"
}
}
])
C# Driver com BsonDocument
var pipeline = new[]
{
new BsonDocument("$match", new BsonDocument("status", "paid")),
new BsonDocument("$group", new BsonDocument
{
{ "_id", "$customerId" },
{ "totalAmount", new BsonDocument("$sum", "$amount") },
{ "totalOrders", new BsonDocument("$sum", 1) }
}),
new BsonDocument("$merge", new BsonDocument
{
{ "into", "customer_summary" },
{ "on", "_id" },
{ "whenMatched", "merge" },
{ "whenNotMatched", "insert" }
})
};
await collection.Aggregate<BsonDocument>(pipeline).ToListAsync();
Diferença entre $merge e $out
| Estágio | Comportamento |
|---|---|
$out | Substitui completamente a collection destino |
$merge | Insere, atualiza ou mescla documentos na collection destino |
Exemplo:
{ $out: "customer_summary" }
Substitui a collection inteira.
{
$merge: {
into: "customer_summary",
on: "_id",
whenMatched: "merge",
whenNotMatched: "insert"
}
}
Atualiza/inclui documentos sem necessariamente apagar tudo.
Resumo
| Ponto | Explicação |
|---|---|
$merge | Grava o resultado do aggregate em uma collection |
into | Collection destino |
on | Campo usado para identificar documentos iguais |
whenMatched | Ação quando o documento já existe |
whenNotMatched | Ação quando o documento ainda não existe |
| Uso comum | Materialized views, relatórios, resumos e sincronização de dados |
Pipeline Avançado
$facet→ múltiplos pipelines paralelos$bucket→ agrupa por intervalos definidos$bucketAuto→ cria intervalos automaticamente
db.products.aggregate([
{
$bucket: {
groupBy: "$price",
boundaries: [0, 100, 500, 1000],
default: "outros",
output: {
count: { $sum: 1 }
}
}
}
])
$facet
O $facet é um estágio do aggregate que permite executar vários pipelines em paralelo sobre o mesmo conjunto de documentos.
Ele é útil quando você quer gerar várias visões do mesmo resultado em uma única consulta.
Exemplo clássico:
db.products.aggregate([
{
$facet: {
porCategoria: [
{
$group: {
_id: "$category",
total: { $sum: 1 }
}
}
],
precoMedio: [
{
$group: {
_id: null,
media: { $avg: "$price" }
}
}
],
maisCaros: [
{ $sort: { price: -1 } },
{ $limit: 5 }
]
}
}
])
Nesse exemplo, o MongoDB executa três pipelines ao mesmo tempo:
| Facet | O que faz |
|---|---|
porCategoria | Agrupa produtos por categoria |
precoMedio | Calcula o preço médio geral |
maisCaros | Retorna os 5 produtos mais caros |
Resultado aproximado:
{
porCategoria: [
{ _id: "notebook", total: 10 },
{ _id: "mouse", total: 25 }
],
precoMedio: [
{ _id: null, media: 350.75 }
],
maisCaros: [
{ name: "Notebook Gamer", price: 8000 },
{ name: "Monitor 4K", price: 3500 }
]
}
Exemplo com filtro antes do $facet
Normalmente você usa $match antes do $facet para reduzir os documentos processados:
db.products.aggregate([
{
$match: {
active: true
}
},
{
$facet: {
porCategoria: [
{
$group: {
_id: "$category",
total: { $sum: 1 }
}
}
],
faixaDePreco: [
{
$bucket: {
groupBy: "$price",
boundaries: [0, 100, 500, 1000, 5000],
default: "acima de 5000",
output: {
total: { $sum: 1 }
}
}
}
]
}
}
])
Aqui o MongoDB primeiro filtra os produtos ativos e depois gera duas análises:
| Pipeline | Função |
|---|---|
porCategoria | Total por categoria |
faixaDePreco | Total por faixa de preço |
C# Driver com BsonDocument
var pipeline = new[]
{
new BsonDocument("$match", new BsonDocument("active", true)),
new BsonDocument("$facet", new BsonDocument
{
{
"porCategoria", new BsonArray
{
new BsonDocument("$group", new BsonDocument
{
{ "_id", "$category" },
{ "total", new BsonDocument("$sum", 1) }
})
}
},
{
"maisCaros", new BsonArray
{
new BsonDocument("$sort", new BsonDocument("price", -1)),
new BsonDocument("$limit", 5)
}
}
})
};
var result = await collection
.Aggregate<BsonDocument>(pipeline)
.ToListAsync();
Quando usar $facet
Use $facet quando você precisa retornar vários resultados agregados na mesma consulta.
Exemplos:
| Cenário | Uso |
|---|---|
| Tela de e-commerce | produtos + filtros por categoria + faixa de preço |
| Dashboard | total por status + média + ranking |
| Relatório | agrupamentos diferentes sobre os mesmos dados |
| Paginação com total | lista paginada + contagem total |
Exemplo para paginação com total
db.products.aggregate([
{
$match: {
active: true
}
},
{
$facet: {
data: [
{ $skip: 0 },
{ $limit: 10 }
],
total: [
{ $count: "count" }
]
}
}
])
Resultado:
{
data: [
{ _id: 1, name: "Mouse", active: true },
{ _id: 2, name: "Teclado", active: true }
],
total: [
{ count: 120 }
]
}
Resumo
| Ponto | Explicação |
|---|---|
$facet | Executa múltiplos pipelines dentro do mesmo aggregate |
| Entrada | Todos os pipelines recebem os mesmos documentos de entrada |
| Saída | Retorna um documento com arrays, um para cada facet |
| Melhor prática | Usar $match antes para reduzir o volume processado |
| Uso comum | Dashboards, filtros, relatórios e paginação com total |
$bucket
O $bucket é um estágio do aggregate usado para agrupar documentos em faixas de valores.
Ele é muito usado para criar agrupamentos por intervalo, como:
| Exemplo | Faixas |
|---|---|
| Preço de produtos | 0-100, 100-500, 500-1000 |
| Idade de usuários | 0-18, 18-30, 30-60 |
| Valor de pedidos | 0-50, 50-200, 200-1000 |
Sintaxe
db.<collection_name>.aggregate([
{
$bucket: {
groupBy: "$<campo>",
boundaries: [<valor1>, <valor2>, <valor3>],
default: "<grupo_padrao>",
output: {
<campo_saida>: { <acumulador>: <expressao> }
}
}
}
])
| Campo | Para que serve |
|---|---|
groupBy | Campo usado para definir em qual faixa o documento entra |
boundaries | Limites das faixas |
default | Grupo usado quando o valor não se encaixa em nenhuma faixa |
output | Define os cálculos retornados por faixa |
Exemplo
Collection products:
db.products.insertMany([
{ name: "Mouse", price: 50 },
{ name: "Teclado", price: 150 },
{ name: "Monitor", price: 900 },
{ name: "Notebook", price: 3500 }
])
Agregação com $bucket:
db.products.aggregate([
{
$bucket: {
groupBy: "$price",
boundaries: [0, 100, 500, 1000, 5000],
default: "fora da faixa",
output: {
total: { $sum: 1 },
produtos: { $push: "$name" },
precoMedio: { $avg: "$price" }
}
}
}
])
Resultado aproximado:
[
{
_id: 0,
total: 1,
produtos: ["Mouse"],
precoMedio: 50
},
{
_id: 100,
total: 1,
produtos: ["Teclado"],
precoMedio: 150
},
{
_id: 500,
total: 1,
produtos: ["Monitor"],
precoMedio: 900
},
{
_id: 1000,
total: 1,
produtos: ["Notebook"],
precoMedio: 3500
}
]
Como as faixas funcionam
Com:
boundaries: [0, 100, 500, 1000, 5000]
O MongoDB cria estas faixas:
_id do bucket | Faixa |
|---|---|
0 | 0 <= price < 100 |
100 | 100 <= price < 500 |
500 | 500 <= price < 1000 |
1000 | 1000 <= price < 5000 |
O valor inicial da faixa vira o _id do bucket.
Exemplo com $match antes
db.products.aggregate([
{
$match: {
active: true
}
},
{
$bucket: {
groupBy: "$price",
boundaries: [0, 100, 500, 1000, 5000],
default: "fora da faixa",
output: {
total: { $sum: 1 },
precoMedio: { $avg: "$price" }
}
}
}
])
Boa prática: use $match antes do $bucket para reduzir a quantidade de documentos processados.
C# Driver com BsonDocument
var pipeline = new[]
{
new BsonDocument("$match", new BsonDocument("active", true)),
new BsonDocument("$bucket", new BsonDocument
{
{ "groupBy", "$price" },
{ "boundaries", new BsonArray { 0, 100, 500, 1000, 5000 } },
{ "default", "fora da faixa" },
{
"output", new BsonDocument
{
{ "total", new BsonDocument("$sum", 1) },
{ "precoMedio", new BsonDocument("$avg", "$price") },
{ "produtos", new BsonDocument("$push", "$name") }
}
}
})
};
var result = await collection
.Aggregate<BsonDocument>(pipeline)
.ToListAsync();
Diferença entre $bucket e $group
| Estágio | Uso principal |
|---|---|
$group | Agrupa por valor exato |
$bucket | Agrupa por faixa de valores |
Exemplo:
{ $group: { _id: "$category", total: { $sum: 1 } } }
Agrupa por categoria exata.
{
$bucket: {
groupBy: "$price",
boundaries: [0, 100, 500, 1000],
output: { total: { $sum: 1 } }
}
}
Agrupa por faixa de preço.
Resumo
| Ponto | Explicação |
|---|---|
$bucket | Agrupa documentos em faixas |
groupBy | Campo usado para classificar o documento |
boundaries | Define os intervalos |
default | Recebe valores fora das faixas |
output | Calcula totais, médias, listas, etc. |
| Uso comum | Histogramas, filtros de preço, faixas de idade, relatórios |
Observações Importantes
- Cada estágio é executado sequencialmente
- O output de um estágio é o input do próximo
- A ordem dos stages impacta performance
$matchdeve ser usado no início sempre que possível$projectajuda a reduzir payload
11) Administração e Monitoramento
11.1) Status do servidor
db.serverStatus()
11.2) Estatísticas do database atual
db.stats()
11.3) Estatísticas de uma coleção
db.<collection_name>.stats()
11.4) Espaço ocupado por uma coleção
db.<collection_name>.dataSize()
12) Usuários e Permissões
12.1) Criar usuário
db.createUser({
user: "<username>",
pwd: "<password>",
roles: [{ role: "<role>", db: "<database>" }]
})
12.2) Listar usuários do database
db.getUsers()
12.3) Remover usuário
db.dropUser("<username>")
12.4) Alterar senha do usuário
db.updateUser("<username>", { pwd: "<new_password>" })
13) Backup e Restauração (MongoDB Tools)
13.1) Exportar collection para JSON
mongoexport --db <database_name> --collection <collection_name> --out <file_name>.json
13.2) Importar JSON em uma collection
mongoimport --db <database_name> --collection <collection_name> --file <file_name>.json
14) Transações (quando aplicável)
14.1) Iniciar transação
session = db.getMongo().startSession()
session.startTransaction()
14.2) Commit da transação
session.commitTransaction()
session.endSession()
14.3) Abort da transação
session.abortTransaction()
session.endSession()
15) Replica Sets
15.1) Inicializar replica set
rs.initiate()
15.2) Ver status do replica set
rs.status()
15.3) Adicionar nó no replica set
rs.add("<hostname>:<port>")
15.4) Remover nó do replica set
rs.remove("<hostname>:<port>")
15.5) Forçar eleição de novo primário
rs.stepDown()
16) Sharding
16.1) Habilitar sharding em um database
sh.enableSharding("<database_name>")
16.2) Habilitar sharding em uma collection
sh.shardCollection(
"<database_name>.<collection_name>",
{ <shard_key>: 1 }
)
16.3) Ver status do sharding
sh.status()