Lecciones de un año con MongoDB
En algún momento, todas las desarrolladoras Back-end interactuaremos con Bases de datos NoSQL, sea Redis, Dynamo, ElasticSearch o MongoDB, cada una con usos específicos.
Revisando MongoDB, es un servicio enfocado en documentos, estructuras parecidas al formato JSON (javascript object notation), en el caso de MongoDB varía al ser BSON (Binary JSON).
Cuando agrupamos estos documentos, creamos las colecciones.
Podemos guardar dentro de los documentos muchos tipos de valores, entre ellos: Numéricos, Cadenas de caracteres, Objetos, Arrays de cualquier tipo y también nos permite guardar funciones Javascript 🤯.
Sabiendo esto, quiero contarte algunas lecciones que aprendí en el camino:
🤷🏼♀️¿Cuándo Usarla?
Mongo DB al ser una base de datos “schemaless” permite que cada documento tenga campos independientes, no necesitas establecer cual será la estructura, esta es una ventaja y desventaja en cada caso.
Al no tener una estructura definida, el peso de mantener la integridad caerá en gran medida sobre el equipo de desarrollo y esto permite que nuestros cambios en ambientes productivos sean más agiles.
Si bien es una base de datos schemaless, es nuestro deber decidir según nuestras necesidades, si normalizar o de-normalizar nuestros datos. Podemos tener las entidades hijas dentro de la entidad principal, o dividirlas en diferentes colecciones.
También es Flexible y Ágil para escalar, tenemos dentro de las opciones, la escalabilidad vertical agregando más hardware a nuestra instancia (RAM/CPU) o escalar horizontalmente, agregando más nodos a la base de datos para responder mejor a nuestros clientes.
En resumen, úsala cuando:
- Te enfrentes a un entorno cambiante en la lógica de negocio en la que necesites agilidad al agregar/eliminar campos y estructuras.
- Necesites respuestas rápidas para consultas sencillas en una colección de millones de datos.
- Necesites escalar rápidamente, agregando más nodos distribuidos en diferentes regiones para responder a tus clientes.
📄¿Cómo manejar los datos?
Cada documento dentro de una colección siempre manejará un ID único, está comúnmente ubicado en el campo _id
y esta es una buena opción para usarla como identificadores en nuestra aplicación.
Este ID está compuesto por un hash único de 12 bytes distribuido en:
- 4-bytes para el timestamp.
- 5-bytes de un valor aleatorio.
- 3-bytes reservados para un contador incremental en la colección.
{
"_id:" ObjectId("507f1f77bcf86cd799439011")
...
}
🤝🏼Manejo de relaciones:
Teniendo en cuenta que esta no es una base de datos relacional, podemos manejar todas las sub-entidades dentro de un mismo documento, evitando usar joins para conocer la información que esta estrechamente integrada.
// Colección: Publicaciones{
"_id:" ObjectId("507f1f77bcf86cd799439011"),
"titulo": "Nueva publicación",
"descripción": "Probando nuevas funcionalidades",
"calificación": 5.6,
"comentarios": ["Hola", "Nuevo comentario"]
}
Como ves, lo que era antes una relación entre la tabla publicaciones y la tabla comentarios usando un ID entre ellos, ahora es simplemente un nuevo campo en la entidad principal (publicaciones) que contiene una de sus relaciones.
Por esto, una buena practica antes de construir nuestras colecciones y documentos es pensar en cuales serán las búsquedas más comunes y así tener esas relaciones dentro de nuestro documento principal.
Otra opción si necesitamos (en el caso de arriba) saber qué usuario creó el comentario y algunos datos extras, podemos crear un Array de objetos y así tener más detalles de los datos relacionados:
// Colección: Publicaciones{
"_id:" ObjectId("507f1f77bcf86cd799439011"),
"titulo": "Nueva publicación",
"descripción": "Probando nuevas funcionalidades",
"calificación": 5.6,
"comentarios": [{
texto: "Hola! un comentario",
fechaDeCreacion: ISODate("2020-11-12T23:00:00.032"),
creadoPor: ObjectId("507f1f77bcf86cd799439011")
}, {
texto: "Hola! Soy otro comentario",
fechaDeCreacion: ISODate("2020-11-13T23:00:00.032"),
creadoPor: ObjectId("507f1f77bcf86cd7994457855")
}]
}
Ahora, nuestro array contiene sub-documentos, llamados nested-objects, estos nos permiten agrupar más información dentro de nuestra entidad principal y hacer búsquedas directamente en estos arrays.
Si bien, no está pensado para esto al ser NoSQL, también es posible hacer JOINS, MongoDB nos ofrece la oportunidad de mezclar datos de diferentes colecciones usando las agregaciones.
🔍Búsquedas eficientes:
No pensemos en calcular datos después de recibir un resultado, la idea es ofrecer rapidez al encontrar los datos y una forma para esto, es tener siempre guardados los valores que necesitamos, ya sean contadores, status de nuestra entidad y más.
Otro de nuestros aliados al crear búsquedas son los índices, nos ayudan a acelerar nuestros queries en grandes cantidades de datos, con los índices podemos pasar de 900ms de búsqueda a 120ms en muchos casos.
Existen muchos tipos de índices, únicos, compuestos y de texto. Utilízalos para crear uno por cada búsqueda que tengas en tus aplicaciones, esto ayudará a MongoDB a encontrar más rápido los documentos.
Si no existe el índice, el motor de la DB recorrerá documento por documento hasta encontrar el que buscas. 🙂🔥
Estos índices no solo ayudan a optimizar las búsquedas sino, la mayoría de operaciones de ordenamiento y agrupamiento. Sin los índices obligamos a MongoDB a mantener el scan a toda la DB en memoria y causar malos tiempos.
En resumen para tus búsquedas:
- Utiliza los índices para mejorar tus tiempos, crea un índice por cada find() recurrente que tengas y ayuda con esto a la DB.
- Los índices también ayudarán a las operaciones de agrupamiento y ordenamiento.
- Agrupa tus relaciones en el documento principal y evita usar JOINS para traer la data que necesitas.
Gracias por leer, espero te sirva para iniciar en MongoDB ágilmente 🤘🏼🤘🏼
Si crees que existen más formas de optimizar las búsquedas y las estructuras de datos, deja un comentario, seguro ayudarán!