Ir al contenido
angel.library

MongoDB práctico — documentos, índices y agregaciones

MongoDB almacena documentos BSON en colecciones. BSON (Binary JSON) es un formato binario con tipos adicionales como fechas, ObjectId y decimales. Un documento flexible sigue necesitando una forma entendida por productores y consumidores.

RelacionalMongoDBMatiz
tablacolecciónlos documentos pueden variar de forma
filadocumentocontiene campos y estructuras anidadas
columnacampopuede no existir en todos los documentos
PK_ides único e indexado
join$lookup o modelado embebidose usa con intención, no como base de todo el modelo
GROUP BY$group en aggregationforma parte de un pipeline
{
_id: ObjectId("..."),
customerId: ObjectId("..."),
status: "paid",
shippingAddress: {
city: "Bogotá",
country: "CO"
},
items: [
{
productId: ObjectId("..."),
name: "Teclado",
unitPriceCents: 120000,
quantity: 1
}
],
createdAt: ISODate("2026-08-28T15:00:00Z")
}

El pedido es un agregado: dirección e ítems se leen juntos y representan la compra histórica. customerId referencia una entidad compartida. Evita que items crezca sin límite; para conversaciones, logs o historiales extensos suele convenir otra colección o un patrón de segmentos.

Embeber cuando:

  • los datos se consultan y actualizan como una unidad;
  • la cantidad está acotada;
  • necesitas atomicidad de un solo documento;
  • la copia representa una fotografía histórica.

Referenciar cuando:

  • la entidad se comparte en muchos lugares;
  • cambia independientemente;
  • puede crecer sin límite;
  • necesitas consultarla por sí misma.

No intentes eliminar toda duplicación. Documenta qué copia es histórica, cuál debe sincronizarse y cuál es la fuente de verdad.

MongoDB puede validar documentos con JSON Schema:

db.createCollection("orders", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["customerId", "status", "items", "createdAt"],
properties: {
status: {
enum: ["pending", "paid", "cancelled"]
},
items: {
bsonType: "array",
minItems: 1
},
createdAt: {
bsonType: "date"
}
}
}
}
})

La validación de la aplicación ofrece mensajes de dominio; la colección evita que otros procesos inserten formas imposibles. Evoluciona el validador junto con migraciones y versiones antiguas.

const order = await db.collection("orders").insertOne({
customerId,
status: "pending",
items,
createdAt: new Date()
})
const recent = await db
.collection("orders")
.find({ customerId, status: "paid" })
.project({ status: 1, createdAt: 1, totalCents: 1 })
.sort({ createdAt: -1, _id: -1 })
.limit(20)
.toArray()

find define el filtro; project limita campos; sort necesita un desempate estable; limit acota el resultado. Nunca construyas operadores desde objetos externos sin validar: propiedades como $where, $gt o claves inesperadas pueden cambiar la consulta.

const result = await db.collection("products").findOneAndUpdate(
{ _id: productId, stock: { $gte: quantity } },
{
$inc: { stock: -quantity },
$set: { updatedAt: new Date() }
},
{ returnDocument: "after" }
)

$inc modifica atómicamente el número en el documento. $set cambia campos concretos; $unset los elimina; $push añade a un arreglo y $addToSet evita repetir un valor exacto. Aun con operaciones atómicas, limita arreglos y comprueba si el filtro encontró documento.

db.orders.createIndex(
{ customerId: 1, createdAt: -1, _id: -1 },
{ name: "orders_customer_recent" }
)
db.users.createIndex({ email: 1 }, { unique: true, name: "users_email_unique" })

Los índices compuestos respetan orden y prefijos. Un índice multikey indexa campos de arrays; tiene restricciones al combinar varios arrays. Un índice único protege integridad, pero revisa cómo trata documentos sin el campo y valores nulos. Usa explain("executionStats") para comparar claves y documentos examinados.

Demasiados índices aumentan RAM, almacenamiento y costo de cada escritura. Conserva únicamente los que protegen reglas o respaldan consultas relevantes.

db.orders.aggregate([
{
$match: {
status: "paid",
createdAt: { $gte: ISODate("2026-08-01T00:00:00Z") }
}
},
{
$group: {
_id: "$customerId",
orders: { $sum: 1 },
revenueCents: { $sum: "$totalCents" }
}
},
{ $sort: { revenueCents: -1 } },
{ $limit: 20 }
])

Un pipeline transforma documentos etapa por etapa. Coloca filtros selectivos temprano para reducir trabajo. $project cambia la forma, $unwind genera una salida por elemento de array, $lookup combina colecciones y $facet ejecuta varias ramas. Observa uso de memoria, disco e índices en pipelines grandes.

Una modificación de un solo documento es atómica. Esto favorece embeber datos que forman una unidad. MongoDB también permite transacciones multidocumento en configuraciones compatibles, pero agregan costo y no compensan un modelo que fragmenta cada agregado sin necesidad.

Usa transacción cuando varios cambios deben confirmarse juntos. Manténla corta, usa la misma sesión y trata errores/reintentos recomendados por el driver.

  • write concern define qué confirmación exige una escritura;
  • read concern define garantías de la lectura;
  • read preference elige qué miembros pueden atenderla.

No aumentes o reduzcas estas garantías sin describir el caso. Leer de una secundaria puede devolver datos atrasados; una confirmación más fuerte aumenta durabilidad y posiblemente latencia.

  • una colección “sin esquema” con campos y tipos incompatibles;
  • documento o array que crece indefinidamente;
  • un índice por cada campo sin medir escrituras;
  • duplicar datos sin reconciliación;
  • pasar filtros JSON del cliente directamente al driver;
  • usar $lookup para reconstruir en cada consulta un modelo completamente relacional;
  • asumir que réplica equivale a backup.