06 Ene

Consultas Avanzadas con el Framework de Agregación

Usando aggregate, 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) y cuántas actividades hay que cumplen dicha condición. Queremos esos valores solo para los TipoOcio que tengan más de 5 actividades. Clasifica el resultado de modo descendente por TipoOcio y Empresa.

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 } }
])

Operaciones de Actualización Atómica y Upsert

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

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

Además, no queremos que otro proceso pueda escribir en la colección hasta terminar esta operación. Los cambios a realizar cuando se cumplen las condiciones (si no se cumplen: insertar el documento que además incluya estos datos) son: Nivel = 9, Coste = 5, y un atributo nuevo, MeGusta: "sí".

db.Ocio.update(
  {
    $and: [
      { TipoOcio: "montañismo" },
      { Empresa: "CabraMonteSA" },
      { Elemento: "CaminoSchmid" },
      { Propiedades: { $elemMatch: { "dificultad": "media" } } }
    ],
    $isolated: 1
  },
  { $set: { Nivel: 9, Coste: 5, MeGusta: "sí" } },
  { upsert: true }
)

Nota: El literal «upsert» se utiliza para resaltar que, si no existe el documento, se creará uno nuevo con los datos proporcionados.

Procesamiento de Datos con find y forEach

c) Usando solo una instrucción find con la función forEach que recorra la colección: queremos actualizar solo aquellas actividades cuyo TipoOcio es 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 (< 5):

  • Si el coste es menor de 1000, aplicamos esta fórmula: (10 - (5 – nivel) / 10) * coste. Además, añadimos el atributo: Oferta = "regular".
  • Si el coste es mayor o igual a 1000, aplicamos: (8 - (5 – nivel) / 10) * coste. Además, añadimos el atributo: Oferta = "buena".
db.Ocio.find({
  $and: [
    { Nivel: { $lt: 5 } },
    { TipoOcio: { $in: ["montañismo", "rafting", "piragua", "piano"] } }
  ]
}).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(myDoc); /* Para visualizar la modificación */
  db.Ocio.save(myDoc); /* También se puede realizar mediante update */
});

Gestión de la Colección de Películas

Tenemos creada una colección llamada Peliculas, dentro de la base de datos cosasDeCine, cuyos documentos tienen los siguientes campos: Género, Director, Título, Valoración (0 a 10), Coste y NombresPersonajes (array).

Filtrado y Formateo de Resultados

a) Usando una sola instrucción find, obtener las películas con valoración entre 4 y 8 ambos incluidos, y donde el número de NombresPersonajes sea exactamente 3. 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) lo siguiente: el Género, el Título y la valoración relativa (calculada como Valoración * Coste).

SOLUCIÓN:

db.getCollection('Peliculas').find({
  $and: [
    { Valoración: { $gte: 4 } },
    { Valoración: { $lte: 8 } },
    { NombresPersonajes: { $size: 3 } }
  ]
}).forEach(function(doc) {
  print("Género: " + doc.Género + " Título: " + doc.Título + " -- Val.Rel.: " + (doc.Valoración * doc.Coste));
});

Agrupación y Estadísticas por Género

b) Usando una sola instrucción aggregate, queremos acumular lo siguiente para las películas con el mismo Género y Valoración (solo para aquellas con Valoración igual o mayor a 5):

  • Un campo con la suma total de todos sus Costes.
  • Otro campo con la cantidad de dichas películas.
  • Otro campo con el Director y el Título de cada una de dichas películas.

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

db.Peliculas.aggregate([
  { $match: { "Valoración": { $gte: 5 } } },
  {
    $group: {
      _id: { Género: "$Género", Valoración: "$Valoración" },
      total: { $sum: "$Coste" },
      count: { $sum: 1 },
      lasPelis: { $addToSet: { ElDirector: "$Director", Nombre: "$Título" } }
    }
  },
  { $sort: { "_id.Género": -1 } }
])

Deja un comentario