UD10 · Pràctiques
RA7 Gestiona la informació emmagatzemada en bases de dades no relacionals, avaluant i utilitzant les possibilitats que proporciona el sistema gestor. 5 CE
- RA7.a S'han caracteritzat les bases de dades no relacionals.
- RA7.b S'han avaluat els principals tipus de bases de dades no relacionals.
- RA7.c S'han identificat els elements utilitzats en aquestes bases de dades.
- RA7.d S'han identificat diferents formes de gestió de la informació segons el tipus de base de dades no relacionals.
- RA7.e S'han utilitzat les eines del sistema gestor per a la gestió de la informació emmagatzemada.
RA1 Reconeix els elements de les bases de dades analitzant-ne les funcions i valorant la utilitat dels sistemes gestors. 1 CE
- RA1.b S'han identificat els diferents tipus de bases de dades segons el model de dades utilitzat.
| Pràctica | Tipus | Nivell | CE principals |
|---|---|---|---|
| 10.1 Posada en marxa de MongoDB | Guiada | ●○○ | RA7.c, RA7.e |
| 10.2 CRUD sobre els expedients | Guiada | ●○○ | RA7.d, RA7.e |
| 10.3 Consultes amb operadors, subdocuments i arrays | Autònoma | ●●○ | RA7.d, RA7.e |
| 10.4 Agregacions: el mateix informe en SQL i en MongoDB | Guiada | ●●○ | RA7.d, RA7.e |
| 10.5 Modelatge documental d’un catàleg | Autònoma | ●●● | RA7.c, RA7.d |
| 10.6 Altres models: clau-valor i grafs | Autònoma | ●●○ | RA7.a, RA7.b |
| 10.7 Repte: relacional o NoSQL? | Repte | ●●● | RA7.a, RA7.b, RA1.b |
| Projecte EduGest · UD10 | Projecte | ●●● | RA7 complet |
Usarem MongoDB Community Server 8.0 en Docker,
mongoshi MongoDB Compass. Els resultats indicats suposen que has carregat edugest_mongo.js i no has modificat les dades (repetix la càrrega abans de cada pràctica: l’script esborra i recrea les col·leccions).
Pràctica 10.1 · Posada en marxa de MongoDB
Objectiu
Instal·lar el servidor, connectar amb les dues eines client i reconéixer els elements d’una base de dades documental.
Desenvolupament
Arrenca el servidor (si no ho vas fer en la guia de l’entorn):
docker run -d --name mongo8 -p 27017:27017 -v mongo-data:/data/db mongodb/mongodb-community-server:8.0-ubi9 docker exec -it mongo8 mongosh --eval "db.version()"Carrega les dades d’EduGest:
docker exec -i mongo8 mongosh < edugest_mongo.js # cicles: 4 · professors: 12 · expedients: 32Explora amb
mongosh:show dbs use edugest show collections db.expedientes.findOne({ _id: 1 }) db.expedientes.findOne({ _id: 30 }) db.ciclos.findOne({ _id: "SMR" }) db.expedientes.stats().size // grandària en bytes db.expedientes.getIndexes()Explora amb Compass. Connecta amb
mongodb://localhost:27017. Obriedugest.expedientes, canvia entre les vistes Llista, JSON i Taula i usa la pestanya Schema (Analyze) per a veure quins camps té la col·lecció i en quin percentatge de documents apareix cadascun.
Preguntes
- Compara els documents amb
_id1 i 30. Quins camps té un i no l’altre? Com es representaria eixa diferència en Oracle? - Quins índexs té la col·lecció? Qui els ha creat?
- Segons la pestanya Schema de Compass, en quin percentatge d’expedients apareix el camp
dni? Igrupo? - De quin tipus BSON és
fechaNacimiento? Inota?
Comprovació
db.expedientes.countDocuments()torna 32.Identifiques que l’únic índex és el de
_id, creat automàticament.dniapareix en el 87,5 % dels documents (28 de 32) igrupoen el 90,6 % (29 de 32).
Pràctica 10.2 · CRUD sobre els expedients
Objectiu
Realitzar les quatre operacions bàsiques sobre documents amb subdocuments i arrays, i comparar cadascuna amb el seu equivalent SQL.
Desenvolupament
Crear. Insereix Marina López (consulta l’exemple de la teoria) i dos alumnes més amb
insertMany. Un d’ells sense_id: quin valor li assigna MongoDB?Llegir. Escriu i executa:
db.expedientes.find({ "grupo.codigo": "1DAW" }, { _id: 0, nombre: 1, apellidos: 1 }).sort({ apellidos: 1 }) db.expedientes.countDocuments({ localidad: "Alicante" }) // 13 (sense comptar els nous) db.expedientes.find({}, { nombre: 1, fechaNacimiento: 1 }).sort({ fechaNacimiento: 1 }).limit(3)Actualitzar.
// a) Afegir un telèfon a qui no en té (Paula, _id 4) db.expedientes.updateOne({ _id: 4 }, { $set: { "contacto.telefono": "612345678" } }) // b) Marcar tot 2DAW com «en pràctiques en empresa» db.expedientes.updateMany({ "grupo.codigo": "2DAW" }, { $set: { enFCT: true } }) // 5 // c) Corregir la nota de Bases de dades d'Adrián (_id 1) amb l'operador posicional db.expedientes.updateOne( { _id: 1, "matriculas.modulo.codigo": "0484" }, { $set: { "matriculas.$.nota": 5 } }) // d) Afegir una falta a eixa mateixa matrícula db.expedientes.updateOne( { _id: 1, "matriculas.modulo.codigo": "0484" }, { $push: { "matriculas.$.faltas": { fecha: ISODate("2026-05-20"), horas: 2, justificada: false } } }) // e) Llevar el camp enFCT de tots db.expedientes.updateMany({}, { $unset: { enFCT: "" } })Comprova cada canvi amb
findOnei una projecció adequada, per exemple:db.expedientes.findOne({ _id: 1 }, { "matriculas.modulo.codigo": 1, "matriculas.nota": 1 })Eliminar. Esborra els alumnes que has inserit en el pas 1 amb una sola ordre. Pista:
{ _id: { $in: [...] } }no val per al que no tenia_id. Usa el NIA.Equivalències. Escriu, per a cada ordre dels passos 2, 3a, 3b i 5, la sentència SQL equivalent en EduGest-Oracle. Quina de les operacions 3c i 3d necessita una altra taula en SQL?
Comprovació
L’alumne inserit sense
_idrep unObjectId("...").3b informa
matchedCount: 5, modifiedCount: 5i 3emodifiedCount: 5.Després del pas 3c, la matrícula de 0484 d’Adrián té
nota: 5i la resta de les seues matrícules no ha canviat.Al final,
countDocuments()torna a ser 32.
Errors habituals
| Error | Causa |
|---|---|
MongoServerError: E11000 duplicate key error | Insereix un _id que ja existix |
Update document requires atomic operators | Has oblidat $set en un updateOne |
L’actualització «no fa res» (matchedCount: 0) | El filtre no coincidix: revisa majúscules, tipus ("1" no és 1) i la notació de punt |
Pràctica 10.3 · Consultes amb operadors, subdocuments i arrays
Enunciat
Resol cada consulta. Comprova que obtens el resultat indicat.
| # | Consulta | Resultat esperat |
|---|---|---|
| N1 | Quants alumnes són d’un grup del cicle DAM? | 13 |
| N2 | Nom, cognoms i data de naixement de l’alumnat nascut abans de 2005, del major al menor | 10 documents; el primer, Sara Amorós Guillem |
| N3 | Alumnat d’Elx o de El Campello (usa $in) | 6 |
| N4 | _id de l’alumnat sense telèfon | 4, 10, 16, 22, 28 |
| N5 | Alumnat amb alguna matrícula sense nota | 3, 8, 11, 17, 23, 28 |
| N6 | Alumnat amb algun 10 | Aitana (_id 29) |
| N7 | Alumnat amb més de 5 matrícules (usa $expr i $size) | Nerea, Hugo i Lucía |
| N8 | Alumnat el primer cognom del qual comença per «Ib» (usa $regex) | 4 documents |
| N9 | Professorat que imparteix algun mòdul en 2DAM (col·lecció profesores) | Javier, Lucía, Andrés i Raúl |
| N10 | Professorat que no imparteix classe | _id 108 a 112 |
| N11 | Cicles amb algun mòdul de més de 200 hores | DAM, DAW i ASIR |
| N12 | Els tres alumnes més jóvens | Manuel, Alba i Víctor |
Pista: la trampa de N5
Prova primer db.expedientes.find({ "matriculas.nota": { $exists: false } }). Torna els _id 30, 31 i 32: els documents en què cap element de l’array té nota (perquè el seu array està buit!). Per a «algun element sense nota» necessites $elemMatch:
db.expedientes.find({ matriculas: { $elemMatch: { nota: { $exists: false } } } }, { nombre: 1 })🔒 Solució de N7 y N9
Comprovació
Les dotze consultes donen el resultat esperat.
Expliques amb les teues paraules la diferència entre N5 amb i sense
$elemMatch.Per a N4 i N10 expliques quin equivalent tindrien en SQL (
IS NULL,NOT EXISTS/LEFT JOIN).
Pràctica 10.4 · Agregacions: el mateix informe en SQL i en MongoDB
Objectiu
Construir pipelines d’agregació etapa a etapa i comparar el seu resultat i la seua llegibilitat amb la consulta SQL equivalent.
Desenvolupament
Construïx el pipeline en Compass. Obri
expedientes→ pestanya Aggregations. Afig les etapes una a una i observa la vista prèvia de cadascuna:[ { $group: { _id: "$localidad", alumnos: { $sum: 1 } } }, { $sort: { alumnos: -1, _id: 1 } } ]Ha de coincidir amb la pràctica 7.1 (Alicante 13, Mutxamel 6…). Exporta el pipeline a codi amb el botó Export to language.
Mitjana de Bases de dades per cicle (
$unwind+$matchdesprés de l’$unwind):db.expedientes.aggregate([ { $unwind: "$matriculas" }, { $match: { "matriculas.modulo.codigo": "0484" } }, { $group: { _id: "$matriculas.modulo.ciclo", calificadas: { $sum: { $cond: [ { $eq: [ { $type: "$matriculas.nota" }, "missing" ] }, 0, 1 ] } }, media: { $avg: "$matriculas.nota" } } }, { $project: { calificadas: 1, media: { $round: ["$media", 2] } } }, { $sort: { _id: 1 } } ])[ { _id: 'DAM', calificadas: 7, media: 5.93 }, { _id: 'DAW', calificadas: 6, media: 5.92 } ]Les cinc persones amb més hores de falta (doble
$unwind):db.expedientes.aggregate([ { $unwind: "$matriculas" }, { $unwind: "$matriculas.faltas" }, { $group: { _id: "$_id", alumno: { $first: { $concat: ["$nombre", " ", "$apellidos"] } }, horas: { $sum: "$matriculas.faltas.horas" } } }, { $sort: { horas: -1, _id: 1 } }, { $limit: 5 } ])_id alumno horas 1 Adrián Ferri Baeza 8 10 Nerea Cerdá Tomás 8 9 Martina Alemany Vidal 7 14 Carla Valero Cerdá 6 7 Valeria Quiles Marco 5 Matrícules suspeses per grup. Escriu-ho tu. Resultat: 1ASIR 3, 1DAM 7, 1DAW 5, 2DAM 8, 2DAW 4.
Alumnat per nom de cicle amb
$lookupsobre la col·leccióciclos. Resultat: DAM 13, DAW 11, ASIR 5.Escriu en SQL (Oracle) les consultes dels passos 2 a 5 i compara: nombre de línies, llegibilitat i què et resulta més natural en cada cas.
Comprovació
- Els cinc pipelines donen els resultats indicats, i les consultes SQL donen els mateixos.
- Saps explicar per què el
$matchdel pas 2 va després de l’$unwind. - En el pas 3 expliques per què fa falta
$sortper_ida més de perhoras(hi ha un empat en el cinqué lloc).
$roundde MongoDB arredonix els valors que estan exactament a meitat de camí cap al nombre parell (round half to even):$round: [2.345, 2]dona 2.34. ElROUNDd’Oracle arredonix cap amunt (2.35). Amb les dades d’EduGest no hi ha empats d’este tipus, però tin-ho en compte en comparar informes.
Pràctica 10.5 · Modelatge documental d’un catàleg
Objectiu
Dissenyar una base de dades documental a partir dels patrons de consulta, justificant què s’incrusta i què es referencia.
Context
Una botiga en línia de material informàtic vol migrar el seu catàleg a MongoDB. Els productes són molt diferents entre si:
- Portàtils: processador, RAM, emmagatzematge, pantalla, pes.
- Ratolins: connexió (USB/Bluetooth), DPI, si és sense fil.
- Monitors: polzades, resolució, freqüència, panells.
- Tots tenen: referència, nom, marca, preu, estoc i una o diverses categories.
- Els clients deixen opinions (puntuació d'1 a 5, text, data). Alguns productes en tenen milers.
Consultes més freqüents:
- Fitxa del producte amb les seues característiques i les 5 opinions més recents.
- Llistat per categoria amb filtres per preu i marca.
- Puntuació mitjana de cada producte.
Enunciat
- Dissenya les col·leccions i l’estructura dels documents. Justifica cada decisió d’incrustar o referenciar en funció de les consultes.
- Explica com resoldries la «mitjana de puntuació» sense recalcular-la en cada consulta (pista: patró de camp calculat actualitzat en cada nova opinió).
- Crea la col·lecció
productosamb un validador$jsonSchemaque exigisca els camps comuns,precio >= 0istockenter no negatiu. - Inserix almenys 10 productes de tres tipus distints i 20 opinions.
- Escriu les tres consultes freqüents i crea els índexs que les acceleren. Comprova amb
explain("executionStats")que usenIXSCAN. - Compara amb un disseny relacional: quantes taules farien falta per a les característiques variables? (Pensa en el patró entitat-atribut-valor o en una taula per tipus de producte.)
Comprovació
- Les opinions no estan incrustades sense límit en el producte (problema dels 16 MB); com a màxim, les 5 últimes.
- El validador rebutja un producte amb preu negatiu.
- Les consultes usen índexs.
Pista: patró «subconjunt»
Guarda totes les opinions en una col·lecció opiniones (amb la referència del producte) i, a més, incrusta en el producte un array ultimasOpiniones amb les 5 més recents. En afegir una opinió: inserix-la en opiniones i actualitza el producte amb $push + $each + $sort + $slice: 5, i $inc del nombre d’opinions i de la suma de puntuacions.
Pràctica 10.6 · Altres models: clau-valor i grafs
Objectiu
Experimentar breument amb altres dos tipus de bases de dades NoSQL per a avaluar els seus punts forts i les seues limitacions.
Part A · Clau-valor amb Valkey (compatible amb Redis)
docker run -d --name valkey -p 6379:6379 valkey/valkey:8
docker exec -it valkey valkey-cliSET sesion:abc123 "id_alumno=1;rol=ALUMNO"
EXPIRE sesion:abc123 1800 # caduca en 30 minuts
TTL sesion:abc123
INCR visitas:portal # comptador atòmic
HSET alumno:1 nombre "Adrián" grupo "1DAM"
HGETALL alumno:1
KEYS alumno:* # només en proves! en producció usa SCANRespon: com buscaries «tots els alumnes de 1DAM»? Què hauries d’haver guardat per a poder fer-ho?
Part B · Grafs amb Neo4j
Crea un xicotet graf d’EduGest (5 alumnes, 3 mòduls, 2 professors) i resol amb Cypher:
CREATE (a:Alumno {id:1, nombre:'Adrián'}), (b:Alumno {id:2, nombre:'Rubén'}),
(bd:Modulo {codigo:'0484'}), (pr:Modulo {codigo:'0485'}),
(marta:Profesor {nombre:'Marta'}), (lucia:Profesor {nombre:'Lucía'}),
(a)-[:MATRICULADO {nota:4.75}]->(bd), (b)-[:MATRICULADO {nota:4.75}]->(bd),
(a)-[:MATRICULADO {nota:7.25}]->(pr),
(marta)-[:IMPARTE]->(bd), (lucia)-[:IMPARTE]->(pr);
MATCH (a:Alumno {nombre:'Adrián'})-[:MATRICULADO]->(m)<-[:IMPARTE]-(p) RETURN m.codigo, p.nombre;
MATCH (a:Alumno)-[:MATRICULADO]->(m)<-[:MATRICULADO]-(c:Alumno) WHERE a.nombre = 'Adrián' RETURN DISTINCT c.nombre;Conclusió
Completa una taula amb els quatre models (relacional, documental, clau-valor i grafs) i, per a cadascun: estructura, llenguatge, una consulta en la qual destaca i una consulta en la qual patix.
Pràctica 10.7 · Repte: relacional o NoSQL?
Enunciat
Per a cada cas, recomana el model (o la combinació de models) i justifica’l amb arguments d’estructura de les dades, consultes, consistència, escalabilitat i cost. Indica també què perdries amb l’alternativa descartada.
- Secretaria virtual de la Conselleria: matrícules de tots els instituts de la Comunitat, amb actes oficials i certificats.
- App d’apunts col·laboratius de l’alumnat: cada apunt té text, imatges, etiquetes variables, comentaris i versions.
- Sensors de qualitat de l’aire en totes les aules del centre: CO₂, temperatura i humitat cada 30 segons, consultats per intervals de temps.
- Recomanador de cicles formatius: «l’alumnat que va estudiar el mateix que tu i té els teus interessos va triar…».
- Carret de la compra de la botiga de la pràctica 10.5.
Comprovació
Cap recomanació es basa en «és més modern» o «és més ràpid» sense explicar per què.
Almenys un cas proposa persistència políglota (dos models alhora) i explica com se sincronitzen.
Uses la taula de decisió de la teoria com a punt de partida, no com a regla automàtica.
Projecte EduGest · UD10: l’expedient documental
Enunciat
Exportació. Escriu una consulta en Oracle que genere, per a cada alumne, el seu expedient en JSON usant les funcions SQL/JSON d’Oracle (
JSON_OBJECT,JSON_ARRAYAGG). Exporta el resultat a un fitxer i importa’l en MongoDB (Compass → Add data → Import JSON, omongoimport).SELECT JSON_OBJECT( '_id' VALUE a.id_alumno, 'nia' VALUE a.nia, 'nombre' VALUE a.nombre, 'matriculas' VALUE ( SELECT JSON_ARRAYAGG(JSON_OBJECT('curso' VALUE m.curso_academico, 'modulo' VALUE mo.codigo, 'nota' VALUE m.nota_final ABSENT ON NULL)) FROM matricula m JOIN modulo mo ON mo.id_modulo = m.id_modulo WHERE m.id_alumno = a.id_alumno) ABSENT ON NULL RETURNING CLOB) AS expediente FROM alumno a;Validació. Afig a la col·lecció un validador
$jsonSchemacoherent amb les restriccions d’Oracle (NIA de 8 dígits, nota entre 0 i 10, convocatòria d'1 a 4).Consultes. Reescriu en MongoDB cinc dels teus informes de la UD07 i compara resultats.
Índexs. Crea els índexs necessaris i mostra els plans (
explain).Informe final (
10-nosql.md): avantatges i inconvenients del model documental per a EduGest, quines parts del sistema tindrien sentit en MongoDB i quines han de continuar en Oracle, i com es mantindrien sincronitzades.
Comprovació
- Els 32 expedients importats coincidixen amb les dades d’Oracle.
- El validador rebutja una nota d'11.
- Les cinc consultes donen els mateixos resultats en els dos sistemes.
- L’informe final justifica les decisions amb criteris tècnics.
Oracle 23ai i 26ai inclouen JSON Relational Duality Views, que permeten consultar i modificar dades relacionals com si foren documents JSON, amb totes les garanties d’integritat del model relacional. Investiga esta característica: és un bon exemple que la frontera entre SQL i NoSQL és cada vegada més difusa.