Bases de datos: operar con la base de datos - parte 2
Tags: basesdedatos, mongodb, mql, javascript
Published: 2022-08-25 11:54:06

En la entrada anterior aprendimos cómo crear una base de datos y una colección, y también empezamos a trabajar con la base de datos usando las operaciones CRUD para crear documentos. 

Al final de la entrada, les recomendé utilizar el comando `mongoimport` de las herramientas de base de datos, para importar un set o conjunto de datos más extenso, y así poder trabajar con MQL de manera más compleja. 

Si no lo hicieron, les recomiendo ir a la entrada 3 y seguir las instrucciones.


## Operaciones CRUD para leer documentos

Hoy vamos a aprender a usar MQL, el lenguaje de consultas de MongoDB para, justamente, consultar nuestra base de datos de `redtatuaje`.

Como aprendimos en la entrada anterior, vamos a usar la terminal para conectar a nuestro servidor local, y usar la base de datos deseada y colección, deseadas.


![conectar a mongodb servidor local](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e2jye1asf1x50k4nuyrk.png)


### Operación CRUD findOne()

Lo primero que vamos a hacer es inspeccionar cualquier documento de la base de datos, de manera aleatoria, solo para ver qué estructura tiene un documento.

Lo haremos ejecutando primero, como aprendimos antes, `show collections` para ver qué colecciones tenemos disponibles y asegurarnos que hemos importado correctamente los datos, para poder seguir las indicaciones de esta entrada. 

![mostrar las colecciones mongodb](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/npgwpf2c3t5ofvnspqyj.png)

y luego ejecutando

```
db.artistas.findOne()
```

![mongodb metodo findOne() ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/le2nq6exbt52meze6mkz.png)

`findOne()` devuelve el primer documento que encuentra, que satisface un determinado criterio de consulta, es decir, el primero a partir del orden natural en el que se encuentran los documentos. Si la colección tiene un límite de algún tipo (generalmente en MB), entonces el orden natural corresponde al orden de inserción. En este caso vemos que devuelve el correspondiente al `artist_id: 743` correspondiente al `name: "Alex Holt"`.

#### Parámetros opcionales de `findOne()`

El método findOne() acepta que se le pasen dos argumentos **opcionales** para determinar ese criterio. Por ejemplo, podemos decir

```
db.artistas.findOne(query, projection)
```

la consulta en sí, y una condición de proyección.

Si vemos la imagen anterior, podemos comprobar que para el artista de id 743, la propiedad `owns_studio`, tiene el valor `Yes`. Podemos pedir a MongoDB que nos devuelva el primero por orden natural, para que ese valor sea `No`.

```
db.artistas.findOne({ "owns_studio": "No"})
```

![findOne() pasandole una consulta](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3eyh234dzrt8ogmkdelo.png)

En cuyo caso, como podemos ver, el id de artista es ahora `701`, correspondiente a la artista de `name: "Sue King"`.

## Proyección de consultas (Projection)

Pero hemos dicho que este método además aceptaba un segundo parámetro adicional, de proyección. ¿Qué significa eso?

MongoDB siempre va a devolver los campos de un documento que se encuentran proyectados. Por defecto, todos los campos están proyectados, o sea tienen el valor de proyección a 1 o `true`.

> _**1 u otros valores diferentes de 0 y true, especifican inclusión en la proyección**_

Sin embargo, si explícitamente proyectamos un campo, solo se devolverá ese campo junto con el valor del `_id`, que solo se excluye si se pone su valor de proyección explícitamente a 0 o `false`.

Veamos un ejemplo

```
db.artistas.findOne({ "owns_studio": "No"}, {"owns_studio": 1, "name": 1})
```

![findOne() con consulta y proyección ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gt919sdy3l89f5akupc7.png)

Si queremos omitir el valor del campo `_id`, le tenemos que pasar 0 explícitamente (o `false`) 

```
db.artistas.findOne({ "owns_studio": "No"}, {"owns_studio": 1, "name": 1, "_id": 0})
```

![exluir el _id en la proyección Mongodb](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2pnxcuz0l0cvgpwmkhew.png)

_Vamos a ver a estudiar `Projection` más adelante en la serie con más profundidad, ya que hay muchísimas más formas de proyectar, y formas especiales para datos más complejos, como los datos anidados, o los arreglos._

### Operación CRUD find()

Hasta ahora, como pedíamos MongoDB encontrar uno, o findOne(), siempre devolvía solo uno...Pero, ¿qué pasa cuando queremos una lista?

Entonces debemos ejecutar 

```
db.artistas.find()
```

Pero espera...¿Qué ha pasado? ¿Nos ha devuelto los 1000 documentos que habíamos importado a la colección?

No. Este método devuelve un `cursor`, que es una referencia a un conjunto de resultados que devuelve una consulta, y que se pueden iterar. Es decir, podemos ver más de lo que hay en el cursor y luego acceder al documento que deseamos.

Por defecto el cursor para find() nos muestra 101 resultados siempre que no se exceda el máximo tamaño de documento, que es de 16MB (como ya vimos en la entrada 1 de la serie) y una de las formas de obtener más es ejecutando `it`, como vemos en la imagen.


![iterar el cursor en mongoDB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e0vnenc3jxdwq6gl1wgw.png)

También podemos asignar el cursor a una variable (¡recordemos que estamos en un REPL completamente funcional!) y utilizar algunos de sus métodos. Por ejemplo. 

```javascript
let tatuadores = db.artistas.find({ "owns_studio": "Yes" });

while (tatuadores.hasNext()) { 
  printjson(tatuadores.next());
}
```

Aquí estamos usando los métodos `hasNext()` que evalúa si hay más documentos que satisfagan la consulta, en el cursor, y utilizamos el método `next()`, para iterarlos mientras `hasNext()` devuelva `true`.

Otros métodos del cursor son `forEach()`, `toArray()` y `objsLeftInBatch()`, que nos dice cuántos objetos hay en el batch, después de ejecutar una iteración (o conjunto) devuelto.

Podemos también utilizar el método `isExhausted()` para comprobar que realmente no existen más objetos o documentos, en el cursor.

> **_Recordemos que cada documento mapea a un objeto._**

Una lista completa de métodos del cursor, se pueden encontrar [aquí](https://www.mongodb.com/docs/manual/reference/method/js-cursor/)

_Cuando se usaba `find()` con las versiones anteriores de la consola, se solía concatenar el método `pretty()`, para que los resultados en el cursor se mostrasen formateados. En `mongosh`, `pretty()` se ejecuta por defecto._

Veamos un ejemplo de usar `forEach()`:

```javascript
let tatuadores = db.artistas.find({ "owns_studio": "Yes" });

tatuadores.forEach((tatuador) => { 
  if (tatuador.payment_method === "Credit Card Only") { 
    printjson(tatuador);
  }
});
```

### Limitar el cursor a una cantidad específica de resultados en el batch

En una base de datos muy grande, con documentos de tamaño muy pequeño, podríamos tener demasiada cantidad de objetos devueltos. Si queremos limitarlos de manera explicita para aumentar el rendimiento, podemos usar el método `limit()`, de esta manera.

```
db.artistas.find({"payment_method" : "Credit Card Only"}).limit(5)
```

Más información sobre el método `limit()` se encuentra [aquí](https://www.mongodb.com/docs/manual/reference/method/cursor.limit/#mongodb-method-cursor.limit).

#### Parámetros opcionales de `find()`

Find también acepta la configuración de dos parámetros opcionales: la consulta y la proyección.


## Consultas complejas con operadores de consulta

Vamos a empezar a realizar consultas más complejas, utilizando los operadores de comparación. En otras entradas exploraremos los lógicos, de elemento, de evaluación, de array y geoespaciales. Y por supuesto, haremos una entrada específica sobre proyección.

¡Empecemos!

### Encontrar un valor equivalente al pasado en la consulta: operador `$eq`

Este operador nos permite consultar la base de datos para encontrar todos los documentos que tienen un valor especifico que equivale al que se pasa. Por ejemplo, vamos a buscar todos los artistas que no tienen estudio propio.

_Vamos a limitar las consultas siempre a 5 para reducir el tiempo de respuesta, pero eso no es un requerimiento._

```
db.artistas.find({ "owns_studio": { $eq: "No" } }).limit(5)

```
El formato de la consulta es:

```
{ <campo>: { $eq: <valor> } }
```

Podemos decir que esto es equivalente a realizar esta consulta

```
db.artistas.find({ "owns_studio": "No" }).limit(5)

```

sin embargo, al usar operadores, los podemos combinar o usar operadores lógicos, para consultas más complejas, como veremos mientras progresemos.

### Encontrar un valor más grande que el valor pasado: operador `$gt`

Este operador nos permite encontrar los documentos para los cuales el valor del campo pasado, es más grande que el especificado. En este caso, todos los artistas con un identificador más grande que 500

```
db.artistas.find({ "artist_id": { $gt: 500 } }).limit(5)

```

El formato de la consulta es:

```
{ <campo>: { $gt: <valor> } }
```

Igualmente podemos encontrar valores más grandes o iguales al valor pasado, con `$gte`, más pequeños con `$lt`, o más pequeños o iguales al valor pasado, con `$lte`. La sintaxis es la misma.

### Encontrar todos los valores diferentes a: operador `$ne`

`ne` vendría a significar `not equal`, o `no igual`. Lo usamos para encontrar todos los documentos cuyo valor no es igual al pasado.

```
db.artistas.find({ "artist_id": { $ne: 500 } }).limit(5)

```

_Por supuesto que no necesariamente estamos haciendo comparaciones numéricas. Estamos comparando valores literales. Por lo cual, podemos pasar cualquier tipo._

```
db.artistas.find({ "name": { $ne: "Cordelia Swanson" } }).limit(5)

```

Y esto es cierto para las comparaciones de arriba, también. Por ejemplo:

```
db.artistas.find({ "country": { $lt: "PE" } }).limit(5)
```

Donde devolverá todos los valores más pequeños que "PE" como código de país, en orden alfabético.

## Comparar fechas con operadores de comparación

También los podemos usar para las fechas. Por ejemplo, si queremos encontrar a todos los artistas que se unieron a la plataforma hace un año o más, lo haríamos así:

```
db.artistas.find({ "joined_network": { $lte: new Date("2021-08-12") } }).limit(5)
```

## Excluir resultados que contengan un valor en un campo array: operador `$nin`

Este operador `not in`, nos permite excluir objetos del cursor, sí contienen un cierto valor en un campo de tipo arreglo. 

Por ejemplo, supongamos que queremos filtrar a todos los artistas que se dedican a los estilos `geometric` y `realism`, de nuestro cursor de resultados.

```
db.artistas.find({ "styles": { $nin: ["geometric", "realism"] } }).limit(10)

```

`$nin` también puede usarse para excluir del cursor los documentos **que no contienen** un cierto campo. 


Más información sobre este operador de condición, se encuentra en [la documentación](https://www.mongodb.com/docs/manual/reference/operator/query/nin/#mongodb-query-op.-nin)

## MQL en el mongosh vs. operar con un driver

No se debe confundir los métodos de operación de MQL en la consola de MongoDB, con los métodos que provean los diferentes drivers cuando se está escribiendo una aplicación.

Muchos de estos drivers (o software intermedio) crean capas de abstracción para simplificar la combinación de operadores, y es posible que ofrezcan diferentes métodos para operar con la base de datos.

En la próxima entrada, vamos a usar operadores lógicos. ¡Hasta entonces!


_Gracias especiales a @wassimchegham que siempre me revisa los screenshots y a @dfreniche al que le pedí peer review, y me contesto "RIGHT NAO!" <3_