Manipulação de dados e consultas em MongoDB
1. Introdução ao MongoDB e seu modelo de dados
O MongoDB é um banco de dados NoSQL orientado a documentos que armazena dados em formato BSON (Binary JSON), diferentemente das tabelas relacionais tradicionais. Enquanto bancos relacionais organizam dados em linhas e colunas com esquemas rígidos, o MongoDB utiliza coleções que contêm documentos flexíveis, onde cada documento pode ter uma estrutura diferente.
Cada documento possui um campo _id obrigatório, que funciona como chave primária. Se não for especificado, o MongoDB gera automaticamente um ObjectId único. Os tipos de dados suportados incluem strings, números, booleanos, arrays, objetos aninhados, datas, ObjectId, entre outros. O aninhamento de documentos permite representar relacionamentos complexos sem a necessidade de JOINs.
// Exemplo de documento BSON
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"nome": "Maria Silva",
"idade": 34,
"endereco": {
"rua": "Av. Paulista",
"numero": 1000,
"cidade": "São Paulo"
},
"telefones": ["11999999999", "11888888888"]
}
2. Operações básicas de CRUD
Inserção
A inserção de documentos pode ser feita individualmente ou em lote. O MongoDB valida automaticamente a estrutura do documento, mas não exige um esquema pré-definido.
// Inserir um documento
db.usuarios.insertOne({
"nome": "João",
"email": "joao@email.com",
"idade": 28
})
// Inserir múltiplos documentos
db.usuarios.insertMany([
{ "nome": "Ana", "email": "ana@email.com", "idade": 32 },
{ "nome": "Carlos", "email": "carlos@email.com", "idade": 25 }
])
Leitura
O método find() permite consultar documentos com projeção de campos e limitação de resultados.
// Buscar todos os documentos
db.usuarios.find()
// Buscar com projeção (apenas nome e idade)
db.usuarios.find({}, { "nome": 1, "idade": 1, "_id": 0 })
// Limitar resultados
db.usuarios.find().limit(5)
Atualização
Operadores como $set, $unset e $inc permitem modificar documentos de forma precisa.
// Atualizar um campo específico
db.usuarios.updateOne(
{ "nome": "João" },
{ $set: { "idade": 29 } }
)
// Incrementar um valor numérico
db.usuarios.updateMany(
{ "idade": { $gte: 30 } },
{ $inc: { "idade": 1 } }
)
// Remover um campo
db.usuarios.updateOne(
{ "nome": "Ana" },
{ $unset: { "telefone": "" } }
)
Remoção
A remoção deve ser feita com cuidado, especialmente em operações em massa.
// Remover um documento
db.usuarios.deleteOne({ "nome": "Carlos" })
// Remover múltiplos documentos
db.usuarios.deleteMany({ "idade": { $lt: 18 } })
3. Consultas com filtros e operadores de comparação
Os operadores de comparação permitem filtrar documentos com base em condições específicas.
// Operadores básicos
db.produtos.find({ "preco": { $eq: 100 } }) // Igual
db.produtos.find({ "preco": { $ne: 100 } }) // Diferente
db.produtos.find({ "preco": { $gt: 50 } }) // Maior que
db.produtos.find({ "preco": { $gte: 50 } }) // Maior ou igual
db.produtos.find({ "preco": { $lt: 100 } }) // Menor que
db.produtos.find({ "preco": { $lte: 100 } }) // Menor ou igual
// Operadores lógicos
db.produtos.find({
$and: [
{ "preco": { $gte: 50 } },
{ "categoria": "eletrônicos" }
]
})
db.produtos.find({
$or: [
{ "preco": { $lt: 20 } },
{ "categoria": "promoção" }
]
})
// Operadores de array
db.produtos.find({ "tags": { $in: ["urgente", "destaque"] } })
db.produtos.find({ "tags": { $all: ["novo", "coleção"] } })
db.produtos.find({ "avaliacoes": { $elemMatch: { "nota": { $gte: 4 } } } })
4. Trabalhando com arrays e documentos aninhados
A navegação em subdocumentos utiliza dot notation, e a manipulação de arrays é feita com operadores específicos.
// Consulta em subdocumento
db.pedidos.find({ "endereco.entrega.cidade": "São Paulo" })
// Adicionar elemento ao array
db.usuarios.updateOne(
{ "nome": "Maria" },
{ $push: { "telefones": "11777777777" } }
)
// Remover elemento do array
db.usuarios.updateOne(
{ "nome": "Maria" },
{ $pull: { "telefones": "11888888888" } }
)
// Adicionar elementos únicos
db.usuarios.updateOne(
{ "nome": "Pedro" },
{ $addToSet: { "interesses": "futebol" } }
)
// Atualizar elemento específico em array
db.pedidos.updateOne(
{ "itens.nome": "Notebook" },
{ $set: { "itens.$.quantidade": 3 } }
)
5. Agregação e pipeline de dados
O pipeline de agregação permite processar dados em múltiplos estágios.
// Pipeline básico de agregação
db.vendas.aggregate([
{ $match: { "data": { $gte: ISODate("2024-01-01") } } },
{ $group: {
_id: "$categoria",
totalVendas: { $sum: "$valor" },
mediaPreco: { $avg: "$preco" },
quantidade: { $count: {} }
}},
{ $sort: { "totalVendas": -1 } },
{ $project: {
_id: 0,
categoria: "$_id",
totalVendas: 1,
mediaPreco: { $round: ["$mediaPreco", 2] }
}}
])
// Desaninhar arrays com $unwind
db.pedidos.aggregate([
{ $unwind: "$itens" },
{ $group: {
_id: "$itens.nome",
totalVendido: { $sum: "$itens.quantidade" }
}}
])
// Adicionar campos calculados
db.produtos.aggregate([
{ $addFields: {
precoComDesconto: { $multiply: ["$preco", 0.9] },
categoriaUpper: { $toUpper: "$categoria" }
}}
])
6. Indexação e otimização de consultas
Índices são essenciais para performance em consultas frequentes.
// Criar índice simples
db.usuarios.createIndex({ "email": 1 })
// Criar índice composto
db.pedidos.createIndex({ "cliente_id": 1, "data": -1 })
// Criar índice textual
db.produtos.createIndex({ "descricao": "text" })
// Analisar performance com explain()
db.usuarios.find({ "email": "joao@email.com" }).explain("executionStats")
// Criar índice TTL (expiração automática)
db.sessoes.createIndex({ "criadoEm": 1 }, { expireAfterSeconds: 3600 })
7. Transações e consistência de dados
Transações multi-documento garantem atomicidade em operações complexas.
// Transação multi-documento
const session = db.getMongo().startSession()
session.startTransaction()
try {
const contas = session.getDatabase("banco").contas
contas.updateOne(
{ "conta": "A001" },
{ $inc: { "saldo": -500 } }
)
contas.updateOne(
{ "conta": "B002" },
{ $inc: { "saldo": 500 } }
)
session.commitTransaction()
} catch (error) {
session.abortTransaction()
} finally {
session.endSession()
}
// Configurar write concern para consistência
db.produtos.insertOne(
{ "nome": "Produto X" },
{ writeConcern: { w: "majority", j: true } }
)
8. Boas práticas e padrões de modelagem
A escolha entre documentos embutidos e referências impacta diretamente a performance.
// Modelagem embeddable (um-para-um)
db.clientes.insertOne({
"nome": "João",
"endereco": {
"rua": "Rua A",
"cidade": "São Paulo",
"cep": "01001-000"
}
})
// Modelagem referencial (um-para-muitos)
db.pedidos.insertOne({
"cliente_id": ObjectId("507f1f77bcf86cd799439011"),
"itens": [
{ "produto_id": ObjectId("507f1f77bcf86cd799439012"), "quantidade": 2 }
]
})
// Schema validation
db.createCollection("produtos", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["nome", "preco"],
properties: {
nome: { bsonType: "string" },
preco: { bsonType: "double", minimum: 0 }
}
}
}
})
// Bucket pattern para séries temporais
db.sensores.insertMany([
{
"sensor_id": "S001",
"leituras": [
{ "timestamp": ISODate("2024-01-01T10:00:00Z"), "valor": 25.5 },
{ "timestamp": ISODate("2024-01-01T10:05:00Z"), "valor": 26.1 }
]
}
])
Referências
- Documentação Oficial do MongoDB: CRUD Operations — Guia completo sobre operações de criação, leitura, atualização e exclusão no MongoDB.
- MongoDB University: M001 - MongoDB Basics — Curso gratuito introdutório sobre fundamentos do MongoDB, incluindo manipulação de dados.
- Aggregation Pipeline Documentation — Documentação oficial sobre o pipeline de agregação, com exemplos detalhados de cada estágio.
- MongoDB Indexing Strategies — Guia completo sobre tipos de índices, estratégias de otimização e boas práticas.
- MongoDB Transactions — Documentação oficial sobre transações multi-documento, controle de concorrência e consistência.
- Schema Design Patterns — Artigo técnico sobre padrões de modelagem, incluindo bucket pattern e schema validation.