Como ya hemos comentado en anteriores artículos, MongoDB es una base de datos documental que almacena documentos JSON. Una de las peculiaridades de este tipo de documentos es que sus campos pueden contener arrays o subdocumentos. Incluso los campos pueden contener arrays de subdocumentos. Y es entonces cuándo las consultas se vuelven un poco más complicadas.
En el anterior artículo, pudimos ver que actualizar documentos en MongoDB es sencillo. Usando la sentencia update, podemos filtrar los datos qué queremos actualizar, y elegir qué campos queremos actualizar. Pero es algo más complicado si hablamos de arrays o subdocumentos. Hoy vamos a ver como podemos realizar alguna de estas operaciones.
Actualizando subdocumentos
Un subdocumento es el contenido de un campo de un documento JSON, pero que podría considerarse como un documento en si mismo. Algo así:
{ _id:1, "title": "Star Wars: a new hope", "year": 1977, "info": { "director": "George Lucas", "writer": "George Lucas", "sound": "Peter Wiliams" } }
El contenido del campo info, es un documento JSON dentro de otro documento JSON. Ahora imaginemos que queremos actualizar el campo sound de ese subdocumento. Para ello utilizaríamos lo que se conoce como dot notation. Un ejemplo:
db.genbetadev.update( {_id: 1}, {$set: {"info.sound": "John Williams"}} );
La sentencia es sencilla, y muy similar a las que ya explicamos en el primer artículo sobre actualización de datos. La única diferencia es que en este caso estamos indicando con un punto (info.sound), que estamos actualizando el campo de un subdocumento.
Si tuviésemos varios subdocumentos anidados, tendríamos que ir añadiendo niveles, hasta llegar al campo que queremos actualizar. Y eso lo haríamos poniendo puntos para delimitar los documentos y sus campos.
Añadiendo elementos a un array
Uno de los puntos fuertes de utilizar JSON, es que podemos añadir arrays que contengan una lista con varios elementos. Si queremos actualizar un documento existente, y añadir elementos a un campo array, tendremos que utilizar el operador $push. Vamos a suponer un documento como este:
{ _id:2, "title": "The Shawshank Redemption", "year": 1994, "info": { "director": "Frank Darabont", "writer": "Stephen King" } "cast": ["Morgan Freeman"] }
El campo cast, es un campo que contiene un array de un solo elemento. Con la siguiente sentencia, añadiremos un elemento nuevo a la lista:
db.genbetadev.update( {_id: 2}, {$push: {"cast": "Tim Robins"}} );
Si hacmeos un find para buscar el documento, obtendremos lo siguiente:
{ "_id" : 2, "cast" : [ "Morgan Freeman", "Tim Robins" ], "info" : { "director" : "Frank Darabont", "writer" : "Stephen King" }, "title" : "The Shawshank Redemption", "year" : 1994 }
El elemento "Tim Robins", se ha añadido a al array. Hay que tener en cuenta, que $push, añade el elemento al array sin comprobar si existía previamente. Si el elemento ya estaba en el array, este se volverá a añadir. De hecho, si ejecutamos otra vez la operación anterior, "Tim Robins", se insertará otra vez. Si queremos que MongoDB solo inserte el elemento cuando no existe, tendremos que utilizar el operador $addToSet.
db.genbetadev.update( {_id: 2}, {$addToSet: {"cast" : "Tim Robins"}} );
En este caso, MongoDB solo inserta el elemento si no existe en el array.
Si queremos añadir varios elementos a la lista, podemos ejecutar $push o $addToSet varias veces, pero es poco práctico y nada óptimo. Para insertar varios elementos en la misma sentencia, podemos usar el operador $each, de forma conjunta con $push o $addToSet.
db.genbetadev.update( {_id: 2}, {$addToSet: {"cast": {$each: ["Bob Gunton","Clancy Brown"]}}} );
En esta ocasión, hemos añadido dos elementos al array. Al utilizar $addToSet, los elementos solo se insertan en el caso de que no existan en el array. Si queremos permitir el uso de elementos repetidos, tenemos que usar $push.
Opciones especiales de $push: $slice y $sort
A la hora de actualizar arrays, tenemos dos operadores que pueden ayudarnos a realizar ciertas operaciones. Imaginemos que tenemos un documento como este:
{ "_id": "rubenfa", "last5": ["Star Wars", "The Shawshank Redemption", "Training Day", "The Incredibles", "The Departed"] }
En este caso, lo que queremos conseguir, es que el array tenga como mucho 5 elementos. Para ello utilizaremos el operador $slice.
db.genbetadev.update( {_id: "rubenfa"}, {$push: {"last5": { $each: ["Independece Day", "Matrix"], $slice: -5 } } } );
El operador $slice tiene que ser 0 o un valor negativo. Si es 0 el array quedará vacío. En el ejemplo estamos pasando un -5, por lo cual se almacenarán solo los últimos 5 valores. En este caso:
{ "_id" : "rubenfa", "last5" : [ "Training Day", "The Incredibles", "The Departed", "Independece Day", "Matrix" ] }
Como vemos, se guardan los dos valores que estamos insertando con el $each, y los tres anteriores. Los dos primeros son eliminados. Por tanto $slice, es útil para simular colas.
Antes de que se eliminen los elementos que sobran podemos ordenarlos con el operador $sort. Por ejemplo imaginemos el siguiente documento:
{ "_id": "rubenfa", "top5": [ {"title" : "Star Wars","vote" : 9}, {"title" : "The Shawshank Redemption","vote" : 8}, {"title" : "Training Day","vote" : 7}, {"title" : "The Incredibles","vote" : 8}, {"title" : "The Departed","vote" : 7.5} ] }
Si ejecutamos la siguiente sentencia:
db.genbetadev.update( {_id: "rubenfa"}, {$push: {"top5": { $each : [ {"title" : "Independece Day","vote": 6}, {"title" : "Matrix",vote : 8} ], $sort : {"vote" : 1}, $slice: -5 } } } );
Lo que estamos haciendo es primero ordenar los elementos del array por el campo vote de forma ascendente, y luego seleccionar los últimos 5. El resultado, después de hacer un find, sería el siguiente:
{ "_id" : "rubenfa", "top5" : [ { "title" : "The Departed", "vote" : 7.5 }, { "title" : "The Shawshank Redemption", "vote" : 8 }, { "title" : "The Incredibles", "vote" : 8 }, { "title" : "Matrix", "vote" : 8 }, { "title" : "Star Wars", "vote" : 9 } ] }
Como se puede ver, solo quedan los cinco elementos con las calificaciones más altas.
Hemos podido comprobar, que añadir elementos a un array de un documento que ya existe es sencillo con los operadores $push y $addToSet. También podemos añadir varios elementos ayudándonos del operador $each.
En el siguiente artículo veremos como podemos eliminar elementos de un array, o como podemos actualizar elementos según su posición. Y también explicaré la última operación que nos queda por ver: el borrado de documentos.
En GenbetaDev | MongoDB: qué es, cómo funciona y cuándo podemos usarlo (o no), MongoDB: empezando por el principio. Insertando datos Imagen | kishjar