📝 Modelado de datos: documentos y colecciones

Modelado de datos en MongoDB

📝 Modelado de datos: documentos y colecciones

MongoDB no usa tablas ni filas como los sistemas SQL, sino documentos JSON dentro de colecciones. Aquí aprenderás cómo decidir **qué datos van juntos en un mismo documento** y **cuándo separarlos en distintas colecciones**.


📚 Conceptos clave

  • Un documento es una estructura JSON (BSON internamente) — contiene pares clave:valor.
  • Una colección agrupa documentos del mismo tipo o propósito.
  • No hay esquema rígido, pero puedes definir JSON Schema para validar estructura.
  • El diseño influye directamente en el rendimiento de lectura/escritura.

👉 En MongoDB, **diseñas según cómo consultas los datos, no según las relaciones.**


1️⃣ Principios básicos del modelado

  • Modela según consultas frecuentes: qué datos necesitas juntos.
  • Piensa en documentos, no en tablas.
  • Embebe (embed) datos que se usan siempre junto al padre.
  • Referencia (reference) datos independientes o muy grandes.

Regla rápida: “Si los datos se consultan juntos, guárdalos juntos.”


2️⃣ Ejemplo: tienda online — productos, pedidos y usuarios

En SQL tendrías tablas con claves foráneas; en MongoDB puedes hacerlo así:

// Producto
{
  _id: ObjectId("..."),
  name: "Auriculares Bluetooth",
  sku: "AUR-01",
  price: 49.90,
  categories: ["audio", "tecnología"],
  stock: 120
}

// Usuario
{
  _id: ObjectId("..."),
  name: "Laura García",
  email: "laura@gmail.com",
  passwordHash: "...",
  address: {
    calle: "Gran Vía 10",
    ciudad: "Madrid",
    cp: "28013"
  }
}

// Pedido con items embebidos
{
  _id: ObjectId("..."),
  userId: ObjectId("..."),
  createdAt: ISODate("2025-11-13T10:00:00Z"),
  total: 99.80,
  items: [
    { productId: ObjectId("..."), name: "Auriculares Bluetooth", qty: 2, price: 49.90 }
  ],
  status: "pending"
}
  

Aquí el pedido referencia al usuario (userId), pero embeber el detalle de cada producto dentro del pedido evita tener que hacer varias consultas cuando lo visualizas.


3️⃣ Cuándo embeber documentos

  • El subdocumento pertenece exclusivamente al padre.
  • Se consulta y modifica junto con el documento principal.
  • El tamaño total del documento no supera los 16 MB (límite de BSON).
// Ejemplo: comentarios embebidos en un post
{
  _id: ObjectId("..."),
  titulo: "Novedades MongoDB 7.0",
  contenido: "Detalles y nuevas funciones...",
  comentarios: [
    { autor: "Ana", texto: "Excelente post!", fecha: new Date() },
    { autor: "Carlos", texto: "Muy útil, gracias!", fecha: new Date() }
  ]
}
  

Ventaja: consultas más rápidas — todo está en un único documento.
⚠️ Desventaja: si hay demasiados subdocumentos, el documento puede crecer mucho.


4️⃣ Cuándo referenciar

  • El subdocumento se comparte entre varios documentos (ej. un producto aparece en muchos pedidos).
  • Se actualiza por separado o con frecuencia.
  • Su tamaño es grande o crece indefinidamente.
// Ejemplo: relación usuario → pedidos
{
  _id: ObjectId("..."),
  name: "Laura",
  email: "laura@gmail.com"
}

{
  _id: ObjectId("..."),
  userId: ObjectId("..."), // referencia
  items: [ ... ],
  total: 99.80
}
  

Luego puedes combinar ambas colecciones con $lookup (join equivalente) si necesitas unir datos.

// Unir pedidos con datos de usuario
db.orders.aggregate([
  {
    $lookup: {
      from: "usuarios",
      localField: "userId",
      foreignField: "_id",
      as: "usuario"
    }
  },
  { $unwind: "$usuario" }
])
  

5️⃣ Patrón mixto (embed + reference)

Es común mezclar ambos enfoques. Ejemplo: un pedido guarda los datos resumidos del usuario (nombre, email) embebidos, pero también su userId para futuras consultas.

{
  _id: ObjectId("..."),
  userId: ObjectId("..."),
  user: { name: "Laura García", email: "laura@gmail.com" },
  items: [ { productId: ObjectId("..."), name: "Auriculares", qty: 2 } ],
  total: 99.80
}
  

✅ Permite mostrar el pedido directamente sin hacer joins. 🔄 Si el usuario cambia su nombre, el pedido histórico sigue mostrando el nombre original.


6️⃣ Ejemplo práctico — diseño completo para tienda online

  • users: datos personales + direcciones embebidas
  • products: catálogo general
  • orders: referencia al usuario + productos embebidos con datos de precio en el momento del pedido
  • reviews: colección separada (crece mucho)
// users
{
  name: "Laura",
  email: "laura@gmail.com",
  addresses: [
    { calle: "Gran Vía 10", ciudad: "Madrid", cp: "28013" }
  ]
}

// products
{
  name: "Auriculares",
  sku: "AUR-01",
  price: 49.90
}

// orders
{
  userId: ObjectId("..."),
  items: [
    { productId: ObjectId("..."), name: "Auriculares", price: 49.90, qty: 2 }
  ],
  total: 99.80
}

// reviews
{
  productId: ObjectId("..."),
  userId: ObjectId("..."),
  rating: 5,
  text: "Excelente calidad"
}
  

7️⃣ Patrones de modelado comunes en MongoDB

  • One-to-One: datos complementarios → embebidos o colecciones separadas según tamaño.
  • One-to-Many: pocos elementos → embebidos; muchos → referencias.
  • Many-to-Many: usa arrays de referencias o colección intermedia.
  • Subset pattern: almacenar solo parte de los datos (por ejemplo, 5 últimos comentarios) y el resto en otra colección.
  • Extended reference pattern: guardar en el documento los campos más usados del documento referenciado.

8️⃣ Buenas prácticas

  • Evita documentos mayores a 16 MB (usa referencias o particiona datos).
  • Agrega createdAt y updatedAt a todos los documentos.
  • Usa ObjectId como ID por defecto.
  • Piensa en tus consultas más comunes antes de diseñar las relaciones.
  • Prefiere embebido para datos estáticos y de lectura frecuente.
  • Prefiere referencia para datos compartidos o que cambian a menudo.

9️⃣ Ejemplo con Mongoose (estructura del modelo)

const mongoose = require("mongoose");

const ProductSchema = new mongoose.Schema({
  name: String,
  sku: { type: String, unique: true },
  price: Number,
  stock: Number
});

const OrderSchema = new mongoose.Schema({
  userId: mongoose.Schema.Types.ObjectId,
  items: [{
    productId: mongoose.Schema.Types.ObjectId,
    name: String,
    price: Number,
    qty: Number
  }],
  total: Number,
  createdAt: { type: Date, default: Date.now }
});

const Product = mongoose.model("Product", ProductSchema);
const Order = mongoose.model("Order", OrderSchema);
  

🔁 Resumen rápido

  1. Define tus consultas más comunes.
  2. Embeber datos si siempre se usan juntos.
  3. Referenciar si los datos son grandes o compartidos.
  4. Combina ambos enfoques si es necesario (patrón mixto).
  5. Evita crecer documentos sin límite → usa colecciones separadas.

Publicar un comentario

0 Comentarios