Aprender a usar MongoDB: Guía 4

Anteriormente hemos visto como introducir datos en la base de datos MongoDB, y cómo realizar todo tipo de consultas para obtener los datos almacenados con distintos tipos de filtro.

En el post de hoy, vamos a trabajar en las operaciones que quedan, como las actualizaciones, borrado de datos, paginación y consultas por cursor.

Anteriormente

Guía 3: Comandos y operaciones esenciales

Operaciones avanzadas en MongoDB

Paginación

Obtener datos con paginación: en el ejemplo, podemos ver cómo obtener la segunda página de resultados, considerando que nuestras páginas fuesen de 2 elementos únicamente. Para ello, utilizamos la operación skip (que determina cuántos elementos se van a ignorar de los resultados obtenidos) y la operación limit (que especifica cuántos elementos deseamos obtener del nuevo conjunto de resultados).

El orden es irrelevante, ya que primero se aplicará el filtro (si existe), después skip, y finalmente limit.

> db.scores.find().skip(2).limit(2)

{ "_id" : 3, "username" : "franbuipa", "score" : 30 }
{ "_id" : 4, "username" : "cocinero", "favorites" : [ "cocinar", "postres" ], "address" : { "country" : "es", "city" : "madrid" } }

Ordenar resultados en MongoDB

Podemos añadir la operación sort para ordenar los resultados por el valor de sus atributos. Para utilizar el orden natural, hay que utilizar el 1 positivo, y para invertir los resultados, un 1 negativo. Por supuesto podemos combinarlo con skip o limit, y el orden sigue siendo relevante, primero se aplica el filtro, luego sort, después skip y finalmente limit.

> db.scores.find().sort({ _id : 1 }).limit(2)

{ "_id" : 1, "username" : "findemor", ... }
{ "_id" : 2, "username" : "eden4ever", ... }

> db.scores.find().sort({ _id : -1 }).limit(2)

{ "_id" : 5, "username" : "deportista", ... }
{ "_id" : 4, "username" : "cocinero", ... }

Manejo de cursores en MongoDB

Como ya hemos visto, la shell de mongo funciona como una consola javascript, por lo que podemos escribir pequeños programas, o iterar sobre cursores, simplemente declarándolo como una variable javascript.

> var c = db.scores.find().sort({ username : -1 }).limit(2)
> c.hasNext()

true

> c.next()

{ "_id" : 3, "username" : "franbuipa", "score" : 30 }

Actualización de documentos en MongoDB

Para actualizar documentos, basta con utilizar la operación update con dos parámetros. El primero permite especificar el filtro que determina qué documentos serán actualizados (pero ojo, que solo se actualizará el primer elemento que coincida y se detendrá la operación, comportamiento que se puede modificar como veremos más abajo). El segundo parámetro es la actualización que se va a realizar.

Es importante tener en cuenta que la operación update actualizará el documento en su totalidad, para comprenderlo, prestad atención a lo que pasa con el atributo finished del siguiente documento cuando se actualiza su score.

> db.scores.find({ _id : 1 })

{ "_id" : 1, "username" : "findemor", "score" : 50, "finished" : true }

> db.scores.update({ _id : 1 }, { _id : 1, username : “findemor”, score : 60 })

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.scores.find({ _id : 1 })

{ "_id" : 1, "username" : "findemor", "score" : 60 }

Podemos evitar este efecto, como se muestra a continuación.

Actualizar un atributo sin modificar el resto del elemento (si el atributo existe, se modifica, en otro caso se añade).

> db.scores.find({ _id : 1 })

{ "_id" : 1, "username" : "findemor", "score" : 60 }

> db.scores.update({ _id : 1 }, { $set : { finished : true }})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.scores.find({ _id : 1 })

{ "_id" : 1, "username" : "findemor", "score" : 60, "finished" : true }

Se pueden realizar otras acciones trabajando con atributos independientes.

Incrementar el valor (si no existe, lo crea con el valor especificado)

> db.scores.update({ _id : 1 }, { $inc : { score : 1 }})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.scores.find({ _id : 1 })

{ "_id" : 1, "username" : "findemor", "score" : 61, "finished" : true }

Eliminar una propiedad (o atributo) de un elemento.

> db.scores.update({ _id : 1 }, { $unset : { finished : 1 }})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.scores.find({ _id : 1 })

{ "_id" : 1, "username" : "findemor", "score" : 61 }

UPSERT en MongoDB, actualiza el documento, y si no existe, lo crea aprovechando los datos del update como definición del nuevo documento.

> db.scores.find({ _id : 10 }).count()

0

> db.scores.update({ _id : 10 }, { $set : { score : 74 } }, { upsert : true })

WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : 10 })

> db.scores.find({ _id : 10 })

{ "_id" : 10, "score" : 74 }

Actualización masiva de documentos

Cuando se realiza un update, se modificará el primer elemento coincidente y se detendrá la operación. Si queremos realizar una actualización masiva, hay que utilizar el parametro multi. Prestad atención el valor de nModified (numero de documentos modificados) en la respuesta de la operación, tras las siguientes operaciones.

> db.scores.update({}, { $set : { finished : true }})

WriteResult({ "nMatched" : 1, "nUpserted" : 0,"nModified" : 1 })

> db.scores.update({}, { $set : { finished : true }}, { multi : true })

WriteResult({ "nMatched" : 6, "nUpserted" : 0, "nModified" : 5 })

Actualizando atributos de tipo Array (listas)

Los arrays admiten numerosas operaciones específicas que proporcionan una enorme flexibilidad de actualización en MongoDB, vamos a repasar rápidamente algunas de ellas.

Primero veamos qué arrays tenemos en nuestra colección, para que podáis seguir la ejecución de las operaciones.

> db.scores.find({ favorites : { $exists : 1 }}).pretty()

{
    "_id": 4,
    "username": "cocinero",
    "favorites": ["cocinar", "postres"],
    "address": {
        "country": "es",
        "city": "madrid"
    },
    "finished": true
} {
    "_id": 5,
    "username": "deportista",
    "favorites": ["running"],
    "address": {
        "country": "es",
        "city": "toledo"
    },
    "finished": true
}

Modificar un elemento concreto (por posición) dentro de un array.

> db.scores.update({ _id:4 }, { $set : { “favorites.1” : “comer” }})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.scores.findOne({ _id:4 })

{
    "_id": 4,
    "username": "cocinero",
    "favorites": ["cocinar", "comer"],
    "address": {
        "country": "es",
        "city": "madrid"
    },
    "finished": true
}

Añadir un elemento al array por la derecha

> db.scores.update({ _id:4 }, { $push : { favorites : “cazar” }})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

Eliminar un elemento del array por la izquierda (para que fuese por la derecha, habria que sustituir -1 por 1 positivo)

> db.scores.update({ _id:4 }, { $pop : { favorites : -1 }})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

Veamos como va quedando nuestro documento para comprender las acciones anteriores…

> db.scores.findOne({ _id : 4 })

{
    "_id": 4,
    "username": "cocinero",
    "favorites": ["comer", "cazar"],
    "address": {
        "country": "es",
        "city": "madrid"
    },
    "finished": true
}

Añadir todos los elementos especificados al Array

> db.scores.update({ _id : 4 }, { $pushAll : { favorites : [ “a”, “b”, “c” ]}})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.scores.findOne({ _id : 4 })

{
    "_id": 4,
    "username": "cocinero",
    "favorites": ["comer", "cazar", "a", "b", "c"],
    "address": {
        "country": "es",
        "city": "madrid"
    },
    "finished": true
}

Eliminar todos los elementos coincidentes del Array. También podríamos usar el operador pull para eliminar solo un elemento cualquiera especificando su valor.

> db.scores.update({ _id : 4 }, { $pullAll : { favorites : [ “a”, “b”, “c” ]}})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.scores.findOne({ _id : 4 })

{
    "_id": 4,
    "username": "cocinero",
    "favorites": ["comer", "cazar"],
    "address": {
        "country": "es",
        "city": "madrid"
    },
    "finished": true
}

Añadir un elemento al array únicamente si no existe ya.

> db.scores.update({ _id : 4 }, { $addToSet : { favorites : “cazar” }})

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })

Eliminar datos y colecciones en MongoDB

Para eliminar documentos, podemos determinar un criterio de filtro para eliminar todos los coincidentes del mismo modo que lo hacemos al utilizar el comando find.

> db.scores.remove({ _id : 4 })

WriteResult({ "nRemoved" : 1 })

Eliminar una colección (y todos los documentos que contiene)

> db.scores.drop()

true

> show collections

system.indexes

Conclusión

Tras las operaciones descritas desde la última publicación, hemos visto multitud de operadores para trabajar con datos dentro de MongoDB. Pero MongoDB va mucho más allá, y para usarlo eficientemente necesitamos conocer aún tres elementos relevantes:

  • Cómo trabajar con esquemas dirigidos por la aplicación (frente a la tradicional Tercera Forma Normal de SQL).

  • Definir índices para acelerar las consultas en MongoDB.

  • Cómo utilizar el framework de agregación para agrupar y analizar datos.

  • Trabajar con réplicas y Shards (Sharding).

Todo esto lo veremos en las siguientes publicaciones.

Enlaces a la guía completa

1 Comment / Add your own comment below

  1. Pingback: Aprender a usar MongoDB: Guía 1 | findemor

Leave a Reply

Your email address will not be published. Required fields are marked *