Salta el contingut

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:

 0  1  2  3  |  4  5  6  |  7  8  |  9 10 11
  timestamp  | machine id |  pid  |  counter

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
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:

  1. Dissenya l'esquema complet de les col·leccions necessàries amb exemples de documents reals (com a mínim 3 camps per document).
  2. Justifica per a cada relació (usuari-article, article-comentari, article-tag) si uses embedding o referencing i per qué.
  3. 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).
  4. 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ó.