Ficha 01 · MongoDB fundamentos
Exercício 1 · Setup MongoDB
Instala MongoDB localmente (preferível com Docker) ou cria conta MongoDB Atlas free tier. Conecta-te e verifica que funciona.
Resposta:
Docker (recomendado):
docker run -d -p 27017:27017 --name mongo -v mongo-data:/data/db mongo:7
# Conectar com mongosh dentro do container
docker exec -it mongo mongosh
Ou Atlas:
1. atlas.mongodb.com → sign up.
2. Free cluster M0.
3. Network → add my IP.
4. Connection string mongodb+srv://user:pass@cluster.mongodb.net/.
Verificar:
> show dbs
> use teste
> db.test.insertOne({hello: "world"})
> db.test.find()
Exercício 2 · CRUD básico
Numa BD "loja", cria collection "products". Insere 5 produtos com fields name, price, stock, category. Faz find de todos, e findOne pelo nome.
Resposta:
use loja
db.products.insertMany([
{name: "Caneta", price: 1.50, stock: 100, category: "Escolar"},
{name: "Caderno", price: 5.00, stock: 50, category: "Escolar"},
{name: "Mochila", price: 35.00, stock: 20, category: "Acessórios"},
{name: "Estojo", price: 8.00, stock: 30, category: "Acessórios"},
{name: "Lápis", price: 0.50, stock: 200, category: "Escolar"}
]);
// Find all
db.products.find();
// Find one
db.products.findOne({name: "Caneta"});
// Count
db.products.countDocuments(); // 5
Exercício 3 · Queries com operadores
Continuando, executa estas queries:
1. Produtos com price > 5.
2. Produtos com stock < 30 ordenados por preço ascendente.
3. Produtos da categoria "Escolar" OU com price > 30.
4. Nomes (só) dos produtos com stock entre 30 e 100.
Resposta:
// 1. price > 5
db.products.find({price: {$gt: 5}});
// 2. stock < 30, sort by price asc
db.products.find({stock: {$lt: 30}}).sort({price: 1});
// 3. Escolar OR price > 30
db.products.find({
$or: [
{category: "Escolar"},
{price: {$gt: 30}}
]
});
// 4. stock entre 30 e 100, só nomes
db.products.find(
{stock: {$gte: 30, $lte: 100}},
{name: 1, _id: 0}
);
Exercício 4 · Updates
Faz: 1. Aumenta o preço de todos os produtos em 10%. 2. Marca como "low_stock" qualquer produto com stock < 30. 3. Apaga produtos com stock = 0.
Resposta:
// 1. +10%
db.products.updateMany({}, {$mul: {price: 1.10}});
// 2. Marcar low_stock
db.products.updateMany(
{stock: {$lt: 30}},
{$set: {low_stock: true}}
);
// 3. Apagar stock=0
db.products.deleteMany({stock: 0});
// Verificar
db.products.find();
Exercício 5 · Aggregation pipeline
Calcula: 1. Soma total do valor em stock (price * stock) por categoria. 2. Produto mais caro de cada categoria.
Resposta:
// 1. Valor total em stock por categoria
db.products.aggregate([
{$group: {
_id: "$category",
totalValue: {$sum: {$multiply: ["$price", "$stock"]}},
count: {$sum: 1}
}},
{$sort: {totalValue: -1}}
]);
// 2. Produto mais caro por categoria
db.products.aggregate([
{$sort: {category: 1, price: -1}},
{$group: {
_id: "$category",
nameMaisCaro: {$first: "$name"},
precoMaisCaro: {$first: "$price"}
}}
]);
Exercício 6 · Indexes
Cria indexes apropriados:
1. Single em name.
2. Composto em category + price desc.
3. Unique em sku (adiciona sku aos produtos primeiro).
Verifica explain antes e depois.
Resposta:
// Adicionar SKU
db.products.find().forEach((p, i) => {
db.products.updateOne(
{_id: p._id},
{$set: {sku: `SKU-${1000 + i}`}}
);
});
// 1. Index em name
db.products.createIndex({name: 1});
// 2. Composto
db.products.createIndex({category: 1, price: -1});
// 3. Unique
db.products.createIndex({sku: 1}, {unique: true});
// Listar todos
db.products.getIndexes();
// Explain antes (sem usar index)
db.products.find({name: "Caneta"}).explain("executionStats");
// procurar "winningPlan.stage": "IXSCAN" ou "COLLSCAN"
Exercício 7 · Python — pymongo CRUD
Escreve script Python que:
1. Conecta à BD loja.
2. Insere 3 novos produtos.
3. Lista produtos com price < 10.
4. Atualiza stock do primeiro para 999.
Resposta:
pip install pymongo
from pymongo import MongoClient
client = MongoClient("mongodb://localhost:27017/")
db = client.loja
# 1. Insert
db.products.insert_many([
{"name": "Borracha", "price": 0.75, "stock": 150, "category": "Escolar"},
{"name": "Régua", "price": 1.20, "stock": 80, "category": "Escolar"},
{"name": "Tesoura", "price": 3.50, "stock": 40, "category": "Escolar"},
])
print("Inseridos!")
# 2. List price < 10
print("\nProdutos com preço < 10:")
for p in db.products.find({"price": {"$lt": 10}}):
print(f" {p['name']}: {p['price']:.2f}€ (stock {p['stock']})")
# 3. Update stock do primeiro
primeiro = db.products.find_one()
db.products.update_one(
{"_id": primeiro["_id"]},
{"$set": {"stock": 999}}
)
print(f"\n{primeiro['name']} stock = 999")
client.close()
Exercício 8 · Modelagem: Blog
Desenha modelagem (em JSON sample) para um blog com: - Posts (título, body, autor, data, tags). - Comments por post. - Autores (nome, bio, avatar).
Decide se embeds ou referencias, e justifica.
Resposta:
Estratégia escolhida: - Autores em collection separada (reutilizados em vários posts). - Comments embedded até ~100; reference se >>100. - Autor info denormalizada parcialmente nos comments (snapshot).
// Collection: authors
{
"_id": "author1",
"name": "Ana Silva",
"bio": "Developer Python há 10 anos",
"avatar": "https://..."
}
// Collection: posts (com comments embedded)
{
"_id": "post1",
"title": "Tutorial Python",
"body": "...",
"authorId": "author1",
"authorName": "Ana Silva", // denormalizado para evitar lookup
"date": ISODate("2026-05-25"),
"tags": ["python", "tutorial"],
"comments": [
{
"_id": ObjectId(),
"text": "Excelente!",
"byId": "author2",
"byName": "Bruno", // snapshot
"date": ISODate("2026-05-26")
}
],
"commentCount": 1
}
Justificação:
- Posts vistos sempre com comments → embed reduz round trips.
- Authors mudam pouco → denormalizar nome em posts/comments é OK.
- Se autor muda nome, podemos correr update batch.
- Quando comments > 100, mover para collection separada comments com postId reference + paginação.
Indexes recomendados:
db.posts.createIndex({date: -1});
db.posts.createIndex({authorId: 1});
db.posts.createIndex({tags: 1});
db.posts.createIndex({title: "text", body: "text"}); // full-text