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:

  1. Crea la base de datos con al menos 15 libros, 7 usuarios y 10 préstamos (algunos activos, otros completados)
  2. Realiza 3 préstamos completos usando el código de la Fase 3
  3. Realiza 2 devoluciones usando el código de la Fase 4
  4. Genera todos los reportes de la Fase 5
  5. Crea todos los índices de la Fase 6 y verifica con explain() que se usan
  6. 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

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

¿Me invitas a un café?

Comentarios

Todavía no hay ningún comentario.

Visitantes en tiempo real

Estás solo: 🐱