Clase 25 - NoSQL - MongoDB
🧠 ¿Qué es MongoDB?
MongoDB es una base de datos NoSQL orientada a documentos que revoluciona la forma en que almacenamos y gestionamos datos. A diferencia de las bases de datos relacionales tradicionales (como MySQL o PostgreSQL), MongoDB almacena datos en formato JSON (específicamente BSON - Binary JSON).
✅ Características Clave
- NoSQL: No usa tablas ni relaciones estrictas como las bases de datos relacionales.
- Flexible: Permite esquemas dinámicos - puedes agregar campos sin modificar toda la estructura.
- Escalable: Ideal para grandes volúmenes de datos con capacidades de sharding horizontal.
- Orientado a documentos: Guarda la información como documentos JSON anidados.
- Alto rendimiento: Optimizado para lecturas y escrituras rápidas (⚠️).
- Replicación: Soporte nativo para alta disponibilidad.
- Indexación: Sistema de índices flexible y potente.
🔄 MongoDB vs Bases de Datos Relacionales
| Concepto | MongoDB | SQL |
|---|---|---|
| Base de datos | Database | Database |
| Tabla | Collection | Table |
| Fila | Document | Register / Row |
| Columna | Field | Column |
| Clave primaria | _id | Primary Key |
⚙️ Levantando MongoDB con Docker
Vamos a levantar un contenedor con MongoDB para trabajar localmente de forma profesional.
🧪 Paso 1: Crear un docker-compose.yml
services: mongodb: image: mongo:7.0 container_name: mongodb_dev ports: - 27017:27017 environment: MONGO_INITDB_ROOT_USERNAME: admin MONGO_INITDB_ROOT_PASSWORD: password123 MONGO_INITDB_DATABASE: escuela volumes: - mongo_data:/data/db restart: always
volumes: mongo_data:▶️ Paso 2: Ejecutar
# Levantar los serviciosdocker compose up -d
# Verificar que están corriendodocker compose ps
# Ver logs si hay problemasdocker compose logs mongodbMongoDB estará disponible en: mongodb://admin:password123@localhost:27017
🔌 Conectarse a MongoDB
Opciones de Conexión:
- MongoDB Compass (interfaz gráfica oficial)
- Mongo Shell (terminal)
- Mongo Express (interfaz web)
Usar Mongo Shell:
# Conectar al contenedordocker exec -it mongodb_dev mongoshComandos de Conexión y Navegación:
// Mostrar bases de datosshow dbs
// Mostrar base de datos actualdb
// Cambiar a una base de datosuse escuela
// Mostrar colecciones de la BD actualshow collections
// Obtener ayudahelp
// Ayuda específica para coleccionesdb.help()
// Estadísticas de la base de datosdb.stats()
// Información del servidordb.serverStatus()📂 Crear y Gestionar Bases de Datos y Colecciones
Creación y Gestión de Bases de Datos:
// Crear o cambiar a una base de datosuse escuela
// La base de datos se crea cuando insertas el primer documento// Eliminar base de datos actualdb.dropDatabase()
// Ver el tamaño de la base de datosdb.stats()Creación y Gestión de Colecciones:
// Crear una colección explícitamentedb.createCollection("alumnos")
// Crear colección con opciones - "nuevo"db.createCollection("profesores", { validator: { $jsonSchema: { bsonType: "object", required: ["nombre", "edad", "especialidad"], properties: { nombre: { bsonType: "string" }, edad: { bsonType: "int", minimum: 25, maximum: 70 }, especialidad: { bsonType: "string" } } } }})
// Listar coleccionesshow collections
// Obtener información de una coleccióndb.alumnos.stats()
// Renombrar coleccióndb.alumnos.renameCollection("estudiantes")
// Eliminar coleccióndb.alumnos.drop()📝 Insertar Documentos - Operaciones de Escritura
Insertar un solo documento:
// Inserción básicadb.alumnos.insertOne({ nombre: "Luis", edad: 22, email: "luis@gmail.com", materias: ["Matemáticas", "Física", "Química"], promedio: 8.5, activo: true, fechaIngreso: new Date(), direccion: { calle: "Av. Independencia 123", ciudad: "Chubut", codigoPostal: "4564" }})
// Con _id personalizadodb.alumnos.insertOne({ _id: "ALU001", nombre: "Carlos", edad: 24, materias: ["Química", "Biología"], promedio: 7.8})Insertar múltiples documentos:
db.alumnos.insertMany([ { nombre: "Ana", edad: 21, email: "ana@gmail.com", materias: ["Historia", "Literatura", "Filosofía"], promedio: 9.2, becario: true, direccion: { calle: "San Martín 456", ciudad: "Córdoba", codigoPostal: "5000" } }, { nombre: "Diego", edad: 23, email: "diego@gmail.com", materias: ["Programación", "Base de Datos", "Algoritmos"], promedio: 8.7, becario: false, direccion: { calle: "Belgrano 789", ciudad: "Rosario", codigoPostal: "2000" } }, { nombre: "María", edad: 20, email: "maria@gmail.com", materias: ["Arte", "Diseño", "Historia del Arte"], promedio: 9.5, becario: true }])
// Inserción con opcionesdb.alumnos.insertMany([ { nombre: "Pedro", edad: 25 }, { nombre: "Facundo", edad: 26 }], { ordered: false }) // Continúa insertando aunque falle uno🔍 Consultas y Búsquedas - Operaciones de Lectura
Consultas Básicas:
// Mostrar todos los documentosdb.alumnos.find()
// Contar documentosdb.alumnos.countDocuments()
// Encontrar uno solodb.alumnos.findOne()
// Buscar por campo específicodb.alumnos.find({ nombre: "Lucía" })
// Buscar con múltiples condiciones (AND implícito)db.alumnos.find({ edad: 22, activo: true})Operadores de Comparación:
// Mayor que ($gt)db.alumnos.find({ edad: { $gt: 22 } })
// Mayor o igual que ($gte)db.alumnos.find({ promedio: { $gte: 9.0 } })
// Menor que ($lt)db.alumnos.find({ edad: { $lt: 23 } })
// Menor o igual que ($lte)db.alumnos.find({ promedio: { $lte: 8.0 } })
// Diferente de ($ne)db.alumnos.find({ activo: { $ne: false } })
// En un array de valores ($in)db.alumnos.find({ edad: { $in: [20, 21, 22] } })
// No en un array de valores ($nin)db.alumnos.find({ edad: { $nin: [25, 26, 27] } })
// Existe el campo ($exists)db.alumnos.find({ email: { $exists: true } })Operadores Lógicos:
// ORdb.alumnos.find({ $or: [ { promedio: { $gt: 9 } }, { becario: true } ]})
// AND explícitodb.alumnos.find({ $and: [ { edad: { $gte: 20 } }, { edad: { $lte: 25 } } ]})
// NOTdb.alumnos.find({ promedio: { $not: { $lt: 8.0 } }})
// NOR (ni uno ni otro)db.alumnos.find({ $nor: [ { edad: { $lt: 20 } }, { promedio: { $lt: 7.0 } } ]})Consultas en Arrays:
// Buscar documentos que tengan un elemento específico en un arraydb.alumnos.find({ materias: "Matemáticas" })
// Buscar con múltiples elementos (todos deben estar presentes)db.alumnos.find({ materias: { $all: ["Matemáticas", "Física"] } })
// Buscar por tamaño de arraydb.alumnos.find({ materias: { $size: 3 } })
// Usar $elemMatch para condiciones complejas en arraysdb.alumnos.find({ "notas": { $elemMatch: { materia: "Matemáticas", calificacion: { $gte: 8 } } }})Consultas en Documentos Anidados:
// Buscar en documento anidado usando notación de puntodb.alumnos.find({ "direccion.ciudad": "Chubut" })
// Múltiples campos en documento anidadodb.alumnos.find({ "direccion.ciudad": "Córdoba", "direccion.codigoPostal": "5000"})Expresiones Regulares:
// Buscar nombres que contengan "Ana"db.alumnos.find({ nombre: /Ana/ })
// Insensible a mayúsculas/minúsculasdb.alumnos.find({ nombre: /ana/i })
// Que empiece con "M"db.alumnos.find({ nombre: /^M/ })
// Que termine con "ez"db.alumnos.find({ nombre: /ez$/ })Proyección - Seleccionar Campos:
// Mostrar solo nombre y edad (incluye _id por defecto)db.alumnos.find({}, { nombre: 1, edad: 1 })
// Excluir _iddb.alumnos.find({}, { nombre: 1, edad: 1, _id: 0 })
// Excluir campos específicosdb.alumnos.find({}, { direccion: 0, fechaIngreso: 0 })
// Proyección en arrays (mostrar solo primer elemento)db.alumnos.find({}, { "materias.$": 1 })Ordenamiento y Limitación:
// Ordenar por edad ascendentedb.alumnos.find().sort({ edad: 1 })
// Ordenar por promedio descendentedb.alumnos.find().sort({ promedio: -1 })
// Ordenamiento múltipledb.alumnos.find().sort({ promedio: -1, edad: 1 })
// Limitar resultadosdb.alumnos.find().limit(5)
// Saltar documentos (paginación)db.alumnos.find().skip(10).limit(5)
// Combinando tododb.alumnos.find({ activo: true }) .sort({ promedio: -1 }) .limit(3)🛠️ Actualizar Documentos - Operaciones de Modificación
Actualización de Un Documento:
// Actualizar un campodb.alumnos.updateOne( { nombre: "Carlos" }, { $set: { promedio: 8.3, activo: true } })
// Incrementar un valor numéricodb.alumnos.updateOne( { nombre: "Ana" }, { $inc: { edad: 1 } })
// Agregar elemento a un arraydb.alumnos.updateOne( { nombre: "Diego" }, { $push: { materias: "Matemáticas Avanzadas" } })
// Agregar múltiples elementos a un arraydb.alumnos.updateOne( { nombre: "María" }, { $push: { materias: { $each: ["Escultura", "Pintura"] } } })
// Remover elemento de un arraydb.alumnos.updateOne( { nombre: "Diego" }, { $pull: { materias: "Algoritmos" } })
// Remover el último elemento de un arraydb.alumnos.updateOne( { nombre: "Lucía" }, { $pop: { materias: 1 } } // 1 para último, -1 para primero)
// Eliminar un campodb.alumnos.updateOne( { nombre: "Carlos" }, { $unset: { fechaIngreso: "" } })
// Renombrar un campodb.alumnos.updateOne( { nombre: "Ana" }, { $rename: { "becario": "esBecario" } })Actualización de Múltiples Documentos:
// Actualizar todos los documentos que coincidandb.alumnos.updateMany( { edad: { $lt: 22 } }, { $set: { categoria: "joven" } })
// Incrementar promedio para todos los becariosdb.alumnos.updateMany( { becario: true }, { $inc: { promedio: 0.5 } })Upsert (Actualizar o Insertar):
// Si no existe, lo creadb.alumnos.updateOne( { nombre: "Roberto" }, { $set: { edad: 22, promedio: 8.0, materias: ["Introducción a la Programación"] } }, { upsert: true })Replace (Reemplazar Documento Completo):
db.alumnos.replaceOne( { nombre: "Carlos" }, { nombre: "Carlos", edad: 25, email: "carlos.mendez.nuevo@email.com", materias: ["Química Avanzada", "Biología Molecular"], promedio: 8.8, graduado: false })🗑️ Eliminar Documentos - Operaciones de Borrado
Eliminar Documentos:
// Eliminar un documentodb.alumnos.deleteOne({ nombre: "Ana" })
// Eliminar múltiples documentosdb.alumnos.deleteMany({ activo: false })
// Eliminar todos los documentos de la coleccióndb.alumnos.deleteMany({})
// Encontrar y eliminar (devuelve el documento eliminado)db.alumnos.findOneAndDelete({ promedio: { $lt: 6.0 } })📊 Agregaciones - Pipeline de Procesamiento
Las agregaciones son una de las características más poderosas de MongoDB para procesar y transformar datos.
Agregaciones Básicas:
// Promedio general de todos los alumnosdb.alumnos.aggregate([ { $group: { _id: null, promedioGeneral: { $avg: "$promedio" }, totalAlumnos: { $sum: 1 }, promedioMaximo: { $max: "$promedio" }, promedioMinimo: { $min: "$promedio" } } }])
// Agrupar por ciudaddb.alumnos.aggregate([ { $group: { _id: "$direccion.ciudad", cantidadAlumnos: { $sum: 1 }, promedioEdad: { $avg: "$edad" } } }])
// Estadísticas por rango de edaddb.alumnos.aggregate([ { $group: { _id: { $switch: { branches: [ { case: { $lt: ["$edad", 21] }, then: "Menores de 21" }, { case: { $lt: ["$edad", 25] }, then: "21-24 años" } ], default: "25 años o más" } }, cantidad: { $sum: 1 }, promedioAcademico: { $avg: "$promedio" } } }])🔍 Índices - Optimización de Consultas
Los índices son cruciales para el rendimiento en colecciones grandes.
Crear y Gestionar Índices:
// Crear índice simpledb.alumnos.createIndex({ nombre: 1 }) // 1 = ascendente, -1 = descendente
// Crear índice compuestodb.alumnos.createIndex({ edad: 1, promedio: -1 })
// Índice de texto para búsquedasdb.alumnos.createIndex({ nombre: "text", "direccion.ciudad": "text"})
// Índice en documento anidadodb.alumnos.createIndex({ "direccion.ciudad": 1 })
// Índice en arraydb.alumnos.createIndex({ materias: 1 })
// Índice únicodb.alumnos.createIndex({ email: 1 }, { unique: true })Información de Índices:
// Listar todos los índicesdb.alumnos.getIndexes()
// Eliminar índicedb.alumnos.dropIndex({ nombre: 1 })
// Eliminar todos los índices (excepto _id)db.alumnos.dropIndexes()🔄 Operaciones Bulk (Por Lotes)
Para operaciones eficientes con múltiples documentos:
// Bulk write ordenadodb.alumnos.bulkWrite([ { insertOne: { document: { nombre: "Pedro Bulk", edad: 25, promedio: 8.0 } } }, { updateOne: { filter: { nombre: "Lucía García" }, update: { $set: { promedio: 9.0 } } } }, { deleteOne: { filter: { promedio: { $lt: 6.0 } } } }], { ordered: true })
// Bulk write no ordenado (más eficiente)db.alumnos.bulkWrite([ { updateMany: { filter: { edad: { $lt: 22 } }, update: { $set: { categoria: "junior" } } } }, { updateMany: { filter: { edad: { $gte: 22 } }, update: { $set: { categoria: "senior" } } } }], { ordered: false })🎯 Ejercicios Prácticos
📝 Ejercicio 1: Crear un Sistema de Gestión Académica
Crea una base de datos académica para gestionar la información de profesores.
Requisitos:
-
Crear una colección llamada
profesorescon validación de esquema que incluya los siguientes campos requeridos:-
nombre(string, mínimo 2 caracteres) -
edad(entero, entre 25 y 70) -
especialidad(string) -
añosExperiencia(entero, mínimo 0)
-
-
Insertar al menos tres documentos de ejemplo que contengan también los siguientes campos adicionales:
-
email -
departamento -
salario -
activo(booleano)
-
-
Realizar las siguientes consultas:
-
Buscar todos los profesores con más de 15 años de experiencia.
-
Calcular el promedio de edad, experiencia y salario de los profesores activos.
-
Generar estadísticas por departamento: número de profesores, experiencia total, salario promedio y listado de nombres.
-
📚 Ejercicio 2: Sistema de Cursos y Matriculaciones
Diseña una colección para administrar los cursos y su proceso de inscripción.
Requisitos:
-
Crear una colección llamada
cursose insertar al menos dos documentos con los siguientes campos:-
codigo(ej."MAT101") -
nombre -
creditos -
profesorId(relación con la colecciónprofesores) -
horario: un objeto con los días y el horario del curso. -
cupoMaximo -
inscritos: un arreglo inicialmente vacío.
-
-
Matricular un alumno en un curso agregando al arreglo
inscritosun objeto con:-
alumnoId -
fechaInscripcion(fecha actual) -
estado(ej."activo")
-
-
Consultar los cursos con cupos disponibles, calculando el número de cupos restantes y mostrando solo aquellos con disponibilidad.