Ahora hablaremos sobre las queries o busquedas que podemos hacer en Mongo, este post puede que sea algo más corto que el anterior, pero prefería poner esta parte un poco por separado.
Antes de empezar quiero comentar algunos conceptos similares entre las bases de datos relacionales y MongoDB pero que quizás tienen distinto nombre:
- Database: O base de datos en relacional es lo mismo en MongoDB.
- Table: O tabla en relacional en Mongo es Colección.
- Row: O fila en relacional en Mongo es Document/Documento.
- index: O indice en relacional es lo mismo en MongoDB.
- Join: Como se conoce en relacional en Mongo es Lookup
- Foreign Key: En Mongo se conoce como Reference/Referencia.
- Transacciones: En Mongo existen como transacciones de un unico documento. En relacional son necesarias las transacciones porque tenemos datos que modificar en multiples tablas, sin embargo, en Mongo toda la información que hace referencia a un objeto esta siempre en un unico documento, pensar que más o menos los datos se guardan al estilo JSON, es decir esta todo dentro de un mismo objeto.
Primero de todo vamos a crearnos de nuevo nuestra base de datos ya que la eliminamos en el post anterior
use ninjas
Y vamos a crearnos 3 elementos o documentos en ella
db.heros.insert({name: "Batman", money: 1000000})
db.heros.insert({name: "Iron Man", money: 5000000})
db.heros.insert({name: "Black Panther", money: 10000000})
Con esto ya tendríamos los 3 creados
Ya vimos como hacer algún filtro básico al estilo, buscame por el nombre X
db.heros.find({name: "Iron Man"})
También podemos usar expresiones regulares directamente, imaginemos que queremos que nos muestre todos los elementos que tienen Man o man en su campo name
db.heros.find({name: /[Mm]an/})
Esto nos devuelve tanto a Batman como Iron Man
Podemos limitar o ignorar la cantidad de datos que queremos. Con limit/skip y la cantidad de elementos
db.heros.find().limit(1)
db.heros.find().skip(1)
Pero, ¿y si queremos buscar por dos nombres distintos?...pués para ello usamos $in y le pasamos un array con las opciones
db.heros.find({name: {$in:["Batman","Black Panther"]}})
(tenemos tambien la opción $nin para indicarle que no sea distinto a lo que le indicamos)
Acabamos de ver un ejemplo buscando dentro de la misma propiedad, pero ¿y para buscar en propiedades distintas?...tenemos un $or muy bonito para ello, al cual le pasamos un array con las propiedades que queremos buscar
db.heros.find({$or:[{name: "Black Panther"},{money: 5000000}]})
Sin problemas nos encuentra lo que buscamos
Hasta el momento hemos realizado búsquedas exactas, es decir, buscábamos por elementos coincidentes 100%, pero no siempre sabemos el número exacto de algo....veamos como podemos realizar búsquedas que cumplan algún tipo de condición, por ejemplo hagamos que nos encuentre todos los elementos que su propiedad money sea superior a 5000000
db.heros.find({money: {$gt:5000000}})
Nos devuelve solo a Black Panther
Como os podéis imaginar el $gt es de greather than y al igual que greather than, tenemos less than (lt), greather than or equal (gte) o less than equal (lte). Probemos el gte
db.heros.find({money: {$gte:5000000}})
Ahora nos devuelve también a Iron Man
Todas estas formas de buscar se pueden mezcla entre sí, por ejemplo hagamos una búsqueda que sea menor de 5000000 o mayor de 5000000
db.heros.find({$or:[{money:{$lt: 5000000}},{money:{$gt:5000000}}]})
Y nos devuelve todo menos lo que era 5000000
Aunque esta búsqueda la podriamos haber realizado de alguna forma más sencilla, es solo para que se vea que podemos mezclar todo como queramos,pero , por ejemplo, podríamos haberlo indicado con un $nin
db.heros.find({money: {$nin: [5000000]}})
Veamos algunas cosas más...como tal un documento de mongo no tiene porque tener solo un nivel de profundidad, puede tener un montón de subdocumentos dentro.
Vamos a crearle un subdocumento a Batman indicando si tiene muchos o pocos amigos (al principio solo Alfred jejejeje). Repasamos así como usar update
db.heros.update({name: "Batman"},{$set:{friends:{civils: "Alfred",heros: "Robin"}}})
Si ahora buscamos a Batman veamos que tiene (le ponemos pretty para verlo mejor)
db.heros.find({name: "Batman"}).pretty()
También es posible tener un array de elementos, a los cuales podemos añadir más elementos directamente. Ahora trabajemos con Iron Man, añadamos una array de objetos con algunas de sus armaduras. Primero creemos un array vacio, con update
db.heros.update({name: "Iron Man"},{$set:{armors: []}})
Bien ahora añadamos el primero
db.heros.update({name: "Iron Man"},{$push:{armors: {armor1: "Mark I"}}})
Como véis hemos usado $push como modificador para añadir elementos. Si ahora mostramos vemos como tenemos un array con un elemento
Probemos añadiendo otro
db.heros.update({name: "Iron Man"},{$push:{armors: {armor2: "HearthBreaker"}}})
Ahora veríamos como tenemos 2 elementos
Performance
Hasta ahora hemos visto un poco de todas las operaciones que podemos hacer con mongo, no hemos visto algo que posiblemente algunos se pregunten, ¿es posible insertar más de un elemento?...Por supuesto que sí, pero depende más del lenguaje de programación usado, en general existe un método que es bulkInsert o similar donde pasandole un array,list, arrayList....insertará los registros. Esto lo veremos cuando juntemos MongoDB con NodeJS.
Bien ahora vamos a parar un poco y vamos a ver como podemos saber cuanto tardan nuestras querys, que hacen internamente y si podemos mejorar algo. Además veremos como podemos hacer para que nos devuelva solo los datos que queramos y no todo el objeto. Empecemos por esto ya que es lo más sencillo.
Para esto simplemente tenemos que indicarlo al hacer el find, probemos por ejemplo a traernos solo el campo money de Black Panther
db.heros.find({name: "Black Panther"},{money:1})
Como véis solo le pasamos el campo y con 1 indicamos si queremos ese campo, si le pusieramos un 0 sería que no queremos ese, si no el resto.
Con el 1 tendríamos este resultado
Con el 0
Entonces como hemos dicho si no quisiéramos ver el campo _id
db.heros.find({name: "Black Panther"},{money:1,_id:0})
Ahora supongamos que queremos ordenar la salida. Para ello vamos a quitar el filtro de nombre, dejar que nos muestre name y money pero quitando el campo _id y ordenando por money
db.heros.find({},{_id:0,money:1,name:1}).sort({money:1})
Esa sería con money de forma ascendente si lo queremos descendente
db.heros.find({},{_id:0,money:1,name:1}).sort({money:-1})
Perfecto, ya hemos visto como hacer querys más específicas, ahora veamos como hacen las querys mongo por dentro y como podríamos mejorarlas.
Para obtener información sobre una query tenemos el método explain(). Probemoslo por ejemplo con el último comando
db.heros.find({},{_id:0,money:1,name:1}).sort({money:-1}).explain()
Este comando nos devolvería algo como esto
Como podéis ver nos proporciona información en cuanto a la configuración de la query, como que hacemos un SORT y nos indica el patrón
"stage" : "SORT",
"sortPattern" : {
"money" : -1
},
O por ejemplo en que forma genera el sort y en que dirección
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "COLLSCAN",
"direction" : "forward"
}
}
Aquí tenemos que fijarnos en COLLSCAN, esto nos indica que se esta recorriendo toda la colección y luego la ordena (lo mismo nos saldría en una búsqueda sin sort), un poco más abajo veremos como cambia este campo.
Bien tenemos información pero realmente sería ideal poder ver algunas estadísticas de esto como de tiempo por ejemplo...no hay problema solo tenemos que pedirselo a explain
db.heros.find({},{_id:0,money:1,name:1}).explain("executionStats")
Vemos como además de lo que nos mostraba antes tenemos estadísticas
Por ejemplo tenemos executionTimeMillis que nos indica el tiempo que ha tardado la query.....en este caso como tenemos pocos datos no tarda nada, pero ahora os voy a mostrar un ejemplo que contiene unos 1000000 elementos y estas serían sus estadísticas
Fijáos que ha tardado 421 milisegundos y que el stage sigue siendo COLLSCAN. Para mí esta query tardaría mucho, por lo que yo quiero optimizar esta query.....y ¿qué podemos hacer para esto? Antes hemos dicho que al poner COLLSCAN lo que hace es recorrerse toda la colección y luego filtra fijáos en la imagen que pone totalDocsExamined: 1000000, para optimizar esto podemos crear indices. Esto ya comentamos como era, con createIndex con el campo y el orden
db.heros.createIndex({money:1})
Veamos ahora que pone en nuestro explain con nuestra query anterior de sort
db.heros.find({},{_id:0,money:1,name:1}).sort({money:-1}).explain()
Si os fijáis ahora ha cambiado nuestro stage: IXSCAN, esto nos indica que esta buscando en el índice que hemos creado. No tiene sentido ver el executionStats en nuestro ejemplo porque tenemos pocos documentos, pero ahora os muesto una imagen real del mismo ejemplo de antes de 1000000 de elementos pero con un indice
Si os fijáis lo primero que vemos es que executionTimeMillis es 0 y lo segundo que totalDocsExamined son 90, es decir que en el index existen 90 documentos que cumplen con la query que le hemos pasado y nos los ha devuelto directamente.
Aquí podemos ver como realmente los índices tienen una función real y realmente útil. Tenemos que tener cuidado con los índices porque puede parecer que lo mejor es hacer índices de todo, pero realmente no son totalmente gratis, estos ocupan espacio de almacenamiento y el índice se tiene que ir actualizando junto con la base de datos por lo que aumentamos el tiempo de procesamiento a la hora de hacer inserciones en la base de datos, todo esto lo tenemos que tener en cuenta a la hora de crear indices siendo lo ideal planear bien cuales serán nuestras querys más usadas o las más importantes antes de ponernos a crear índices como locos.
Por el momento esto es todo sobre las Queries $ Performance más adelante veremos alguna cosa más pero creo que con esto cubrimos casi todos los escenarios que podemos necesitar en cuanto a queries ser refiere.
En el siguiente post veremos algunas cosas sobre el scheme, como almacena los datos Mongo y varias cosas más...nos vemos en el siguienteeeee un abrazoooorrr





















