MongoDB Academy · Lección

$sort, $limit y $skip en el pipeline

Ordenará y paginará los resultados de agregación, y comprenderá las reglas del optimizador para adelantar $match y $sort antes de $group.

Lección 4 de 413 pasos

$sort, $limit y $skip en el pipeline es una lección gratuita de MongoDB Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de MongoDB Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de MongoDB Academy incluye 4 lecciones en total.

Descripción general: ordenar y paginar resultados

Tres etapas del pipeline de agregación controlan el orden y el volumen de los resultados: $sort ordena los documentos por uno o varios campos, $limit toma solo los primeros N documentos y $skip descarta los primeros N documentos. Juntas permiten implementar la ordenación y la paginación. Entender dónde colocarlas en el pipeline tiene un gran impacto en el rendimiento.

La etapa $sort

La etapa $sort ordena los documentos según los valores de uno o varios campos. Use 1 para orden ascendente y -1 para orden descendente. Cuando se especifican varios campos, los documentos se ordenan primero por el primer campo, después por el segundo entre los que empatan, y así sucesivamente, exactamente igual que en la ordenación por varias claves de SQL o en find().sort() de MongoDB.

// Sort by revenue descending, then alphabetically by name for ties
db.products.aggregate([
  { $group: { _id: '$category', revenue: { $sum: '$price' } } },
  { $sort: { revenue: -1, _id: 1 } }  // revenue desc, category name asc
]);

$sort y el uso de índices

Cuando $sort es la primera etapa del pipeline (o aparece después de un $match que usa un índice), MongoDB puede usar un índice para realizar la ordenación y evitar por completo una ordenación en memoria. Cuando $sort aparece después de etapas que producen campos nuevos, como $group o $project, no hay ningún índice disponible y MongoDB realiza una ordenación en memoria. Las ordenaciones en memoria grandes (>100 MB) requieren allowDiskUse: true.

// Index-backed sort: $sort on indexed field before any transformation
db.orders.aggregate([
  { $match: { userId: 'u1' } },
  { $sort: { createdAt: -1 } }  // if createdAt is indexed, no in-memory sort
]);

// In-memory sort: $sort after $group (computed field, no index)
db.orders.aggregate([
  { $group: { _id: '$userId', total: { $sum: '$amount' } } },
  { $sort: { total: -1 } }  // in-memory: 'total' is a computed field
]);

La etapa $limit

$limit solo pasa las primeras N documentos a las etapas siguientes y descarta el resto. Es sencillo: se pasa un entero que especifica el número máximo de documentos que pueden continuar. Colocar $limit antes de etapas costosas como $lookup reduce considerablemente el número de combinaciones o búsquedas realizadas. Pregúntese siempre: «¿Puedo limitar antes?»

// Top 10 best-selling products
db.orders.aggregate([
  { $group: { _id: '$productId', sold: { $sum: '$qty' } } },
  { $sort: { sold: -1 } },
  { $limit: 10 },  // only top 10 pass through
  // Only 10 lookups instead of thousands:
  { $lookup: { from: 'products', localField: '_id', foreignField: '_id', as: 'product' } }
]);

La etapa $skip

$skip descarta los primeros N documentos del flujo. Es el equivalente en agregaciones de la paginación por desplazamiento: la página 2 comienza en skip 10 (suponiendo un límite de 10 por página). $skip siempre va después de $sort para producir resultados coherentes; omitir documentos sin ordenar genera páginas impredecibles. Al igual que SQL OFFSET, $skip es lento con desplazamientos grandes porque MongoDB debe procesar igualmente todos los documentos omitidos.

const PAGE = 2;
const PAGE_SIZE = 10;

db.articles.aggregate([
  { $match: { status: 'published' } },
  { $sort: { publishedAt: -1 } },
  { $skip: (PAGE - 1) * PAGE_SIZE },  // skip page 1's 10 docs
  { $limit: PAGE_SIZE }               // take page 2's 10 docs
]);

Optimizador: combinación de $sort + $limit

El optimizador de agregaciones de MongoDB detecta cuándo una etapa $sort va seguida inmediatamente de una etapa $limit y las combina en una ordenación de los N primeros elementos. En lugar de ordenar todos los documentos y descartar después la mayoría, MongoDB conserva únicamente los N candidatos principales en una cola de prioridad durante la ordenación, lo que utiliza O(N) de memoria en lugar de O(total). Esta optimización es automática: basta con escribir $sort seguido de $limit.

// This pattern triggers the top-N sort optimisation
db.reviews.aggregate([
  { $match: { productId: 'p1' } },
  { $sort: { rating: -1, helpful: -1 } },  // sort
  { $limit: 5 }   // $sort + $limit merged into top-5 sort internally
]);
// MongoDB never sorts ALL reviews; it tracks only the best 5

Reordenación del optimizador: $match y $sort

El optimizador de agregaciones también mueve automáticamente una etapa $match a una posición anterior del flujo cuando hacerlo es seguro. Por ejemplo, si $sort aparece antes de $match sobre un campo que $sort no calculó, el optimizador mueve $match antes de $sort para reducir el número de documentos que deben ordenarse. Puede observar los cambios del optimizador en el resultado de explain().

// You write:
db.orders.aggregate([
  { $sort: { amount: -1 } },
  { $match: { status: 'completed' } }  // optimizer moves this BEFORE $sort
]);

// Optimized execution (equivalent to):
db.orders.aggregate([
  { $match: { status: 'completed' } },  // fewer docs to sort
  { $sort: { amount: -1 } }
]);

Combinación de las cuatro: un endpoint de lista completo

Un endpoint de API realista que enumera productos con filtros, ordenación y paginación combina $match, $sort, $skip y $limit en el orden correcto. El patrón es siempre: filtrar → ordenar → omitir → limitar. Este orden garantiza que MongoDB utilice índices tanto para filtrar como, cuando sea posible, para ordenar antes de descartar los documentos innecesarios.

async function getProducts({ category, sort = 'price', page = 1, pageSize = 20 }) {
  return db.products.aggregate([
    { $match: { category, inStock: true } },   // 1. filter
    { $sort: { [sort]: 1 } },                  // 2. sort
    { $skip: (page - 1) * pageSize },          // 3. offset
    { $limit: pageSize }                       // 4. take page
  ]).toArray();
}

Rendimiento de $skip con desplazamientos profundos

El problema fundamental de $skip es que MongoDB aún debe procesar y descartar todos los documentos omitidos. En la página 1000, con 20 elementos por página, MongoDB descarta 19.980 documentos antes de devolver 20. El proceso se vuelve progresivamente más lento en las páginas más profundas. Para las API públicas de búsqueda o de feeds, cuyos usuarios rara vez pasan de la página 5-10, la paginación por desplazamiento es adecuada. Para una paginación profunda y de alta frecuencia, se necesita paginación por conjuntos de claves.

// Page 1000 is SLOW: MongoDB processes and discards 19,980 docs
db.posts.aggregate([
  { $sort: { createdAt: -1 } },
  { $skip: 19980 },  // 999 * 20
  { $limit: 20 }
]);

// Keyset pagination: always fast regardless of page depth
db.posts.aggregate([
  { $match: { createdAt: { $lt: lastSeenTimestamp } } },  // cursor
  { $sort: { createdAt: -1 } },
  { $limit: 20 }
]);

Límite de memoria de $sort y allowDiskUse

De forma predeterminada, una etapa $sort en memoria dentro del flujo de agregaciones está limitada a 100 MB. Si la ordenación supera este límite, el flujo falla con un error QueryExceededMemoryLimitNoDiskUseAllowed. Pase { allowDiskUse: true } como segundo argumento de aggregate() para permitir el uso del disco como memoria auxiliar. Como alternativa, aplique un $match al principio o utilice un índice adecuado para reducir el tamaño de entrada de la ordenación.

// Enable disk use for large sorts
db.events.aggregate(
  [
    { $match: { year: 2024 } },
    { $sort: { timestamp: -1 } },
    { $group: { _id: '$userId', events: { $push: '$type' } } }
  ],
  { allowDiskUse: true }  // allows spilling to disk
);

Obtención del recuento total junto con los resultados

Un patrón habitual de las API es devolver tanto los resultados paginados como el recuento total en una sola ida y vuelta a la base de datos. Utilice $facet para dividir el flujo en dos ramas paralelas: una para los resultados de $skip/$limit y otra para $count. Esto evita dos llamadas de agregación independientes y es más eficiente que contar todos los documentos por separado.

db.products.aggregate([
  { $match: { category: 'electronics' } },
  { $sort: { price: 1 } },
  { $facet: {
    // Branch 1: paginated results
    data: [
      { $skip: 0 },
      { $limit: 20 }
    ],
    // Branch 2: total count
    total: [
      { $count: 'count' }
    ]
  }}
]);
// Output: { data: [...20 docs...], total: [{ count: 348 }] }

Comprobación rápida

Compruebe su comprensión de $sort, $limit y $skip en el flujo de agregaciones.

Resumen de la lección

En esta lección ha aprendido que $sort ordena los documentos y se beneficia del uso de índices cuando se coloca al principio; que $limit toma los primeros N documentos y se combina con $sort en una ordenación eficiente de los N primeros elementos; y que $skip implementa la paginación por desplazamiento, pero su rendimiento empeora con desplazamientos profundos. A continuación, exploraremos etapas avanzadas de agregación, incluida $lookup para combinar colecciones.

Gratis para empezar

Aprende JavaScript con un tutor de IA — gratis

Escribe y ejecuta código real en tu navegador, obtén ayuda instantánea de un tutor de IA disponible 24/7 y continúa donde lo dejaste en la web o en la aplicación.

Cursos
30
Lecciones
120

Preguntas frecuentes

¿La lección «$sort, $limit y $skip en el pipeline» es gratis?

Sí — el texto completo de «$sort, $limit y $skip en el pipeline» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de MongoDB Academy, actualiza a CoddyKit PRO. El curso de MongoDB Academy incluye 4 lecciones en total.

¿Qué aprenderé en «$sort, $limit y $skip en el pipeline»?

Ordenará y paginará los resultados de agregación, y comprenderá las reglas del optimizador para adelantar $match y $sort antes de $group. Practicas MongoDB Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar MongoDB Academy?

No se requiere experiencia previa. MongoDB Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «$sort, $limit y $skip en el pipeline»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de MongoDB Academy?

Sí. Cada lección de MongoDB Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Conceptos de pipelines: etapas, operadores y expresiones
  2. $match y $project: filtrar y remodelar
  3. $group: agregación y cálculo de totales
  4. $sort, $limit y $skip en el pipeline
← Volver a MongoDB Academy