Ejercicios Prácticos de Manipulación de Datos en MongoDB: Aggregation y Actualizaciones


Corrección y Estructuración de Ejercicios de MongoDB

A continuación, se presenta la corrección detallada y la estructuración del documento original, enfocándose en la claridad, la sintaxis correcta y la aplicación de buenas prácticas en la manipulación de datos con MongoDB.

Ejercicio 1: Consultas de Agregación sobre la Colección ‘Ocio’

a) Cálculo de Medias y Conteo con Filtros

Usando el framework de aggregation, obtener para cada TipoOcio y Empresa:

  • El TipoOcio.
  • La Empresa.
  • La media de los costes de sus actividades que son más populares (Nivel >= 7).
  • La cantidad de actividades que cumplen dicha condición.

Se requieren estos valores solo para los TipoOcio que tengan más de 5 actividades que cumplan el criterio de popularidad. Clasifique el resultado de modo descendente por TipoOcio y Empresa.

Solución en MongoDB Shell:

db.Ocio.aggregate([
    { $match: { "Nivel": { $gte: 7 } } },
    { $group: { 
        _id: { 
            ElTipo: "$TipoOcio", 
            LaEmpresa: "$Empresa" 
        },
        mediaCoste: { $avg: "$Coste" }, 
        totdocs: { $sum: 1 } 
    } },
    { $match: { "totdocs": { $gte: 5 } } },
    { $sort: { _id: -1 } } 
])

b) Actualización Condicional con Bloqueo (Upsert y Aislamiento)

Usando solo una instrucción update, sin funciones que recorran la colección. Se busca actualizar el primer documento que cumpla las siguientes condiciones (o insertar un documento con esos datos, si no existe ninguno que cumpla las condiciones – upsert):

  • TipoOcio: «montañismo»
  • Empresa: «CabraMonteSA»
  • Elemento: «CaminoSchmid»
  • Dentro de las propiedades debe estar el par: (dificultad, media).

Además, no se quiere que otro proceso pueda escribir en la colección hasta terminar esta operación (uso de $isolated: 1).

Los cambios a realizar cuando se cumplen las condiciones (si no se cumplen, se inserta el documento) son:

  • Nivel = 9
  • Coste = 5
  • Atributo nuevo: MeGusta: «sí»

Solución en MongoDB Shell:

db.Ocio.update(
    { $and: [
        { TipoOcio: "montañismo" },
        { Empresa: "CabraMonteSA" },
        { Elemento: "CaminoSchmid" },
        { Propiedades: { $elemMatch: { "dificultad" : "media" } } }
    ], $isolated: 1 }, /* Bloquea escrituras hasta finalizar */
    { $set: { Nivel: 9, Coste: 5, MeGusta: "sí" } }, 
    { upsert: true } /* Si no existe, crea el documento */
)

c) Actualización Iterativa con find().forEach()

Usando solo una instrucción find con la función forEach que recorra la colección. Se deben actualizar solo aquellas actividades cuyo TipoOcio sea alguno de estos: «montañismo», «rafting», «piragua», «piano».

La actualización consiste en bajar el coste proporcionalmente, de aquellas actividades que tienen el nivel bajo (Nivel < 5):

  1. Si el Coste es menor de 1000:
    • Nuevo Coste: (10 - (5 – Nivel) / 10) * Coste.
    • Añadir atributo: Oferta = «regular».
  2. Si el Coste es mayor o igual a 1000:
    • Nuevo Coste: (8 - (5 – Nivel) / 10) * Coste.
    • Añadir atributo: Oferta = «buena».

Solución en MongoDB Shell (Corregida):

db.Ocio.find({ 
    TipoOcio: { $in: ["montañismo", "rafting", "piragua", "piano"] },
    Nivel: { $lt: 5 }
}).forEach(function (myDoc) {
    if (myDoc.Coste < 1000) {
        myDoc.Coste = ((10 - (5 - myDoc.Nivel)) / 10) * myDoc.Coste;
        myDoc.Oferta = "regular";
    } else {
        myDoc.Coste = ((8 - (5 - myDoc.Nivel)) / 10) * myDoc.Coste;
        myDoc.Oferta = "buena";
    }
    print("Documento modificado: " + myDoc);
    db.Ocio.save(myDoc); /* Se recomienda usar update en lugar de save para operaciones masivas */
});

Ejercicio 2: Consultas sobre la Colección ‘Peliculas’

Tenemos una colección llamada Peliculas, dentro de la base de datos cosasDeCine. Los documentos tienen los siguientes campos:

  • Genero (de la película).
  • Director (uno por película).
  • Título.
  • Valoracion (de 0 a 10).
  • Coste.
  • NombresPersonajes (un array).

a) Búsqueda con Proyección y Cálculo en forEach

Usando una sola instrucción find, obtener las películas con Valoracion entre 4 y 8 (ambos incluidos) y que el número de NombresPersonajes sea exactamente 3 (usando $size).

Al resultado del find, para cada película encontrada, aplicar una función anónima que imprima en una línea, incluyendo el nombre del campo y su valor, los siguientes datos:

  • El Género.
  • El Título.
  • La valoración relativa (calculada como Valoracion multiplicada por el Coste).

Solución en MongoDB Shell:

db.Peliculas.find(
    { $and : [
        { Valoracion: { $gte: 4 } },
        { Valoracion: { $lte: 8 } },
        { NombresPersonajes: { $size: 3 } }
    ]}
).forEach(function(doc){
    var valoracionRelativa = doc.Valoracion * doc.Coste;
    print("Género: " + doc.Genero + ", Título: " + doc.Título + ", Val.Rel.: " + valoracionRelativa);
});

b) Agregación por Género y Valoración

Usando una sola instrucción aggregate, queremos acumular lo siguiente para las películas con el mismo Género y Valoracion (pero solo para aquellas películas con Valoracion >= 5):

  1. Un campo con la suma total de todos sus Costes.
  2. Otro campo con la cantidad de dichas películas.
  3. Otro campo con el Director y el Título de cada una de dichas películas (usando $addToSet para evitar duplicados si se agrupa por campos que no son únicos).

Después, en la misma instrucción aggregate, queremos clasificar el resultado por Género, de modo descendente.

Solución en MongoDB Shell (Corregida):

db.Peliculas.aggregate([
    { $match: { "Valoracion": { $gte: 5 } } },
    { $group: { 
        _id: { 
            Genero: "$Genero", 
            Valoracion: "$Valoracion" 
        },
        totalCoste: { $sum: "$Coste" },
        cantidad: { $sum: 1 },
        peliculasDetalle: { 
            $addToSet: { 
                ElDirector: "$Director", 
                Nombre: "$Título" 
            }
        }
    } },
    { $sort: { "_id.Genero": -1 } } /* Orden descendente por el campo Género dentro del _id */
])

Dejar un Comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *