Iniciando el entorno

Para testear Mongo, vamos a usar Docker, creando un contenedor con la ultima imagen de Mongo y entrando en él para comenzar a ejecutar los primeros comandos.

docker container run –name mongo -d mongo
docker container exec -ti mongo bash

Para saber más de como instalar y el manejo básico de Docker, podéis ver las entradas relacionadas con Docker en este blog.

Creando los primeros documentos

Mongo es un gestor de bases de datos no relacionales. Las bases de datos en Mongo están compuestas por colecciones, que a su vez están compuestas por documentos.

Vamos a crear una base de datos llamada test y una colección inventory de documentos.

Para crear una base de datos nueva o cambiar a una existente, usamos el comando use. De esta forma, para crear una base de datos llamada test, escribiremos en la consola de Mongo use test.

Con la imagen anterior podemos darnos cuenta de algunas cosas, además de aprender algun comando útil de la shell de mongo:

  1. El comando para cambiar de base de datos, así como para crear una nueva es use.
  2. Para mostrar las bases de datos usamos el comando show seguido por el subcomando databases. No se muestran las bases de datos que no tengan colecciones en su interior, como la que acabamos de crear.
  3. Para insertar un conjunto de documentos en una colección no es necesario crear previamente la colección (aunque también existe la posibilidad de crear la colección vacía).
  4. Tras el comando ejecutado con éxito se nos muestra un resumen de los documentos creados y el id asignado por Mongo a cada uno de ellos.
  5. Para mostrar las colecciones dentro de la base de datos que estamos usando, usamos nuevamente el comando show seguido por el subcomando collections.
  6. Ahora, al tener una coleccion la base de datos que creamos al principio, se nos muestra al ejecutar el comando correspondiente.

El script inicial para insertar documentos está sacado de la documentación oficial de Mongo.

Operaciones CRUD

CRUD es el acrónimo de las palabras inglesas Create,  Read, Update y Delete.

Vamos a ver la sintaxis básica de cada una de estas operaciones en Mongo

Create

Ya vimos de pasada en el punto anterior para crear colecciones y documentos el procedimiento insertMany(), pero ¿y si queremos insertar sólo un documento? ¿Y si queremos guardar un documento en una variable y luego introducirla en la colección? Podemos hacer ambas cosas.

Los documentos de Mongo siguen una estructura determinada, fuertemente influenciada por JSON, con lo que un documento no es más que una sucesión de pares clave-valor, donde el valor puede ser cualquier tipo de datos BSON, incluyendo otros documentos o arrays. Eso si, todas las claves son de tipo String.

Veamos un ejemplo sencillo de introducción de un único documento. Ya que Mongo permite registros/documentos duplicados (asigna un id diferente a cada uno de ellos internamente que actúa como una suerte de clave primaria), vamos a usar el primer documento introducido en el paso anterior para ejemplificar el proceso.

Vemos como efectivamente, podemos crear una variable a la cual asignar un documento para despues introducir dicho documento en una colección, aunque ya lo hayamos introducido antes. Mongo se encargará de asignarle un ObjectId distinto a cada uno de ellos.

Read

El comando find se encarga de devolver los documentos en funcion de unas condiciones y mostrarlos, si así se desea, de distinta manera, por ejemplo, ocultando algún par clave-valor.

La sintaxis del comando es:

db.<collection>.find(filter, projection);

Ambos parámetros son opcionales, ¿pero qué significan cada uno de ellos?

El parámetro filter es el filtro por el que van a pasar los documentos de la colección. Un ejemplo podría ser: muestrame los documentos que tengan como valor de la clave «qty» el número 25.

El parámetro projection se encarga de mostrar o ocultar los pares clave-valor del documento. Por defecto muestra todos, pero si queremos filtrar la información podríamos hacerlo mediante este parámetro de forma que podríamos ampliar la sentencia anterior como: muestrame sólo los tags de los documentos que tengan como valor de la clave «qty» el número 25.

En definitiva, podríamos decir que el query es el where y el projection el select de una consulta MySQL.

Importante: Los parámetros de las funciones de mongo van entre llaves y separados por comas. Si queremos obviar un parámetro basta con poner dos llaves que no encierran nada.

  1. Seleccionamos los documentos con la clave qty igual a 25.
  2. Seleccionamos los documentos con qty igual a 25 y mostramos sólo sus tags. Como por defecto aparece el id de cada documento, tenemos que decir explícitamente que no lo deseamos mostrar.
  3. Si no queremos filtrar pero si mostrar sólo los tags de los documentos, dejamos el primer parámetro vacío.

Daros cuenta de que las llaves pueden ir entre comillas o sin comillas.

Si quisiéramos añadir una condición de filtro más compleja, la cosa cambia. Por ejemplo, comencemos por una condición de filtro basada en mayor o igual que un valor. Si antes simplemente poníamos dos puntos y el valor por el que queríamos filtrar, ahora ese valor deja de ser único e implica crear una condicion más compleja que un mero equals. Estas condiciones más complejas van encapsuladas entre llaves, como un objeto cuya clave es el operador y su valor, el valor de la condicion. La sintaxis sería:

db.<collection>.find({key:{$operator:value}}, projection);

En este enlace de la documentación oficial podemos ver cuales son los operadores que existen para filtrar y proyectar/seleccionar.

¿Y si queremos añadir condiciones de filtro? Por ejemplo, que el campo qty sea mayor que 10 o no tenga el tag «blue».

El parámetro query se puede expresar también como un objeto que tiene como clave un operador lógico y como valor un array de objetos que contienen los campos y sus condiciones. Se autoexplica con la sintaxis y un ejemplo.

db.<collection>.find({$logical_op:[{key:{$operator:value},key:{$operator:value}}]}, projection);

En la shell de mongo resulta complicado ejecutar consultas complejas, sobre todo las primeras veces debido a lo poco legible que resulta el formato JSON en una única linea. Si usamos la tecla enter podemos comenzar a tener un código más legible.

Nota: El operador lógico and se puede obviar, ya que es el operador por defecto. Simplemente poniendo en el parámetro query el conjunto de campos y los valores por los que filtrarlos, obtenemos el mismo resultado que haciendo explícito el operador and. Estas dos expresiones son equivalentes:

  • db.inventory.find( { $and: [ {qty:{$gt:10}}, {tags:{$in:[«blue»]}} ]});
  • db.inventory.find( {qty:{$gt:10},tags:{$in:[«blue»]}});

Update

Para actualizar documentos usamos los procedimientos updateMany(), updateOne() o replaceOne(). La sintaxis de todos ellos es la siguiente:

db.<collection>.replaceOne(<filter>, <update>, <options>);

  1. El parámetro filter funciona de la misma forma que en la query, filtrando los documentos en función de su campos y valores.
  2. El parámetro update es el encargado de cambiar el valor de una serie da campos que consideremos por otros valores que consideremos.
  3. El parámetro options es una serie de pares clave-valor en un objeto con propósitos muy diversos, que por simplicidad, no vamos a tratar.

Ya sabemos filtrar documentos por haberlo tratado en el apartado correspondiente a la consulta de documentos. El segundo parámetro (encargado de la actualización), es el más interesante en este caso. Podemos ver todos los operadores de actualización con una breve descripción aquí.

Veamos un pequeño ejemplo. Si queremos quitar el campo tags y setear a 0 la cantidad, a todos los documentos con una cantidad inferior a 30 unidades, podríamos poner esta expresión en la shell:

db.inventory.updateMany({qty:{$lte:30}},{$unset:{tags:»»}, $set:{qty:0}})

Existen muchos más operadores para actualizar los campos o los valores de los campos, pero por la simplicidad de la entrada y por existir una excelente documentacion oficial, no nos vamos a extender en este punto.

Delete

Si queremos eliminar un documento de una colección, al igual que a la hora de crearlos, tenemos dos procedimientos en función de si queremos eliminar uno o varios: deleteOne() y deleteMany().

El procedimiento tiene dos parámetros. El primer parámetro es el parámetro filter que ya hemos visto en los dos tipos de operaciones anteriores. En funcion de los documentos filtrados, podemos, en el segundo parámetro especificar una serie de opciones para el procedimiento, siendo este, nuevamente opcional.

No vamos a incidir más por tanto en este procedimiento ya que ya hemos visto cómo funciona el parámetro filter.