Ya sabes qué es MongoDB, cómo funciona un documento, cómo hacer operaciones CRUD básicas. Ahora llega la parte donde la mayoría de los desarrolladores se quedan cortos: el Aggregation Pipeline, el diseño correcto de esquemas, los índices que marcan la diferencia y las capacidades de búsqueda vectorial que hacen de MongoDB una herramienta imprescindible en proyectos de IA.
Este artículo da un paso más allá de lo básico. Si aún no tienes claro qué es MongoDB o cómo funciona internamente, te recomendamos leer primero nuestra guía de MongoDB para principiantes. Si vienes con bases sólidas, sigue leyendo — aquí aprenderás a usarlo como un profesional.
1. El Aggregation Pipeline — el motor de consultas avanzado
El Aggregation Pipeline es la herramienta más poderosa de MongoDB. Permite procesar documentos a través de una serie de etapas (stages), donde cada etapa transforma, filtra, agrupa o enriquece los datos antes de pasarlos a la siguiente.
Piénsalo como una cadena de montaje: los documentos entran en bruto y salen transformados, filtrados, agrupados y calculados exactamente como necesitas.
db.coleccion.aggregate([
{ $etapa1: { ... } }, // transforma documentos
{ $etapa2: { ... } }, // filtra o agrupa
{ $etapa3: { ... } }, // proyecta campos
// ...
])Las etapas más importantes
$match — filtrar documentos (siempre primero)
// Equivalente a WHERE en SQL
// REGLA DE ORO: usa $match lo antes posible para reducir documentos
db.pedidos.aggregate([
{
$match: {
estado: "completado",
fecha: { $gte: new Date("2026-01-01") },
total: { $gt: 100 }
}
}
// Solo los documentos que pasen este filtro avanzan al siguiente stage
])$group — agrupar y calcular
// Equivalente a GROUP BY + funciones de agregación en SQL
db.pedidos.aggregate([
{ $match: { estado: "completado" } },
{
$group: {
_id: "$cliente_id", // agrupar por cliente
total_gastado: { $sum: "$total" }, // suma de totales
num_pedidos: { $count: {} }, // contar pedidos
ticket_medio: { $avg: "$total" }, // media
primer_pedido: { $min: "$fecha" }, // mínimo
ultimo_pedido: { $max: "$fecha" } // máximo
}
},
{ $sort: { total_gastado: -1 } }, // ordenar por gasto desc
{ $limit: 10 } // top 10 clientes
])
// Resultado:
// { _id: "cliente_42", total_gastado: 4580.50, num_pedidos: 23, ... }
// { _id: "cliente_17", total_gastado: 3920.00, num_pedidos: 15, ... }$project — seleccionar y transformar campos
db.usuarios.aggregate([
{
$project: {
nombre_completo: { $concat: ["$nombre", " ", "$apellido"] },
email: 1, // incluir
edad: 1,
password: 0, // excluir (seguridad)
_id: 0, // excluir _id
año_registro: { $year: "$fecha_registro" }, // extraer año
es_adulto: { $gte: ["$edad", 18] } // campo calculado
}
}
])$lookup — unir colecciones (el JOIN de MongoDB)
// Unir pedidos con datos del cliente — equivalente a JOIN en SQL
// Esta es la alternativa a los JOINs relacionales en MongoDB
db.pedidos.aggregate([
{
$lookup: {
from: "clientes", // colección a unir
localField: "cliente_id", // campo en pedidos
foreignField: "_id", // campo en clientes
as: "cliente" // nombre del campo resultado
}
},
{
$unwind: "$cliente" // desenvuelve el array resultado a un objeto
},
{
$project: {
"cliente.password": 0 // excluir campos sensibles del cliente
}
}
])
// Resultado:
// {
// _id: ObjectId(...),
// total: 299.99,
// cliente: {
// nombre: "Ana García",
// email: "ana@ejemplo.com",
// ciudad: "Madrid"
// }
// }$lookup con pipeline — joins avanzados
// $lookup con pipeline: más flexible y potente
// Permite filtrar y transformar durante el join
db.pedidos.aggregate([
{
$lookup: {
from: "productos",
let: { ids_productos: "$items.producto_id" },
pipeline: [
{
$match: {
$expr: { $in: ["$_id", "$$ids_productos"] }
}
},
{
$project: { nombre: 1, precio: 1, categoria: 1 }
}
],
as: "productos_detalle"
}
}
])$unwind — expandir arrays
// Si un documento tiene un array, $unwind crea un documento por cada elemento
// Documento original:
// { pedido_id: 1, items: ["laptop", "mouse", "teclado"] }
db.pedidos.aggregate([
{ $unwind: "$items" }
])
// Resultado (3 documentos):
// { pedido_id: 1, items: "laptop" }
// { pedido_id: 1, items: "mouse" }
// { pedido_id: 1, items: "teclado" }
// Con opciones para preservar documentos vacíos:
{ $unwind: { path: "$items", preserveNullAndEmptyArrays: true } }$addFields y $set — añadir campos calculados
db.productos.aggregate([
{
$addFields: {
precio_con_iva: { $multiply: ["$precio", 1.21] },
en_stock: { $gt: ["$stock", 0] },
categoria_upper: { $toUpper: "$categoria" },
descripcion_corta: {
$substr: ["$descripcion", 0, 100] // primeros 100 caracteres
}
}
}
])$facet — múltiples pipelines en paralelo
// Ejecuta múltiples sub-pipelines sobre los mismos documentos
// Ideal para páginas con filtros + resultados + estadísticas simultáneos
db.productos.aggregate([
{ $match: { categoria: "tecnologia" } },
{
$facet: {
// Pipeline 1: resultados paginados
resultados: [
{ $sort: { precio: 1 } },
{ $skip: 0 },
{ $limit: 20 }
],
// Pipeline 2: total de resultados
total: [
{ $count: "cantidad" }
],
// Pipeline 3: rango de precios
rango_precios: [
{
$group: {
_id: null,
min: { $min: "$precio" },
max: { $max: "$precio" },
promedio: { $avg: "$precio" }
}
}
],
// Pipeline 4: distribución por subcategoría
por_subcategoria: [
{ $group: { _id: "$subcategoria", total: { $sum: 1 } } }
]
}
}
])
// Una sola consulta devuelve resultados, totales y estadísticas2. Pipeline completo real: análisis de ventas
// Reporte: top 5 categorías por ingresos en el último trimestre,
// con ticket medio y número de pedidos
db.pedidos.aggregate([
// 1. Filtrar por fecha (limita documentos lo antes posible)
{
$match: {
fecha: { $gte: new Date("2026-01-01"), $lt: new Date("2026-04-01") },
estado: { $in: ["completado", "entregado"] }
}
},
// 2. Expandir los items de cada pedido
{ $unwind: "$items" },
// 3. Unir con la colección de productos para obtener la categoría
{
$lookup: {
from: "productos",
localField: "items.producto_id",
foreignField: "_id",
as: "producto"
}
},
{ $unwind: "$producto" },
// 4. Agrupar por categoría
{
$group: {
_id: "$producto.categoria",
ingresos_totales: { $sum: { $multiply: ["$items.cantidad", "$items.precio"] } },
num_pedidos: { $count: {} },
ticket_medio: { $avg: { $multiply: ["$items.cantidad", "$items.precio"] } }
}
},
// 5. Añadir campo calculado
{
$addFields: {
ticket_medio: { $round: ["$ticket_medio", 2] }
}
},
// 6. Ordenar por ingresos
{ $sort: { ingresos_totales: -1 } },
// 7. Top 5
{ $limit: 5 },
// 8. Proyectar solo lo necesario
{
$project: {
categoria: "$_id",
ingresos_totales: 1,
num_pedidos: 1,
ticket_medio: 1,
_id: 0
}
}
])3. Índices avanzados — el rendimiento que marca la diferencia
Como ya vimos en nuestro artículo sobre índices en SQL, los índices son la diferencia entre una consulta de milisegundos y una de minutos. En MongoDB el principio es el mismo, pero con algunas particularidades importantes.
Tipos de índices en MongoDB
// Índice simple — el más básico
db.usuarios.createIndex({ email: 1 }) // 1 = ascendente
db.usuarios.createIndex({ fecha: -1 }) // -1 = descendente
// Índice único — garantiza unicidad
db.usuarios.createIndex({ email: 1 }, { unique: true })
// Índice compuesto — múltiples campos
// El ORDEN IMPORTA: diseña según tus patrones de consulta
db.pedidos.createIndex({ cliente_id: 1, fecha: -1 })
// Optimiza consultas como:
// db.pedidos.find({ cliente_id: "x" }).sort({ fecha: -1 })
// Índice de texto completo — búsqueda full-text
db.articulos.createIndex({
titulo: "text",
contenido: "text",
tags: "text"
}, {
weights: { titulo: 10, contenido: 5, tags: 3 }, // importancia relativa
default_language: "spanish"
})
// Usar el índice de texto:
db.articulos.find({
$text: { $search: "inteligencia artificial machine learning" }
}, {
score: { $meta: "textScore" } // añadir puntuación de relevancia
}).sort({ score: { $meta: "textScore" } })
// Índice geoespacial — para ubicaciones
db.restaurantes.createIndex({ ubicacion: "2dsphere" })
db.restaurantes.find({
ubicacion: {
$near: {
$geometry: { type: "Point", coordinates: [-3.7038, 40.4168] },
$maxDistance: 2000 // 2km en metros
}
}
})
// Índice TTL — documentos que expiran automáticamente
db.sesiones.createIndex(
{ creado_en: 1 },
{ expireAfterSeconds: 3600 } // documerntos expiran en 1 hora
)
// Índice parcial — solo indexa documentos que cumplen una condición
db.pedidos.createIndex(
{ fecha: 1 },
{
partialFilterExpression: { estado: "pendiente" }
// Solo indexa pedidos pendientes — más pequeño y eficiente
}
)explain() — diagnosticar consultas lentas
// .explain() es el equivalente a EXPLAIN en SQL
// Te dice si la consulta usa índices o hace un COLLSCAN (full scan)
db.pedidos.find({ cliente_id: "abc123" }).explain("executionStats")
// Lo que necesitas mirar:
// ✅ IXSCAN → usa índice → rápido
// ❌ COLLSCAN → escaneo completo → necesita índice
// Resultado típico sin índice:
{
"stage": "COLLSCAN", // ← MAL: revisa todos los documentos
"nReturned": 5,
"docsExamined": 1000000, // ← examinó 1M documentos para devolver 5
"executionTimeMillisEstimate": 420 // 420ms
}
// Resultado con índice:
{
"stage": "IXSCAN", // ← BIEN: usa el índice
"nReturned": 5,
"docsExamined": 5, // ← solo examina los 5 documentos necesarios
"executionTimeMillisEstimate": 1 // 1ms
}La regla ESR para índices compuestos
// Regla ESR — el orden correcto para índices compuestos:
// E = Equality (igualdad) → primero
// S = Sort (ordenamiento) → segundo
// R = Range (rango: $gt, $lt, $in) → último
// Consulta:
db.pedidos.find({
cliente_id: "x", // E (igualdad)
estado: { $in: ["a","b"] } // R (rango)
}).sort({ fecha: -1 }) // S (sort)
// Índice correcto siguiendo ESR:
db.pedidos.createIndex({
cliente_id: 1, // E primero
fecha: -1, // S segundo (coincide con el sort)
estado: 1 // R último
})4. Diseño de esquemas — las decisiones que importan
A diferencia de SQL donde el diseño es siempre "normalizar al máximo", en MongoDB la pregunta es: ¿embebe o referencia? La respuesta depende de los patrones de acceso de tu aplicación.
Patrón 1: Embedding (embeber) — cuando los datos se usan juntos
// ✅ EMBEBER cuando:
// - Los datos siempre se leen juntos
// - Relación 1:pocos (máximo ~10-15 elementos)
// - Los subdocumentos no se acceden de forma independiente
// Ejemplo: post de blog con comentarios
{
"_id": ObjectId("..."),
"titulo": "MongoDB Avanzado",
"contenido": "...",
"autor": {
"nombre": "Ana García",
"avatar": "https://..."
},
"comentarios": [
{
"usuario": "Luis",
"texto": "Excelente artículo",
"fecha": ISODate("2026-04-01"),
"likes": 12
},
{
"usuario": "María",
"texto": "Muy útil, gracias",
"fecha": ISODate("2026-04-02"),
"likes": 5
}
],
"tags": ["mongodb", "nosql", "avanzado"]
}
// Una sola consulta trae todo lo necesario para mostrar el post
db.posts.findOne({ _id: postId }) // ← sin JOINsPatrón 2: Referencing (referenciar) — cuando los datos son independientes
// ✅ REFERENCIAR cuando:
// - Los datos se acceden de forma independiente
// - Relación 1:muchos (miles o millones de elementos)
// - Los datos se actualizan con frecuencia
// - Múltiples entidades referencian los mismos datos
// Ejemplo: pedido referenciando usuario y productos
{
"_id": ObjectId("..."),
"fecha": ISODate("2026-04-01"),
"cliente_id": ObjectId("abc123"), // referencia al usuario
"items": [
{
"producto_id": ObjectId("prod1"), // referencia al producto
"cantidad": 2,
"precio_unitario": 29.99 // ← precio al momento de compra (snapshot)
}
],
"total": 59.98
}
// Para ver el pedido completo, usas $lookup en el pipeline de agregaciónEl patrón Bucket — para series temporales
// ❌ Antipatrón: un documento por cada medición
// Con millones de sensores enviando datos cada segundo → millones de documentos
{ sensor_id: "s1", temperatura: 22.4, timestamp: ISODate("...") }
{ sensor_id: "s1", temperatura: 22.5, timestamp: ISODate("...") }
// ... millones de documentos pequeños → muy ineficiente
// ✅ Patrón Bucket: agrupa mediciones en "cubos" por hora
{
sensor_id: "s1",
hora: ISODate("2026-04-01T14:00:00Z"),
mediciones: [
{ minuto: 0, temperatura: 22.4 },
{ minuto: 1, temperatura: 22.5 },
// ... hasta 60 mediciones por documento
],
stats: {
min: 22.1,
max: 23.0,
promedio: 22.5,
count: 60
}
}
// Reduce el número de documentos 60x, mejora dramáticamente el rendimientoEl antipatrón del array ilimitado
// ❌ NUNCA hagas esto: arrays que crecen indefinidamente
{
usuario_id: "user1",
// Un usuario puede tener millones de seguidores
// El documento puede superar el límite de 16MB de MongoDB
seguidores: [
"user2", "user3", "user4", ... // potencialmente millones
]
}
// ✅ Inversión de relación: colección separada
// En su lugar, una colección "seguimientos":
{
seguidor_id: "user2",
seguido_id: "user1",
fecha: ISODate("...")
}
// Indexa seguidor_id y seguido_id para queries eficientes5. Transacciones en MongoDB
Desde MongoDB 4.0, las transacciones ACID están disponibles para operaciones que involucran múltiples documentos o colecciones. Si ya leíste nuestro artículo sobre transacciones en SQL, los conceptos son similares.
const { MongoClient } = require('mongodb');
const client = new MongoClient('mongodb://localhost:27017');
async function transferirDinero(origenId, destinoId, importe) {
const session = client.startSession();
try {
await session.withTransaction(async () => {
const cuentas = client.db('banco').collection('cuentas');
// Verificar saldo con bloqueo
const origen = await cuentas.findOne(
{ _id: origenId },
{ session }
);
if (origen.saldo < importe) {
throw new Error('Saldo insuficiente');
}
// Descontar del origen
await cuentas.updateOne(
{ _id: origenId },
{ $inc: { saldo: -importe } },
{ session }
);
// Acreditar en el destino
await cuentas.updateOne(
{ _id: destinoId },
{ $inc: { saldo: importe } },
{ session }
);
// Registrar el movimiento
await client.db('banco')
.collection('movimientos')
.insertOne({
origen: origenId,
destino: destinoId,
importe,
fecha: new Date()
}, { session });
// Si llegamos aquí, todo fue bien → COMMIT automático
});
console.log('✅ Transferencia completada');
} catch (error) {
console.error('❌ Error — ROLLBACK automático:', error.message);
throw error;
} finally {
session.endSession();
}
}⚠️ Importante: Las transacciones en MongoDB tienen un costo de rendimiento. Úsalas solo cuando realmente necesites atomicidad entre múltiples documentos. Para la mayoría de operaciones, el modelo de atomicidad a nivel de documento de MongoDB es suficiente.
6. Mongoose avanzado — esquemas, middlewares y plugins
const mongoose = require('mongoose');
// ============================================
// ESQUEMA CON VALIDACIONES AVANZADAS
// ============================================
const productoSchema = new mongoose.Schema({
nombre: {
type: String,
required: [true, 'El nombre es obligatorio'],
trim: true,
minlength: [3, 'Mínimo 3 caracteres'],
maxlength: [100, 'Máximo 100 caracteres']
},
precio: {
type: Number,
required: true,
min: [0, 'El precio no puede ser negativo'],
get: v => Math.round(v * 100) / 100 // siempre 2 decimales al leer
},
categoria: {
type: String,
enum: {
values: ['tecnologia', 'ropa', 'hogar', 'deportes'],
message: '{VALUE} no es una categoría válida'
}
},
etiquetas: [{ type: String, lowercase: true, trim: true }],
creado_en: { type: Date, default: Date.now },
activo: { type: Boolean, default: true }
}, {
timestamps: true, // añade createdAt y updatedAt automáticamente
versionKey: false // elimina el campo __v
});
// ============================================
// ÍNDICES DEFINIDOS EN EL ESQUEMA
// ============================================
productoSchema.index({ nombre: 'text', descripcion: 'text' });
productoSchema.index({ categoria: 1, precio: 1 });
productoSchema.index({ activo: 1, creado_en: -1 });
// ============================================
// CAMPOS VIRTUALES — calculados, no guardados en BD
// ============================================
productoSchema.virtual('precio_con_iva').get(function() {
return (this.precio * 1.21).toFixed(2);
});
productoSchema.virtual('es_nuevo').get(function() {
const diasDesdeCreacion = (Date.now() - this.creado_en) / (1000 * 60 * 60 * 24);
return diasDesdeCreacion < 30;
});
// ============================================
// MIDDLEWARES (hooks) — ejecutan antes/después de operaciones
// ============================================
// Pre-save: antes de guardar
productoSchema.pre('save', function(next) {
// Normalizar nombre a Title Case
this.nombre = this.nombre
.toLowerCase()
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
next();
});
// Post-save: después de guardar
productoSchema.post('save', function(doc) {
console.log(`Producto guardado: ${doc.nombre} (${doc._id})`);
});
// Pre-find: añadir filtro automático en todas las consultas
productoSchema.pre(/^find/, function(next) {
// Solo devuelve productos activos por defecto
this.where({ activo: { $ne: false } });
next();
});
// ============================================
// MÉTODOS DE INSTANCIA — en un documento específico
// ============================================
productoSchema.methods.aplicarDescuento = function(porcentaje) {
this.precio = this.precio * (1 - porcentaje / 100);
return this.save();
};
// ============================================
// MÉTODOS ESTÁTICOS — en el modelo
// ============================================
productoSchema.statics.findPorCategoria = function(categoria) {
return this.find({ categoria }).sort({ precio: 1 });
};
productoSchema.statics.masVendidos = function(limit = 10) {
return this.aggregate([
{ $sort: { ventas: -1 } },
{ $limit: limit },
{ $project: { nombre: 1, precio: 1, ventas: 1 } }
]);
};
// ============================================
// PLUGIN REUTILIZABLE — añade funcionalidad a múltiples esquemas
// ============================================
function softDeletePlugin(schema) {
schema.add({ eliminado: { type: Boolean, default: false } });
schema.add({ eliminado_en: Date });
schema.methods.softDelete = function() {
this.eliminado = true;
this.eliminado_en = new Date();
return this.save();
};
schema.pre(/^find/, function() {
this.where({ eliminado: { $ne: true } });
});
}
// Aplicar el plugin a cualquier esquema:
productoSchema.plugin(softDeletePlugin);
const Producto = mongoose.model('Producto', productoSchema);7. Vector Search — MongoDB para aplicaciones de IA
En 2026, MongoDB Atlas Vector Search es una de las características más relevantes para desarrolladores que trabajan con IA. Permite almacenar embeddings (vectores numéricos que representan el significado semántico de texto o imágenes) y hacer búsquedas por similitud semántica.
Esto es la base de los sistemas RAG (Retrieval-Augmented Generation) — la técnica que permite a ChatGPT y otros modelos responder preguntas sobre tus propios documentos.
// 1. Crear el índice vectorial en MongoDB Atlas
// (se configura desde la UI de Atlas o con la API)
// {
// "fields": [{
// "type": "vector",
// "path": "embedding",
// "numDimensions": 1536, ← dimensiones del modelo (OpenAI: 1536)
// "similarity": "cosine" ← métrica de similitud
// }]
// }
// 2. Insertar documentos con sus embeddings
const { OpenAI } = require('openai');
const openai = new OpenAI();
async function indexarDocumento(db, texto, metadata) {
// Generar embedding con OpenAI
const response = await openai.embeddings.create({
model: "text-embedding-3-small",
input: texto
});
const embedding = response.data[0].embedding; // vector de 1536 dimensiones
await db.collection('documentos').insertOne({
texto,
embedding,
...metadata,
indexado_en: new Date()
});
}
// 3. Búsqueda semántica — encuentra documentos por significado, no por palabras exactas
async function busquedaSemantica(db, consulta, limite = 5) {
// Generar embedding de la consulta
const response = await openai.embeddings.create({
model: "text-embedding-3-small",
input: consulta
});
const queryEmbedding = response.data[0].embedding;
// Búsqueda vectorial con el pipeline de agregación
const resultados = await db.collection('documentos').aggregate([
{
$vectorSearch: {
index: "vector_index",
path: "embedding",
queryVector: queryEmbedding,
numCandidates: 100, // candidatos a considerar
limit: limite // resultados a devolver
}
},
{
$project: {
texto: 1,
score: { $meta: "vectorSearchScore" }, // puntuación de relevancia
embedding: 0 // no devolver el vector (ahorra ancho de banda)
}
}
]).toArray();
return resultados;
}
// Ejemplo de uso:
// busquedaSemantica(db, "¿cómo optimizar consultas en bases de datos?")
// → Devuelve los 5 documentos más semánticamente relacionados
// aunque no contengan exactamente esas palabras8. Rendimiento: patrones que debes conocer
Proyecciones — solo trae lo que necesitas
// ❌ Malo: trae todos los campos aunque solo necesites 2
db.usuarios.find({ activo: true })
// ✅ Proyección: solo los campos necesarios
db.usuarios.find(
{ activo: true },
{ nombre: 1, email: 1, _id: 0 } // 1=incluir, 0=excluir
)
// Reduce dramáticamente el uso de red y memoriaPaginación eficiente con cursor
// ❌ Paginación con skip — lento en páginas grandes
// skip(10000) obliga a MongoDB a saltar 10,000 documentos
db.productos.find().skip(10000).limit(20)
// ✅ Paginación con cursor — eficiente a cualquier profundidad
// Usa el _id del último documento de la página anterior
db.productos.find({
_id: { $gt: ultimo_id_de_pagina_anterior }
}).limit(20).sort({ _id: 1 })
// MongoDB usa el índice del _id directamente, sin saltarContador eficiente
// ❌ count() con filtros complejos puede ser lento
db.pedidos.find({ estado: "pendiente" }).count()
// ✅ countDocuments() usa el índice si existe
db.pedidos.countDocuments({ estado: "pendiente" })
// ✅ Para estimados rápidos del total (sin filtros)
db.pedidos.estimatedDocumentCount()
// Lee el total de los metadatos de la colección → instantáneoResumen: lo que aprendiste hoy
- ✅ El Aggregation Pipeline transforma documentos en etapas: $match, $group, $lookup, $unwind, $facet y más
- ✅
$matchsiempre lo primero para filtrar antes de procesar - ✅
$lookupes el JOIN de MongoDB — con pipeline para joins avanzados - ✅
$facetejecuta múltiples pipelines en paralelo — ideal para páginas con filtros - ✅ La regla ESR para índices compuestos: Equality → Sort → Range
- ✅
explain("executionStats")diagnostica si se usa índice (IXSCAN) o full scan (COLLSCAN) - ✅ Embeber vs Referenciar: embebe cuando los datos se leen juntos; referencia cuando son independientes o crecen sin límite
- ✅ El patrón Bucket agrupa datos de series temporales para eficiencia máxima
- ✅ Las transacciones ACID están disponibles desde MongoDB 4.0 para múltiples documentos
- ✅ Mongoose avanzado: middlewares pre/post, métodos estáticos, campos virtuales y plugins reutilizables
- ✅ Vector Search convierte MongoDB en el backend ideal para aplicaciones de IA con búsqueda semántica
- ✅ Proyecciones, paginación por cursor y
countDocuments()son claves para el rendimiento
🧪 ¿Tienes bien claros los fundamentos de bases de datos NoSQL?
Para aprovechar MongoDB a nivel avanzado necesitas tener sólidos los conceptos de NoSQL y saber cuándo usarlo frente a SQL. Comprueba dónde estás:
👉 Test: SQL vs NoSQL 👉 Test: NoSQL Fundamentos y Tipos
¿Cuál parte te resultó más útil: el Aggregation Pipeline, el diseño de esquemas o el Vector Search? ¿Ya usabas transacciones en MongoDB o es la primera vez que lo ves? Cuéntanos en los comentarios 👇 — respondemos todos. 🚀
No hay comentarios todavía. Sé el primero en compartir tu opinión.