Los talleres 4 y 5 del curso de Bases de Datos No Relacionales introducen el framework de agregaciones de MongoDB, la herramienta más poderosa del motor para transformar, agrupar y analizar datos a escala. Trabajamos sobre la colecciónDocumentation 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.
personas, que contiene más de un millón de documentos generados con datos sintéticos en formato RandomUser.me, y construimos pipelines progresivamente más complejos: desde simples agrupaciones hasta combinaciones de $match, $group, $sort, $project y $limit en una sola consulta.
La Colección personas
Todos los ejercicios de los talleres 4 y 5 operan sobre la colección personas en la base de datos dbpersonas. Esta colección fue generada con el script Python del repositorio (ver Script Usuarios) y contiene documentos con la siguiente estructura, inspirada en la API RandomUser.me:
| Campo | Descripción |
|---|---|
gender | Género: "male" o "female" |
nat | Código de nacionalidad: US, ES, FR, BR, CA, DE, DK |
location.city | Ciudad de residencia (en minúsculas) |
location.state | Estado o provincia (en minúsculas) |
dob.age | Edad de la persona en años |
registered.age | Antigüedad del registro en años |
Ejercicios de Agrupación
Regla de oro: Cuando el enunciado dice “por cada” o “para cada” algo → usa
$group. El campo que viene después de “por cada” es el _id del grupo.Ejercicio 1 — Top 50 nacionalidades con más personas
Enunciado: Listar por nacionalidad de origen, el top de las 50 con más personas.| Etapa | Qué hace |
|---|---|
$group | Agrupa todos los documentos por el campo nat y cuenta cuántos hay por cada nacionalidad con $sum: 1 |
$sort | Ordena los grupos de mayor a menor según el conteo (total: -1) |
$limit | Devuelve solo los primeros 50 resultados |
Ejercicio 2 — Top 50 con proyección de campos
Enunciado: Mismo resultado que el ejercicio 1, pero renombrando los campos de salida: mostrarnacionalidad y conteo en lugar de _id y total.
| Etapa | Qué hace |
|---|---|
$group | Igual que el ejercicio 1: agrupa por nat y cuenta |
$sort | Ordena de mayor a menor por total |
$project | Suprime _id (_id: 0), renombra _id del grupo a nacionalidad y total a conteo |
$limit | Limita a 50 documentos |
Ejercicio 3 — Top 5 ciudades con menos personas
Enunciado: Top 5 de las ciudades donde viven las personas con menor cantidad de habitantes. Se presentan dos variantes: la básica (solo por total) y la completa (con ordenación doble por ciudad y total). Variante básica — solo ordena por total de menor a mayor:_id: 1) y luego por total de menor a mayor (total: 1):
| Etapa | Qué hace |
|---|---|
$group | Agrupa por la ciudad (location.city) y cuenta personas por ciudad |
$sort | Variante básica: ordena solo por total de menor a mayor (total: 1). Variante doble: ordena por ciudad A→Z (_id: 1) y luego por total de menor a mayor (total: 1) |
$limit | Retorna solo los 5 primeros resultados |
Ejercicio 4 — Conteo por género y nacionalidad
Enunciado: Número de personas ordenado por género y nacionalidad, con nacionalidad de menor a mayor.| Etapa | Qué hace |
|---|---|
$group | Agrupa usando un _id compuesto con dos campos: nat (como nacionalidad) y gender (como genero). Cuenta el total de personas por cada combinación |
$sort | Ordena por _id.nacionalidad de A→Z y luego por genero de A→Z |
Cuando el
_id del $group es un objeto compuesto, para referenciar sus campos en etapas siguientes usa la notación de punto: "_id.nacionalidad", "_id.genero".Ejercicio 5 — Promedio de edad por género y nacionalidad (Top 5)
Enunciado: Promedio de las edades por género y nacionalidad → Top 5 de mayor a menor promedio.| Etapa | Qué hace |
|---|---|
$group | Agrupa por gender y nat, calcula el promedio de dob.age con $avg |
$sort | Ordena de mayor a menor por el campo promedio |
$project | Reestructura la salida: suprime _id, extrae genero y nacionalidad del objeto _id del grupo |
$limit | Devuelve solo los 5 primeros |
Ejercicio 6 — Estadísticas de edad por género y ciudad
Enunciado: Filtrar personas con edad mayor a 20 años, agrupar por género y ciudad, y calcular promedio de edad, promedio de antigüedad de registro, edad máxima, edad mínima y total. Ordenar por promedio de antigüedad de registro de mayor a menor, limitar a 30.| Etapa | Qué hace |
|---|---|
$match | Filtra primero los documentos donde dob.age > 20, antes de agrupar. Esto reduce la cantidad de documentos que procesan las etapas siguientes |
$group | Agrupa por gender + location.city. Calcula: promedio de edad ($avg), promedio de edad de registro ($avg), edad máxima ($max), edad mínima ($min), y conteo total ($sum: 1) |
$sort | Ordena por promedioEdadRegistro de mayor a menor |
$limit | Limita la salida a 30 grupos |
Ejercicio 7 — Total de personas por nacionalidad y estado (con filtro post-group)
Enunciado: Para cada nacionalidad y para cada estado, calcular el total de personas, pero mostrar solo los grupos con total mayor o igual a 10. Sin ordenación. Proyectar sin_id.
| Etapa | Qué hace |
|---|---|
$group | Agrupa por nat + location.state y cuenta el total de personas por cada combinación |
$match | Aplica un filtro sobre los grupos ya calculados: solo pasan los grupos donde total >= 10. Este $match viene después del $group, por eso filtra sobre campos calculados (como total) |
$project | Suprime _id, expone nacionalidad, estado y total como campos de primer nivel |
Contar por Género — 3 Formas
El ejercicio introductorio del taller propone contar personas por género de tres maneras distintas. Cada forma tiene su propio nivel de detalle y utilidad:Forma 1 — Agrupación directa (básica)
El_id del grupo es directamente el valor del campo gender:
_id: "male" y _id: "female".Úsala cuando: Solo necesitas el conteo y no te importa el nombre del campo de salida.
Forma 2 — _id como objeto (explícita)
El _id del grupo es un objeto con un campo nombrado genero:
_id: { genero: "male" } — el valor queda dentro de un objeto.Úsala cuando: Vas a hacer una agrupación compuesta después (agregar más campos al
_id) o quieres mantener la consistencia con otros pipelines.
Forma 3 — Con ordenación (básica + salida limpia)
Combina la agrupación con$sort para ordenar los resultados por conteo:
_id: "female" y _id: "male" ordenados por total ascendente.Úsala cuando: Necesitas el conteo ordenado sin preocuparte por el nombre del campo de salida. También puedes combinarla con
$project para obtener una salida sin _id y con nombres de campo personalizados:
_id.
Tips para Agregaciones
¿Cuándo uso $match antes vs después de $group?
¿Cuándo uso $match antes vs después de $group?
-
$matchANTES de$group: Filtra documentos crudos de la colección antes de agrupar. Siempre prefiere esta posición cuando puedas, porque reduce la cantidad de documentos que procesan las etapas posteriores y puede aprovechar índices de la colección. -
$matchDESPUÉS de$group: Filtra sobre los resultados calculados del grupo (campos comototal,promedio, etc.), que no existen en los documentos originales. Úsalo cuando el filtro depende del resultado del$group.
¿Por qué usar $project después de $group?
¿Por qué usar $project después de $group?
Después de un El
$group, el resultado tiene la forma { _id: ..., campo1: ..., campo2: ... }. El $project sirve para:- Eliminar
_iddel resultado (_id: 0) cuando no lo necesitas. - Renombrar campos calculados a nombres más descriptivos (por ejemplo:
total→conteo,_id→nacionalidad). - Seleccionar qué campos mostrar y cuáles omitir.
$project no cambia los datos — solo transforma la presentación del documento de salida.¿Cómo afecta el orden de las etapas al rendimiento?
¿Cómo afecta el orden de las etapas al rendimiento?
El orden de las etapas en un pipeline de agregación tiene impacto directo en el rendimiento:
MongoDB tiene un optimizador de pipelines que en algunos casos reordena etapas automáticamente (por ejemplo, mueve
| Principio | Recomendación |
|---|---|
| Filtrar temprano | Coloca $match lo más al principio posible para reducir documentos |
| Aprovechar índices | Un $match al inicio puede usar índices de la colección; después de $group, no |
| Limitar antes de procesar | Si sabes cuántos resultados necesitas, usa $limit lo antes posible |
| Proyectar campos innecesarios | Usa $project para eliminar campos grandes que no necesitarás en etapas posteriores |
$match antes de $sort), pero es mejor no depender de esto y escribir pipelines ya optimizados.¿Qué acumuladores puedo usar en $group?
¿Qué acumuladores puedo usar en $group?
Los acumuladores más comunes del operador
$group:| Acumulador | Descripción | Ejemplo |
|---|---|---|
$sum | Suma valores (con 1 cuenta documentos) | { $sum: "$precio" } o { $sum: 1 } |
$avg | Promedio aritmético | { $avg: "$dob.age" } |
$max | Valor máximo del grupo | { $max: "$dob.age" } |
$min | Valor mínimo del grupo | { $min: "$dob.age" } |
$count | Cuenta documentos (MongoDB 5.0+) | { $count: {} } |
$push | Construye un array con todos los valores | { $push: "$nombre" } |
$addToSet | Array de valores únicos (sin duplicados) | { $addToSet: "$ciudad" } |
$first | Primer valor del grupo | { $first: "$nombre" } |
$last | Último valor del grupo | { $last: "$nombre" } |