Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tutosrive/db-nosql-2026-1/llms.txt

Use this file to discover all available pages before exploring further.

Una de las decisiones más importantes al diseñar un sistema con MongoDB es elegir entre dos estrategias de modelado: documentos embebidos (desnormalización) y documentos referenciados (normalización). A diferencia de los sistemas relacionales donde las relaciones se modelan casi siempre con claves foráneas y JOINs, en MongoDB la respuesta depende del patrón de acceso a los datos, el volumen de los subdocumentos y la frecuencia con la que cada parte de la información se consulta o actualiza de forma independiente.

Documentos Embebidos

En el modelo embebido, los datos relacionados se almacenan directamente dentro del mismo documento, formando una estructura jerárquica anidada. MongoDB permite múltiples niveles de anidamiento sin límite de profundidad (aunque se recomienda no superar 3-4 niveles por claridad).

¿Cuándo usar documentos embebidos?

  • Los datos relacionados se consultan siempre juntos.
  • La relación es uno-a-pocos (un usuario con 3-5 aficiones, un vehículo con sus comparendos).
  • Los subdocumentos no se actualizan de forma independiente con frecuencia.
  • Se quiere maximizar el rendimiento en lecturas evitando operaciones $lookup.

Ejercicio de clase: dbvehiculos

El siguiente ejemplo proviene directamente de las notas del curso. Se modela la base de datos dbvehiculos con una colección persona_vehiculo, embebiendo la información del vehículo y su lista de comparendos en tres niveles de anidamiento:
use dbvehiculos

db.persona_vehiculo.insertMany([
  {
    cedulapersona: "12345678",
    nombrepersona: "Santiago Rivera",
    ciudad: "Manizales",
    vehiculo: {
      placa: "ABC123",
      marca: "Renault",
      modelo: 2018,
      comparendos: [
        { tipo: "Exceso de velocidad" },
        { tipo: "Semáforo en rojo" },
        { tipo: "Parqueo indebido" }
      ]
    }
  },
  {
    cedulapersona: "87654321",
    nombrepersona: "Laura Torres",
    ciudad: "Bogotá",
    vehiculo: {
      placa: "XYZ789",
      marca: "Chevrolet",
      modelo: 2020,
      comparendos: [
        { tipo: "No usar cinturón" },
        { tipo: "Celular en conducción" }
      ]
    }
  },
  {
    cedulapersona: "11223344",
    nombrepersona: "Andrés Díaz",
    ciudad: "Medellín",
    vehiculo: {
      placa: "MNO456",
      marca: "Toyota",
      modelo: 2021,
      comparendos: [
        { tipo: "Invasión de carril" }
      ]
    }
  }
])
Niveles de anidamiento:
  1. Nivel 1 — Documento raíz: cedulapersona, nombrepersona, ciudad.
  2. Nivel 2 — Subdocumento vehiculo: placa, marca, modelo, comparendos.
  3. Nivel 3 — Arreglo de subdocumentos comparendos: cada elemento con la clave tipo.
MongoDB impone un límite de 16 MB por documento. Si el arreglo embebido puede crecer sin límite (por ejemplo, todos los comparendos históricos de un vehículo durante décadas), considera referenciar en lugar de embeber para evitar superar este límite.

Historial médico con arreglo de subdocumentos

Otro ejercicio de clase consiste en insertar, dentro de un documento de paciente, un campo historial que contenga un subarreglo llamado medicos. Cada elemento del arreglo tiene dos claves: nombre y especialidad:
db.pacientes.insertOne({
  cedula: "98765432",
  nombre: "Laura Torres",
  historial: {
    medicos: [
      { nombre: "Dr. Óscar Bedoya",   especialidad: "Cardiología" },
      { nombre: "Dra. María Ríos",    especialidad: "Medicina Interna" }
    ]
  }
})

Documentos Referenciados

En el modelo referenciado, los documentos relacionados se almacenan en colecciones separadas y se vinculan mediante un campo identificador (equivalente a una clave foránea en SQL). Para unir los datos en una consulta se utiliza la etapa $lookup dentro de un pipeline de agregación.

¿Cuándo usar documentos referenciados?

  • La relación es uno-a-muchos o muchos-a-muchos (un usuario con cientos de partidas, un equipo con decenas de partidos).
  • Los subdocumentos se consultan o actualizan de forma independiente con frecuencia.
  • El subdocumento puede crecer sin límite en el tiempo.
  • Se quiere evitar duplicación de datos que cambian (ej. datos de un equipo referenciados desde múltiples partidos).

Ejemplo: usuarios y partidos

Las colecciones usuarios y partidos del curso se modelan naturalmente como referencias, ya que un partido involucra a múltiples equipos y un equipo puede aparecer en muchos partidos:
// Colección: usuarios
db.usuarios.insertOne({
  user_id: 3,
  nombre: "Paula Rojas",
  alias: "projas3",
  correo: "projas3@uni.edu",
  programa: "Administración",
  semestre: 3,
  plan: "premium",
  puntos_acumulados: 0
})

// Colección: partidos (esquema real del curso)
db.partidos.insertOne({
  match_id: 101,
  torneo: "Liga Uni",
  fecha: new Date("2026-04-01T18:00:00"),
  local: "Tiburones",
  visitante: "Pumas",
  estado: "programado",
  sede: "Estadio Central",
  resultado: null
})
Los documentos usuarios y partidos conviven en colecciones separadas y se relacionan desde la lógica de aplicación o mediante $lookup. Por ejemplo, una colección apuestas puede referenciar tanto user_id como match_id sin necesidad de duplicar la información de cada colección.

Unir colecciones con $lookup

// Unir una colección apuestas con usuarios para obtener el nombre del apostador
db.apuestas.aggregate([
  {
    $lookup: {
      from: "usuarios",        // colección a unir
      localField: "user_id",   // campo en apuestas
      foreignField: "user_id", // campo en usuarios
      as: "datos_usuario"      // nombre del arreglo resultante
    }
  },
  { $limit: 5 }
])

Cuándo Usar Cada Uno

CriterioEmbebidoReferenciado
Acceso conjunto✅ Ideal — una sola consultaRequiere $lookup
Relación 1:pocos✅ IdealInnecesario / sobrediseño
Relación 1:muchos⚠️ Puede crecer indefinidamente✅ Ideal
Relación muchos:muchos❌ Duplicación masiva✅ Ideal
Actualización independiente⚠️ Requiere actualizar el padre✅ Simple y directa
Documentos < 16 MB✅ Siempre OKRequerido si el doc excede el límite
Rendimiento en lectura✅ Alta (sin JOIN)Media (con $lookup)
Consistencia de datos⚠️ Sin transacciones puede desincronizarse✅ Fuente única de verdad

Consultar Documentos Embebidos

Para filtrar por campos dentro de subdocumentos se utiliza la notación de punto ("campo.subcampo"). Esta sintaxis funciona en filtros, proyecciones y operaciones de actualización.
// Buscar por placa dentro del subdocumento vehiculo
db.persona_vehiculo.find({ "vehiculo.placa": "ABC123" })

// Buscar por tipo de comparendo dentro del arreglo anidado
db.persona_vehiculo.find({ "vehiculo.comparendos.tipo": "Exceso de velocidad" })

// Proyección: mostrar solo la placa y los comparendos, sin _id
db.persona_vehiculo.find(
  { "vehiculo.placa": "ABC123" },
  { "vehiculo.placa": 1, "vehiculo.comparendos": 1, _id: 0 }
)

// Filtrar por especialidad dentro del arreglo medicos
db.pacientes.find({ "historial.medicos.especialidad": "Cardiología" })
Cuando necesitas filtrar por un elemento específico dentro de un arreglo de subdocumentos (y no simplemente verificar que algún elemento del arreglo cumpla la condición), utiliza el operador $elemMatch:
db.pacientes.find({
  "historial.medicos": {
    $elemMatch: { especialidad: "Cardiología", nombre: /^Dr\./ }
  }
})

Historial con Médicos

Este ejercicio directo de clase modela un historial clínico donde dentro del campo historial existe un arreglo medicos compuesto de subdocumentos con claves nombre y especialidad:
use dbclinica

// Insertar paciente con historial médico embebido
db.pacientes.insertOne({
  cedula: "11223344",
  nombre: "Andrés Díaz",
  ciudad: "Manizales",
  historial: {
    medicos: [
      { nombre: "Dr. Óscar Bedoya",    especialidad: "Cardiología" },
      { nombre: "Dra. Patricia Serna", especialidad: "Neurología" }
    ]
  }
})

// Agregar un médico al historial existente usando $push
db.pacientes.updateOne(
  { cedula: "11223344" },
  {
    $push: {
      "historial.medicos": {
        nombre: "Dr. Juan Campos",
        especialidad: "Traumatología"
      }
    }
  }
)

// Consultar todos los pacientes con médico de Cardiología
db.pacientes.find({ "historial.medicos.especialidad": "Cardiología" })

// Mostrar solo el nombre del paciente y su lista de médicos
db.pacientes.find(
  {},
  { nombre: 1, "historial.medicos": 1, _id: 0 }
)
El operador $push es la forma idiomática de agregar elementos a un arreglo existente sin sobreescribir los elementos anteriores. Si usaras $set sobre el campo del arreglo, reemplazarías todo el arreglo con el nuevo valor.

Build docs developers (and LLMs) love