Partilhar: WhatsApp
aulify
UC UC00664 · T. Desenv. Software, T. Sist. Comp. Redes, T. Multimédia

Ficha 01 · MongoDB fundamentos

CRUD, queries, aggregation, indexes
Versão · Aluno
Tempo · 45 minutos
Aluno(a)
Turma
Data

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