Fonaments de MongoDB
MongoDB emmagatzema dades en documents BSON agrupats en col·leccions. El model de documents s'adapta de forma natural a estructures de dades niuades i canviants que en el model relacional requeririen múltiples taules i JOINs.
Per qué existeix MongoDB
Quan el model relacional és suficient per a molts problemes, hi ha categories de problemes on les seves limitacions es fan evidents:
Esquemes variables. En una aplicació on cada producte del catàleg té atributs completament diferents (una sabata té talla i color, un ordinador té processador, RAM i emmagatzematge, un llibre té ISBN i autor), el model relacional obliga a crear una taula amb centenars de columnes NULL o a recórrer a patrons complicats com l'EAV (Entity-Attribute-Value). Tots dos enfocaments degraden el rendiment i la llegibilitat.
Dades semiestructurades i niuades. Una comanda de comerç electrònic conté un client, una adreça d'enviament, una llista de productes amb quantitat i preu unitari, i possiblement un historial de canvis d'estat. En SQL, modelar això requereix almenys 4 taules i un JOIN complex en cada consulta. En MongoDB, és un sol document.
Escala horitzontal. El particionament (sharding) de MongoDB és natiu i permet distribuir les dades entre múltiples servidors de manera transparent. En PostgreSQL, el sharding és possible però és una capa addicional de complexitat.
Velocitat de desenvolupament. En fases inicials d'un projecte, quan l'esquema canvia freqüentment, MongoDB permet iterar sense executar migracions de base de dades en cada sprint.
El model de documents
BSON: Binary JSON
MongoDB no emmagatzema JSON de text pla sinó BSON (Binary JSON), una codificació binària del JSON que afegeix tipus de dades addicionals i permet recorreguts eficients sense parsejar tot el document:
| Tipus BSON | Exemple | Notes |
|---|---|---|
Double |
3.14 |
Nombre en coma flotant de 64 bits |
String |
"Blanes" |
UTF-8 |
Object |
{ nom: "Joan" } |
Document niuat |
Array |
[1, 2, 3] |
Llista ordenada |
ObjectId |
ObjectId("...") |
Identificador únic de 12 bytes |
Boolean |
true / false |
— |
Date |
ISODate("2025-01-15") |
Millisegons des de l'epoch Unix |
Null |
null |
Valor absent |
Int32 / Int64 |
42 |
Enters de 32 o 64 bits |
Decimal128 |
NumberDecimal("9.99") |
Alta precisió decimal (finances) |
Binary |
BinData(...) |
Dades binàries arbitràries |
L'ObjectId
Cada document de MongoDB té un camp _id que actua com a clau primària. Si no s'especifica en la inserció, MongoDB en genera un automàticament de tipus ObjectId. Un ObjectId té 12 bytes:
Això significa que els ObjectIds s'ordenen de manera aproximadament cronològica i que dos servidors que generin ObjectIds simultàniament no col·lidiran.
Exemple de document
{
_id: ObjectId("64f3a2b1c8e9d4f2a1b0c9d8"),
titol: "Introducció a MongoDB",
autor: {
nom: "Maria Garcia",
email: "maria@exemple.cat",
pais: "Espanya"
},
tags: ["mongodb", "nosql", "bases-de-dades"],
publicat: true,
data_publicacio: ISODate("2025-03-15T10:30:00Z"),
estadistiques: {
visites: 1250,
valoracio_mitjana: 4.7,
nombre_comentaris: 23
},
comentaris: [
{
usuari: "joan@exemple.cat",
text: "Molt ben explicat!",
data: ISODate("2025-03-16T08:15:00Z"),
likes: 5
},
{
usuari: "ana@exemple.cat",
text: "Gràcies per la feina",
data: ISODate("2025-03-17T14:20:00Z"),
likes: 3
}
]
}
En aquest document, autor és un document niuat i comentaris és un array de documents niuats. Tot el que en SQL requeriria una taula articles, una taula autors, una taula comentaris i dos JOINs, aquí és un sol document que es llegeix en una sola operació d'I/O.
Terminologia: MongoDB vs SQL
| SQL | MongoDB | Notes |
|---|---|---|
| Base de dades | Base de dades | El concepte és el mateix |
| Taula | Col·lecció | Sense esquema fix |
| Fila / registre | Document | Format BSON, niuat |
| Columna | Camp | El nom del camp és part del document |
| Clau primària | _id |
ObjectId per defecte |
| Clau forana | Referència manual | O document niuat (embedding) |
| JOIN | $lookup |
En l'Aggregation Pipeline |
| INDEX | Index | Mateixa funció |
| VIEW | View | Suportat des de MongoDB 3.4 |
| Stored procedure | Funció JavaScript | Menys usada |
| Transacció | Transacció multi-document | Des de MongoDB 4.0 |
Arquitectura interna de MongoDB
graph TB
Client["Client (mongosh / app)"]
subgraph "Replica Set (3 nodes)"
Primary["mongod Primary\n(lectura + escriptura)"]
Secondary1["mongod Secondary 1\n(lectura opcional)"]
Secondary2["mongod Secondary 2\n(lectura opcional)"]
end
subgraph "Sharded Cluster"
Mongos["mongos\n(router)"]
Config["Config Servers\n(metadades de shards)"]
Shard1["Shard 1\n(Replica Set)"]
Shard2["Shard 2\n(Replica Set)"]
end
Client --> Primary
Primary --> Secondary1
Primary --> Secondary2
Client --> Mongos
Mongos --> Config
Mongos --> Shard1
Mongos --> Shard2
mongod: El procés principal de la base de dades. Gestiona les dades, els índexs, la replicació i les transaccions.
Replica Set: Conjunt de 3 o més nodes mongod on un és Primary (rep escriptures) i els altres són Secondary (reben còpia de les dades via oplog). Si el Primary cau, els Secondaries elegeixen un nou Primary automàticament. Proporciona alta disponibilitat i tolerància a fallades.
mongos: El router de consultes en un entorn sharded. El client s'hi connecta com si fos un mongod normal i el mongos s'encarrega de redirigir les consultes als shards correctes.
Config Servers: Emmagatzemen les metadades del clúster sharded: quines dades estan a quin shard, la shard key, etc.
Schema Design: Embedding vs Referencing
Aquesta és la decisió de disseny més important a MongoDB. No hi ha una resposta universal: depèn del patró d'accés de l'aplicació.
Embedding (documents niuats)
Quan usar-lo:
- La relació és 1:1 o 1:few (poques entitats relacionades).
- Les dades relacionades s'accedeixen sempre juntes (no té sentit llegir un article sense els seus comentaris).
- Les dades relacionades no es comparteixen entre múltiples entitats pare.
- Cal alta velocitat de lectura i es prefereix minimitzar el nombre de consultes.
// Embedding: comanda amb línies de comanda integrades
{
_id: ObjectId("..."),
numero_comanda: "CMD-2025-001",
client_id: "CLI-042",
data: ISODate("2025-06-15"),
linies: [
{ producte_id: "PRD-001", nom: "Portàtil Lenovo", quantitat: 1, preu: 899.00 },
{ producte_id: "PRD-045", nom: "Ratolí wireless", quantitat: 2, preu: 24.99 }
],
total: 948.98,
estat: "enviat"
}
Referencing (referències)
Quan usar-lo:
- La relació és 1:many o many:many (moltes entitats relacionades).
- Les dades relacionades es comparteixen entre múltiples entitats pare (un producte apareix en moltes comandes).
- El document pare creixeria massa si s'embedding-és tot (risc de superar el límit de 16 MB).
- Les dades relacionades s'accedeixen de manera independent sovint.
// Referencing: article amb references als comentaris
// Colecció "articles"
{
_id: ObjectId("article_id_001"),
titol: "Com instal·lar MongoDB",
autor_id: ObjectId("autor_id_042"),
data: ISODate("2025-06-01"),
tags: ["mongodb", "tutorial"]
}
// Colecció "comentaris"
{
_id: ObjectId("..."),
article_id: ObjectId("article_id_001"), // referència a l'article
usuari: "joan@exemple.cat",
text: "Molt útil, gràcies!",
data: ISODate("2025-06-02")
}
Taula de decisió
| Factor | Embedding | Referencing |
|---|---|---|
| Cardinalitat | 1:1, 1:few | 1:many, many:many |
| Accés conjunt | Sempre junts | Independent freqüentment |
| Dades compartides | No | Sí |
| Mida del document | Petita / controlada | Gran o creixent sense límit |
| Consistència | Atòmica (un sol document) | Requereix transaccions |
| Rendiment lectura | Excel·lent (una query) | Cal $lookup |
Anti-patrons habituals
Arrays sense límit (Unbounded Arrays)
Un document MongoDB té un límit de 16 MB. Si emmagatzemes tots els comentaris d'un post popular dins del document del post, eventualament el document superarà el límit i les insercions fallaran. Solució: usar referencing quan el nombre d'elements pot créixer sense límit.
Documents massa grans
El límit de 16 MB és un límit estricte. Dissenyar documents que emmagatzemen imatges en Base64, historials complets o logs sense acotació és un anti-patró. Usar GridFS per a fitxers grans o referencing per a col·leccions creixents.
References excessives
Fer referencing de tot per imitar el model relacional elimina el principal avantatge de MongoDB: llegir dades relacionades en una sola operació. Si l'aplicació sempre fa $lookup per obtenir dades relacionades, potser un SGBD relacional seria una millor elecció.
Mesclar documents d'esquemes molt diferents en una col·lecció
MongoDB permet documents de formats molt diversos en la mateixa col·lecció, però fer-ho excessivament dificulta la creació d'índexs efectius i la comprensió del model de dades. Preferir col·leccions amb documents d'estructura similar.
MongoDB vs PostgreSQL: quan usar cada un
| Criteri | MongoDB | PostgreSQL |
|---|---|---|
| Esquema fix i estable | No ideal | Excel·lent |
| Esquema variable o evolutiu | Excel·lent | Requereix migracions |
| Dades molt niuades (JSON complex) | Excel·lent | JSONB disponible però més complex |
| Transaccions ACID complexes | Limitat | Excel·lent |
| Relacions many-to-many complexes | Complicat | Excel·lent (JOINs) |
| Escala horitzontal (sharding) | Natiu | Extensió externa |
| Text search | Bàsic (índex de text) | Avançat (tsvector, pg_trgm) |
| Consultes analítiques complexes | Aggregation Pipeline | SQL amb funcions de finestra |
| Dades geoespacials | 2dsphere (bo) | PostGIS (excel·lent) |
| Maduresa i ecosistema | Alt (2009) | Molt alt (1996) |
La regla pràctica
Si el teu model de dades és principalment tabular i les relacions entre entitats són el cor del negoci, usa PostgreSQL. Si el teu model de dades és principalment jeràrquic (documents amb subarrays i subdocuments) i l'esquema canviarà sovint, usa MongoDB. En arquitectures de Big Data, sovint s'usen els dos: MongoDB per a l'emmagatzematge operacional i PostgreSQL (o un data warehouse) per a l'anàlisi.
AC5074/03/01 — Miniactivitat
Disseny d'esquema per a una plataforma de blogging.
Una startup vol construir una plataforma de blogging on:
- Els usuaris tenen: nom, email, bio, foto de perfil, data de registre i un array de xarxes socials (nom + URL).
- Els articles tenen: títol, cos (text llarg), autor, data de publicació, tags (array de strings) i estat (esborrany/publicat/arxivat).
- Els comentaris pertanyen a un article i els fan usuaris registrats o anònims. Un comentari pot tenir respostes (comentaris nidats fins a 2 nivells).
- Els tags es poden filtrar i cada tag té un nom i una descripció.
Tasques:
- Dissenya l'esquema complet de les col·leccions necessàries amb exemples de documents reals (com a mínim 3 camps per document).
- Justifica per a cada relació (usuari-article, article-comentari, article-tag) si uses embedding o referencing i per qué.
- Identifica quins camps necessitaran índex per a les queries més habituals (llistar articles per tag, llistar comentaris d'un article, cercar articles per autor).
- Identifica un possible anti-patró en el teu disseny i proposa com evitar-lo.
Lliura el disseny com a document .js amb comentaris explicatius de cada decisió.