Salta el contingut

PR507403 — MongoDB amb Docker

Tipus: Pràctica tècnica (Docker + MongoDB) Durada estimada: 9 hores (3 sessions de 3 hores) Lliurament: Campus Virtual — Script(s) Mongo + captures + informe breu


Objectius

Al finalitzar aquesta pràctica, l'alumne serà capaç de:

  1. Instal·lar i configurar una instància de MongoDB amb Docker i connectar-s'hi amb MongoDB Compass i mongosh.
  2. Dissenyar l'esquema de documents per a un cas d'ús d'e-commerce, justificant les decisions d'embedding vs referencing per a cada relació.
  3. Implementar operacions CRUD completes (insertOne/insertMany, find, updateOne/updateMany, deleteOne/deleteMany) i consultes amb operadors de filtre, lògics i sobre arrays.
  4. Construir pipelines d'agregació amb $match, $group, $project, $lookup i $unwind per respondre preguntes analítiques reals sobre el negoci.
  5. Crear índexs simples, compostos i de text adequats als patrons d'accés de l'aplicació, i verificar-ne l'impacte real amb explain().
  6. Interpretar la sortida d'explain() per distingir un COLLSCAN d'un IXSCAN i argumentar quan un índex és útil.

Materials necessaris

  • Docker Desktop (o Docker Engine + Docker Compose) instal·lat i funcionant.
  • MongoDB Compass instal·lat.
  • Un editor de codi (VS Code recomanat) amb l'extensió MongoDB per a sintaxi .js.
  • Els continguts del Bloc 3, que cal tenir oberts com a referència:

Pràctica tècnica

A diferència de la pràctica del Bloc 1, aquí cal instal·lar programari, escriure codi JavaScript (mongosh) i executar comandes Docker. Si no has completat les miniactivitats AC5074/03/01 a AC5074/03/04 del bloc, fes-les abans de començar aquesta pràctica.


Descripció de la pràctica

Part 1 — Instal·lació de MongoDB amb Docker (1 hora)

Crea un directori de treball pr507403-nom_cognom/ amb l'estructura següent:

pr507403-nom_cognom/
├── docker-compose.yml
├── scripts/
│   ├── 01_carrega_dades.js
│   ├── 02_crud_queries.js
│   ├── 03_aggregation.js
│   └── 04_indexos.js
└── informe/
    └── informe_pr507403.md

Crea el fitxer docker-compose.yml amb un servei MongoDB i un servei Mongo Express (eina web opcional per inspeccionar la BD des del navegador):

# docker-compose.yml
services:
  mongodb-nom-cognom:
    image: mongo:7
    container_name: mongodb-nom-cognom
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: admin123
      MONGO_INITDB_DATABASE: bigdata_nom_cognom
    ports:
      - "27017:27017"
    volumes:
      - mongodata_nom_cognom:/data/db
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5

  mongo-express-nom-cognom:
    image: mongo-express:latest
    container_name: mongo-express-nom-cognom
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: admin
      ME_CONFIG_MONGODB_ADMINPASSWORD: admin123
      ME_CONFIG_MONGODB_SERVER: mongodb-nom-cognom
      ME_CONFIG_BASICAUTH_USERNAME: admin
      ME_CONFIG_BASICAUTH_PASSWORD: admin123
    ports:
      - "8081:8081"
    depends_on:
      mongodb-nom-cognom:
        condition: service_healthy

volumes:
  mongodata_nom_cognom:

Aixeca els serveis i verifica que tot funciona:

docker compose up -d
docker compose ps

# Connectar-se via mongosh dins del contenidor
docker exec -it mongodb-nom-cognom mongosh \
  --username admin --password admin123 --authenticationDatabase admin

Connecta't també amb MongoDB Compass fent servir la cadena de connexió mongodb://admin:admin123@localhost:27017 i confirma que pots veure les bases de dades del servidor.

Lliurable de la Part 1: captura de pantalla de docker compose ps amb els dos serveis en estat healthy/running i captura de Compass connectat al servidor.


Part 2 — Disseny de l'esquema per a l'e-commerce (2 hores)

Cas d'ús: Sapa-Shop

Reutilitzem el cas d'ús Sapa-Shop (e-commerce d'electrònica) ja conegut del Bloc 1. Cal dissenyar tres col·leccions: productes, clients i comandes.

2.1 Col·lecció productes

Catàleg de productes amb especificacions tècniques variables segons la categoria (camp niuat especificacions, exemple ja vist a Fonaments de MongoDB):

{
  _id: "PRD-010",
  nom: "Portàtil Lenovo ThinkPad",
  categoria: "informatica",
  preu: 899.99,
  estoc: 15,
  valoracio: 4.7,
  tags: ["portàtil", "treball", "professional"],
  especificacions: {
    processador: "Intel i7",
    ram_gb: 16,
    disc_gb: 512
  },
  disponible: true,
  data_alta: ISODate("2025-01-10")
}

2.2 Col·lecció clients

{
  _id: "CLI-001",
  nom: "Joan Puig",
  email: "joan.puig@exemple.cat",
  segment: "premium",
  adreces: [
    { tipus: "facturacio", carrer: "Carrer Major 12", ciutat: "Girona", cp: "17001" },
    { tipus: "enviament", carrer: "Avinguda Pau 5", ciutat: "Blanes", cp: "17300" }
  ],
  data_registre: ISODate("2023-04-02")
}

2.3 Col·lecció comandes

Cada comanda referencia el client (per client_id) però embedeix les línies de producte amb una còpia del nom i preu en el moment de la compra (patró habitual en e-commerce: el preu d'una comanda passada no ha de canviar si el producte canvia de preu després):

{
  _id: ObjectId("..."),
  numero_comanda: "CMD-2026-00321",
  client_id: "CLI-001",
  data: ISODate("2026-03-12"),
  linies: [
    { producte_id: "PRD-010", nom: "Portàtil Lenovo ThinkPad", preu_unitari: 899.99, quantitat: 1 },
    { producte_id: "PRD-045", nom: "Ratolí Logitech MX Master", preu_unitari: 89.99, quantitat: 2 }
  ],
  total: 1079.97,
  estat: "completat",
  pais_enviament: "Espanya"
}

Tasques:

  1. Justifica per escrit (al fitxer informe_pr507403.md) la decisió d'embedding vs referencing per a cadascuna d'aquestes relacions: comanda → client, comanda → línies de producte, client → adreces. Usa la taula de decisió d'embedding vs referencing com a referència.
  2. Identifica almenys un possible anti-patró que podries cometre en aquest disseny (per exemple, embedding il·limitat de l'historial de comandes dins del document de client) i explica com l'evites.
  3. Escriu l'script scripts/01_carrega_dades.js que crea la base de dades bigdata_nom_cognom i insereix, amb insertMany, com a mínim 20 productes (almenys 4 categories diferents), 10 clients i 30 comandes (amb dates repartides entre gener i juny de 2026 i estats "completat", "pendent" o "cancel·lat").

Generar dades de mostra ràpidament

No cal escriure els 60 documents a mà. Pots generar-los amb un bucle for dins del mateix script mongosh, variant categoria, preu i data amb Math.random(), o amb un petit script Python (pymongo) si ho prefereixes. L'important és que les dades siguin variades i permetin respostes interessants als pipelines de la Part 4.


Part 3 — CRUD i queries amb operadors (2 hores)

Escriu a scripts/02_crud_queries.js les operacions següents sobre les col·leccions creades a la Part 2 (consulta Queries i CRUD per a la sintaxi completa):

  1. Create: insereix un nou producte amb insertOne i tres comandes noves amb insertMany.
  2. Read — operadors de comparació: productes amb preu entre 50 € i 300 € ($gte/$lte), de les categories "informatica" o "monitors" ($in).
  3. Read — operadors lògics: comandes amb estat "completat" i total superior a 200 € ($and implícit); productes amb valoració superior a 4.5 o que tinguin el tag "oferta" ($or).
  4. Read — documents niuats i arrays: clients amb una adreça d'enviament a "Blanes" (notació punt sobre l'array adreces amb $elemMatch); comandes que continguin una línia amb producte_id: "PRD-010" ($elemMatch).
  5. Read — expressions regulars: productes el nom dels quals contingui "Pro" o "pro" ($regex amb l'opció "i").
  6. Read — projecció i ordenació: els 5 productes més cars, mostrant només nom, preu i categoria.
  7. Update: aplica un descompte del 15% ($mul) a tots els productes de la categoria "peripherics"; afegeix el tag "top-vendes" ($addToSet) als 3 productes amb més valoració.
  8. Upsert: actualitza (o crea si no existeix) un producte concret pel seu nom.
  9. Delete: elimina les comandes amb estat "cancel·lat" i una antiguitat superior a 6 mesos.
  10. Documenta a l'informe quin operador SQL equivaldria a cadascuna de les operacions anteriors (taula de dues columnes).

Part 4 — Aggregation Pipeline (2 hores)

Escriu a scripts/03_aggregation.js els pipelines següents (consulta Aggregation Pipeline):

4.1 Vendes per categoria. Pipeline amb $match (comandes "completat"), $unwind de linies, $lookup amb productes per obtenir la categoria de cada línia, $group per calcular ingressos totals i unitats venudes per categoria, i $sort descendent per ingressos.

4.2 Top 5 clients per facturació. Pipeline que agrupa les comandes completades per client_id, suma el total, fa $lookup amb clients per obtenir el nom, i retorna els 5 clients amb més facturació acumulada.

4.3 Resum mensual de vendes. Pipeline que agrupa les comandes per any i mes ($year, $month sobre el camp data), calculant nombre de comandes i import total per mes, ordenat cronològicament.

4.4 Anàlisi amb $facet. Un sol pipeline que retorni en un sol document: estadístiques generals (total de comandes, import mitjà), distribució de comandes per país d'enviament i el top 3 de productes més venuts (per unitats).

Per a cadascun dels 4 pipelines, anota a l'informe quina pregunta de negoci respon i adjunta una captura del resultat.


Part 5 — Índexs i mesura del rendiment (1,5 hores)

Escriu a scripts/04_indexos.js el procediment següent (consulta Índexs i rendiment):

  1. Executa db.comandes.find({ client_id: "CLI-001", estat: "completat" }).explain("executionStats") abans de crear cap índex. Anota stage, totalDocsExamined, nReturned i executionTimeMillis.
  2. Crea l'índex compost que consideris més adequat per a aquesta query, seguint la regla ESR (Equality, Sort, Range).
  3. Torna a executar explain("executionStats") i compara les mètriques abans/després. Adjunta totes dues captures a l'informe.
  4. Crea un índex de text sobre el camp nom de productes (especifica default_language: "spanish") i prova una cerca amb $text: { $search: ... }.
  5. Crea un índex sobre el camp tags (multikey, automàtic en indexar un array) i comprova amb explain() que una query find({ tags: "..." }) ara fa servir IXSCAN en lloc de COLLSCAN.
  6. Llista tots els índexs creats a comandes i productes amb getIndexes() i inclou la sortida a l'informe.

L'índex no sempre és la solució

Si en crear un índex explain() segueix mostrant COLLSCAN, revisa que l'ordre dels camps de l'índex compost respecta la prefix rule i que el camp té prou cardinalitat per ser útil (consulta la secció de cardinalitat i selectivitat d'Índexs i rendiment).


Lliurament

Puja al Campus Virtual un fitxer .zip anomenat PR507403_cognom_nom.zip amb l'estructura del directori pr507403-nom_cognom/ completa:

Fitxer / carpeta Contingut
docker-compose.yml Definició dels serveis MongoDB i Mongo Express
scripts/01_carrega_dades.js Creació de la BD i càrrega de productes, clients i comandes
scripts/02_crud_queries.js Les 10 operacions CRUD i queries de la Part 3, comentades
scripts/03_aggregation.js Els 4 pipelines de la Part 4, comentats
scripts/04_indexos.js Creació d'índexs i comandes explain() de la Part 5
informe/informe_pr507403.md Justificació de l'esquema, captures d'explain(), taula CRUD↔SQL, resultats dels pipelines, respostes a les preguntes de reflexió

Estructura recomanada de l'informe:

  1. Portada (nom, grup, data).
  2. Disseny de l'esquema: justificació embedding/referencing i anti-patró identificat (Part 2).
  3. CRUD i queries: taula d'equivalència amb SQL (Part 3).
  4. Aggregation Pipeline: pregunta de negoci i resultat de cada pipeline (Part 4).
  5. Índexs: captures d'explain() abans/després i llistat d'índexs (Part 5).
  6. Preguntes de reflexió final.

Sobre l'ús d'IA generativa

Pots usar IA generativa per resoldre dubtes puntuals de sintaxi, però els scripts i l'informe han de reflectir la teva pròpia comprensió del disseny i les decisions preses. El professorat pot convocar una defensa oral per verificar la comprensió del codi lliurat.

Data límit de lliurament: consulta el calendari del Campus Virtual.

Consulta la Rúbrica PR507403 per als criteris detallats d'avaluació.


Preguntes de reflexió final

Un cop completada la pràctica, reflexiona breument sobre:

  1. Si Sapa-Shop hagués de créixer fins als 50 milions de comandes anuals descrits al Bloc 1, quin canvi de disseny (sharding, replica set, revisió de l'embedding) consideraries prioritari?
  2. Quina diferència pràctica has notat entre escriure una consulta analítica amb find() i fer-ho amb l'Aggregation Pipeline? En quin moment find() ja no és suficient?
  3. Després de mesurar l'impacte real d'un índex amb explain(), com canviaria el teu criteri a l'hora de decidir si val la pena crear un índex nou en un projecte real?

Inclou les respostes a les conclusions de l'informe (3-5 línies per pregunta).


Pràctica PR507403 | Mòdul M5074 Sistemes de Big Data | Institut Sa Palomera (Blanes) | Curs CEIABD 2026-2027