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.
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.
y luego ejecutando
db.artistas.findOne()
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"})
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})
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})
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.
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.
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í
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()
:
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í.
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
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
Top comments (0)