MongoDb índices: campos de tipo array

MongoDb nos da mucha flexibilidad y potencia a la hora de modelar documentos, una de las ventajas que nos ofrece es que un documento puede tener un campo de tipo array y podemos realizar búsquedas sobre él. Esta característica tan útil nos puede llevar en escenarios exigentes a tener problemas de rendimiento: para hacer un filtrado por dichos campos tengo que recorrer la colección y además recorrer el array de cada documento para buscar los datos concretos, para solucionar esto MongoDB nos permite crear índices tanto en campos array que tienen tipos simple (por ejemplo, un string) así como campos array que contienen objetos.

Manos a la obra

Esta vez vamos a trabajar con el set de datos de ejemplos de Airbnb. En nuestro caso nos hemos traído a nuestro servidor local esta información y la hemos restaurado en una base de datos, por si queréis hacer lo mismo, aquí la tenéis: (enlace al ):

Si le echamos un vistazo, podemos ver que hay un campo que puede ser interesante para lanzar consultas, el de amenities es decir: qué servicios tenemos disponibles para ese apartamento (que si wifi, que si secador de pelo, toallas...), imagínate que queremos sacar en una consulta cuantos apartamentos permiten llevar mascota, para ello vamos a montar la siguiente query en el tab explain plan de Mongo Compass

listado de servicios de un hotel array de strings

db.listingsAndReviews.find({ amenities: "Pets allowed" });

Si la ejecutamos, el resultado que obtenemos no es muy bueno, ha tenido que hacer un colscan (es decir recorrer los 5555 documentos de la colección) para devolver 642.

Vamos a crear un índice sobre ese campo array:

use airbnb
db.listingsAndReviews.createIndex({ amenities: 1 });

Y ejecutamos de nuevo la consulta

db.listingsAndReviews.find({ amenities: { $in: ["Pets allowed"] } });

Cómo puedes ver, pasamos a usar el índice que hemos creado y sólo hemos tenido que recorrer los 642 documentos para obtener los resultados esperados.

Esto está muy bien para campos array simples, el de amenities tiene por ejemplo un string, ¿Pero qué pasa con campos de tipo objeto? Aquí la estrategia es indexar por campos de ese objeto, por ejemplo tenemos el campo reviews que contiene a su vez varias entradas con información acerca de una opinión sobre un alquiler:

Objeto reviews, podemos ver quien hizo la reseña

¿Qué pasa si por ejemplo queremos ver las reviews que se han hecho a partir de una fecha dada? Tendremos la siguiente consulta

db.listingsAndReviews.find({
  "reviews.date": { $gte: new Date("2019-03-03") },
});

De momento tenemos el mismo problema: colscan recorriendo la colección completa.

Vamos ahora a crear un índice sobre el campo reviews.date

db.listingsAndReviews.createIndex({ "reviews.date": 1 });

Repetimos la consulta, y ahora tiramos del índice y recorremos muchos menos documentos para devolver el resultado.

¿Con ganas de aprender Backend?

En Lemoncode impartimos un Bootcamp Backend Online, centrado en stack node y stack .net, en él encontrarás todos los recursos necesarios: clases de los mejores profesionales del sector, tutorías en cuanto las necesites y ejercicios para desarrollar lo aprendido en los distintos módulos. Si quieres saber más puedes pinchar aquí para más información sobre este Bootcamp Backend.