11. Práctica final
Ahora vas a poner en práctica todo lo que has aprendido con un proyecto realista: Sistema de Gestión de Biblioteca Digital.
Contexto del proyecto
Vas a crear un sistema para gestionar una biblioteca que presta libros. Necesitas registrar libros, usuarios, préstamos y generar estadísticas útiles para la gestión.
Estructura de datos
Vas a trabajar con tres colecciones principales:
Colección books (libros disponibles):
{
isbn: "978-0-123456-78-9",
titulo: "Cien años de soledad",
autor: "Gabriel García Márquez",
genero: "Ficción",
editorial: "Editorial Sudamericana",
año_publicacion: 1967,
copias_totales: 5,
copias_disponibles: 3,
prestamos_historicos: 127,
valoraciones: [4.5, 5, 4, 5, 4.5],
etiquetas: ["realismo mágico", "clásico", "literatura latinoamericana"]
}
Colección users (usuarios registrados):
{
email: "maria@example.com",
nombre: "María González",
fecha_registro: ISODate("2024-01-15"),
tipo: "premium", // o "standard"
libros_prestados_actuales: [
{ isbn: "978-0-123456-78-9", fecha_prestamo: ISODate("2024-03-01") }
],
historial_prestamos: 23,
multas_pendientes: 0
}
Colección loans (registro de préstamos):
{
usuario_email: "maria@example.com",
isbn_libro: "978-0-123456-78-9",
fecha_prestamo: ISODate("2024-03-01"),
fecha_devolucion_esperada: ISODate("2024-03-15"),
fecha_devolucion_real: null,
renovaciones: 0,
estado: "activo" // "activo", "devuelto", "vencido"
}
Fase 1: Preparación y CRUD básico
Primero, crea la base de datos y las colecciones con datos iniciales:
// Usar la base de datos
use biblioteca
// Insertar libros
db.books.insertMany([
{
isbn: "978-0-123456-78-9",
titulo: "Cien años de soledad",
autor: "Gabriel García Márquez",
genero: "Ficción",
editorial: "Editorial Sudamericana",
año_publicacion: 1967,
copias_totales: 5,
copias_disponibles: 3,
prestamos_historicos: 127,
valoraciones: [4.5, 5, 4, 5, 4.5],
etiquetas: ["realismo mágico", "clásico", "literatura latinoamericana"]
},
{
isbn: "978-1-234567-89-0",
titulo: "El Quijote",
autor: "Miguel de Cervantes",
genero: "Ficción",
editorial: "Francisco de Robles",
año_publicacion: 1605,
copias_totales: 3,
copias_disponibles: 3,
prestamos_historicos: 89,
valoraciones: [5, 4.5, 5, 5],
etiquetas: ["clásico", "aventura"]
},
{
isbn: "978-2-345678-90-1",
titulo: "Introducción a la Programación",
autor: "Ana Martínez",
genero: "Tecnología",
editorial: "TechBooks",
año_publicacion: 2020,
copias_totales: 7,
copias_disponibles: 1,
prestamos_historicos: 234,
valoraciones: [4, 4.5, 4, 5, 4.5, 4],
etiquetas: ["programación", "educativo", "principiantes"]
}
// Añade al menos 10 libros más variando géneros y disponibilidad
])
// Insertar usuarios
db.users.insertMany([
{
email: "maria@example.com",
nombre: "María González",
fecha_registro: ISODate("2024-01-15"),
tipo: "premium",
libros_prestados_actuales: [],
historial_prestamos: 23,
multas_pendientes: 0
},
{
email: "juan@example.com",
nombre: "Juan Pérez",
fecha_registro: ISODate("2023-06-20"),
tipo: "standard",
libros_prestados_actuales: [],
historial_prestamos: 8,
multas_pendientes: 15
}
// Añade al menos 5 usuarios más
])
Fase 2: Consultas y filtros
Practica las consultas que necesitarías en un sistema real:
// Buscar libros de ficción disponibles
db.books.find({
genero: "Ficción",
copias_disponibles: { $gt: 0 }
})
// Usuarios con multas pendientes
db.users.find({ multas_pendientes: { $gt: 0 } })
// Libros publicados después del 2000
db.books.find({ año_publicacion: { $gt: 2000 } })
// Usuarios premium con préstamos activos
db.users.find({
tipo: "premium",
libros_prestados_actuales: { $ne: [] }
})
// Libros más solicitados (más de 100 préstamos históricos)
db.books.find({ prestamos_historicos: { $gte: 100 } })
// Buscar libros por autor (búsqueda parcial con regex)
db.books.find({ autor: /García/ })
// Libros con valoración promedio alta (todos >= 4)
db.books.find({ valoraciones: { $not: { $elemMatch: { $lt: 4 } } } })
Fase 3: Operación de préstamo
Implementa la operación completa de prestar un libro. Debes actualizar múltiples colecciones:
// Paso 1: Verificar disponibilidad
var libro = db.books.findOne({
isbn: "978-0-123456-78-9",
copias_disponibles: { $gt: 0 }
})
if (libro) {
var fecha_prestamo = new Date()
var fecha_devolucion = new Date()
fecha_devolucion.setDate(fecha_devolucion.getDate() + 14) // 14 días
// Paso 2: Actualizar libro (decrementar copias, incrementar histórico)
db.books.updateOne(
{ isbn: "978-0-123456-78-9" },
{
$inc: {
copias_disponibles: -1,
prestamos_historicos: 1
}
}
)
// Paso 3: Actualizar usuario (añadir a libros prestados, incrementar historial)
db.users.updateOne(
{ email: "maria@example.com" },
{
$push: {
libros_prestados_actuales: {
isbn: "978-0-123456-78-9",
fecha_prestamo: fecha_prestamo
}
},
$inc: { historial_prestamos: 1 }
}
)
// Paso 4: Crear registro de préstamo
db.loans.insertOne({
usuario_email: "maria@example.com",
isbn_libro: "978-0-123456-78-9",
fecha_prestamo: fecha_prestamo,
fecha_devolucion_esperada: fecha_devolucion,
fecha_devolucion_real: null,
renovaciones: 0,
estado: "activo"
})
print("Préstamo realizado correctamente")
} else {
print("Libro no disponible")
}
Fase 4: Operación de devolución
Implementa la devolución de un libro:
var fecha_devolucion = new Date()
// Paso 1: Actualizar préstamo
db.loans.updateOne(
{
usuario_email: "maria@example.com",
isbn_libro: "978-0-123456-78-9",
estado: "activo"
},
{
$set: {
fecha_devolucion_real: fecha_devolucion,
estado: "devuelto"
}
}
)
// Paso 2: Incrementar copias disponibles
db.books.updateOne(
{ isbn: "978-0-123456-78-9" },
{ $inc: { copias_disponibles: 1 } }
)
// Paso 3: Quitar de libros prestados del usuario
db.users.updateOne(
{ email: "maria@example.com" },
{
$pull: {
libros_prestados_actuales: { isbn: "978-0-123456-78-9" }
}
}
)
Fase 5: Agregaciones y estadísticas
Genera reportes útiles para la biblioteca:
// Top 5 libros más prestados
db.books.aggregate([
{ $sort: { prestamos_historicos: -1 } },
{ $limit: 5 },
{ $project: { titulo: 1, autor: 1, prestamos_historicos: 1 } }
])
// Promedio de valoraciones por género
db.books.aggregate([
{ $unwind: "$valoraciones" },
{ $group: {
_id: "$genero",
promedio: { $avg: "$valoraciones" },
total_libros: { $sum: 1 }
}},
{ $sort: { promedio: -1 } }
])
// Usuarios más activos (más préstamos históricos)
db.users.aggregate([
{ $sort: { historial_prestamos: -1 } },
{ $limit: 10 },
{ $project: { nombre: 1, email: 1, historial_prestamos: 1, tipo: 1 } }
])
// Préstamos por mes
db.loans.aggregate([
{ $group: {
_id: {
año: { $year: "$fecha_prestamo" },
mes: { $month: "$fecha_prestamo" }
},
total_prestamos: { $sum: 1 }
}},
{ $sort: { "_id.año": -1, "_id.mes": -1 } }
])
// Géneros más populares (por préstamos históricos)
db.books.aggregate([
{ $group: {
_id: "$genero",
total_prestamos: { $sum: "$prestamos_historicos" },
libros_en_genero: { $sum: 1 }
}},
{ $sort: { total_prestamos: -1 } }
])
// Libros con mejor valoración promedio (mínimo 3 valoraciones)
db.books.aggregate([
{ $match: { valoraciones: { $exists: true, $ne: [] } } },
{ $addFields: {
num_valoraciones: { $size: "$valoraciones" },
promedio_valoracion: { $avg: "$valoraciones" }
}},
{ $match: { num_valoraciones: { $gte: 3 } } },
{ $sort: { promedio_valoracion: -1 } },
{ $limit: 10 },
{ $project: {
titulo: 1,
autor: 1,
promedio_valoracion: 1,
num_valoraciones: 1
}}
])
Fase 6: Índices para optimización
Crea índices para las consultas más frecuentes:
// Búsqueda rápida por título
db.books.createIndex({ titulo: 1 })
// Búsqueda por ISBN (único)
db.books.createIndex({ isbn: 1 }, { unique: true })
// Búsqueda por género y disponibilidad
db.books.createIndex({ genero: 1, copias_disponibles: 1 })
// Email único para usuarios
db.users.createIndex({ email: 1 }, { unique: true })
// Préstamos activos por usuario
db.loans.createIndex({ usuario_email: 1, estado: 1 })
// Verificar que se usan los índices
db.books.find({ genero: "Ficción" }).explain("executionStats")
Fase 7: Operaciones adicionales
Implementa estas funcionalidades extras:
// Renovar un préstamo (extender 7 días más)
db.loans.updateOne(
{
usuario_email: "maria@example.com",
isbn_libro: "978-0-123456-78-9",
estado: "activo"
},
{
$inc: { renovaciones: 1 },
$set: {
fecha_devolucion_esperada: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
}
}
)
// Añadir valoración a un libro
db.books.updateOne(
{ isbn: "978-0-123456-78-9" },
{ $push: { valoraciones: 4.5 } }
)
// Añadir etiqueta a un libro
db.books.updateOne(
{ isbn: "978-0-123456-78-9" },
{ $addToSet: { etiquetas: "bestseller" } }
)
// Marcar préstamos vencidos (más de 14 días sin devolver)
var hace_14_dias = new Date()
hace_14_dias.setDate(hace_14_dias.getDate() - 14)
db.loans.updateMany(
{
estado: "activo",
fecha_devolucion_esperada: { $lt: new Date() }
},
{ $set: { estado: "vencido" } }
)
// Calcular multas pendientes (ejemplo: $2 por día de retraso)
db.loans.aggregate([
{ $match: { estado: "vencido" } },
{ $addFields: {
dias_retraso: {
$divide: [
{ $subtract: [new Date(), "$fecha_devolucion_esperada"] },
1000 * 60 * 60 * 24
]
}
}},
{ $addFields: {
multa: { $multiply: [{ $floor: "$dias_retraso" }, 2] }
}},
{ $group: {
_id: "$usuario_email",
multa_total: { $sum: "$multa" }
}}
])
Actividad 1
Implementa el sistema completo de biblioteca siguiendo todas las fases:
- Crea la base de datos con al menos 15 libros, 7 usuarios y 10 préstamos (algunos activos, otros completados)
- Realiza 3 préstamos completos usando el código de la Fase 3
- Realiza 2 devoluciones usando el código de la Fase 4
- Genera todos los reportes de la Fase 5
- Crea todos los índices de la Fase 6 y verifica con
explain()que se usan - Implementa al menos 2 operaciones adicionales de la Fase 7
Entregable: captura de pantalla de MongoDB Compass mostrando tus 3 colecciones con datos, y capturas de al menos 3 agregaciones diferentes ejecutadas.
Este trabajo está bajo una licencia Attribution-NonCommercial-NoDerivatives 4.0 International.
Desafíos de programación atemporales y multiparadigmáticos
Te encuentras ante un librillo de actividades, divididas en 2 niveles de dificultad. Te enfrentarás a los casos más comunes que te puedes encontrar en pruebas técnicas o aprender conceptos elementales de programación.
Comprar el libro
Comentarios
Todavía no hay ningún comentario.