En la era digital actual, comprender cómo manejar eficientemente las bases de datos documentales es esencial para garantizar el rendimiento y la escalabilidad de las aplicaciones. Uno de los aspectos clave es la gestión de las relaciones entre documentos, en particular la relación uno a uno. Aquí te presento una guía para entender mejor cómo y cuándo aplicar relaciones uno a uno de forma referenciada en bases de datos documentales.
¿Qué es una relación uno a uno y cómo se implementa de forma embebida?
Una relación uno a uno en bases de datos se da cuando un documento está relacionado con otro documento de manera exclusiva. Tradicionalmente, esto se maneja embebiendo la información del segundo documento directamente dentro del primero. Este método es muy eficiente y es recomendado como mejor práctica en la mayoría de los casos, especialmente cuando tratamos con bases de datos documentales, donde el rendimiento de lectura es prioritario.
¿Cuándo es preferible una relación uno a uno referenciada?
Aunque la embebida es la forma estándar, existen situaciones en las que una relación uno a uno referenciada es más adecuada:
Superación de restricciones de peso de documento: Cuando un documento alcanza o se acerca al límite de tamaño establecido por el sistema de gestión de base de datos (por ejemplo, 16MB en MongoDB).
Desempeño y tiempo de consulta: Al crecer el documento con información embebida, las consultas pueden volverse lentas. Separando la información en documentos diferentes se mejora la velocidad de acceso.
Ejemplo práctico: una tienda y sus detalles
Tomemos el caso de una tienda que necesita almacenar información básica, como el nombre y la ubicación, y detalles más específicos como descripciones o datos del gerente. En este caso, podríamos tener dos colecciones: stores para información básica y store_details para los detalles extendidos, conectados a través de un store_id común.
¿Cómo conectar las colecciones con relaciones referenciadas?
Para conectar documentos de diferentes colecciones mediante una relación uno a uno referenciada, se utiliza un identificador común que sirve como referencia entre ellos. Veamos cómo hacerlo:
Creando las colecciones y documentos
Insertar documentación básica de la tienda:
{"store_id":"ST001","name":"Nico Store Program","address":"Cargo 87"}
Cuando ambos documentos comparten el mismo store_id, ahora están conectados referencialmente, pero no físicamente. Para realizar consultas que junten esta información es necesario utilizar técnicas especiales como el Aggregation Framework.
¿Cómo se realiza una consulta combinada con el Aggregation Framework?
El Aggregation Framework de MongoDB permite realizar operaciones complejas sobre los datos, como combinar documentos de distintas colecciones. El operador $lookup es especialmente útil aquí ya que permite "unir" documentos de manera similar a cómo se haría en bases de datos relacionales.
Esta consulta devuelve un documento que contiene tanto la información básica de la tienda como sus detalles, todo en una única respuesta.
Manejar relaciones uno a uno de forma referenciada puede ser un poco más complejo y requerir más consultas, pero es una solución necesaria cuando se trata de optimizar el rendimiento y evitar superar limitaciones de tamaño. Recuerda que cada caso es único y debe ser evaluado para determinar la mejor estrategia de modelado de datos. ¡Continúa explorando y aplicando estos conocimientos para destacar en el mundo del manejo de bases de datos!
En el 90% de los casos, cuando hay una relación 1:1, esta suele estar EMBEBIDA.
PERO: Uno de los casos donde se usa la relación REFERENCIADA, es cuando el documento ya es muy grande, cerca del límite de 16 mb y mejor se saca información para mejorar el rendimiento.
También se pueden usar combinados, si usas el ObjectId, para referenciar el total de datos y agregar los 2 o 3 campos de los más comunes que se usan juntas para evitar la consulta a la 2da colección.
Agregando otro bloque al aggregate podemos (en este caso) obtener el detalle como un objeto en vez de un arreglo.
Este se usa cuando se superan las restricciones, como pueden ser:
Peso maximo de 16MB: Esto podria pasar si se llena con muchas propiedades y/o muy pesadas, como pueden ser binarios, imagenes, videos, etc.
Consultas lentas: Al realizar una peticion, puede que se procese informacion innecesaria (Se puede superar con restringir las propiedades que se entregaran) o muy pesada.
Se requieran acelerar ciertas consultas: Puede que se requieran hacer consultas que requieran mucha velocidad para grandes cantidad de documentos y dejemos apartado los detalles que no se requiren para esas consultas. Como puede ser en productos y el detalle de esos productos.
Ejemplo
Insertar
db.stores.insertOne({storeId:"ST001",// permitira la relacionname:"PamplonaStore",address:"Calle falsa 123",city:"Sprinfield",state:"Londres",zip:"1234"})db.stores_details.insertOne({storeId:"ST001",// repetimos el storeID, ya que es 1-1description:"PamplonaStore asdasdasd",services:["Ropa","Deportivo"],manager:{name:"Mario",cellphone:"123412412"}})
Consultar
// Se puede consultar por separadodb.stores.findOne({// consultar la tiendastoreId:"ST001"})db.stores_details.findOne({// consultar los detalles de tiendastoreId:"ST001"})// Oooo.. Usar el agregation framework lookup
Agregation framework lookup
Permite consultar otros documentos y formar una relacion.
PD: Si no estan los campos puede que se agreguen datos no esperados. Ya que funciona como: si estan los datos en los documentos correspondientes se comprueba, si faltan ambos no funciona y agrega elementos no esperados. Puede que este detectando cuando faltan propiedades como valores nulos que compara. Parece ser esto ultimo. Esto puede retornar matrices 2d de los que encuentre nulos en ambos colecciones.
// Consultadb.stores.aggregate([// desde stores, usamos el aggregate{$lookup:{// parte desde storesfrom:'stores_details',// agregamos cual coleccion queremos conectarlocalField:'storeId',// que campo los enlaza, en storesforeignField:'storeId',// que campo los enlaza, en stores_detailsas:'detail'// donde dejara la relacion}}])// Respuesta[{"_id":{"$oid":"64966b333b1e45e51454558d"},"storeId":"ST001","name":"PamplonaStore","address":"Calle falsa 123","city":"Sprinfield","state":"Londres","zip":"1234","detail":[// Desde aqui se agrega el detalle, como un array{"_id":{"$oid":"64966b343b1e45e51454558e"},"storeId":"ST001","description":"PamplonaStore asdasdasd","services":["Ropa","Deportivo"],"manager":{"name":"Mario","cellphone":"123412412"}}]}]
Cuando alcanzamos el limite de almacenamiento de documentos podemos usar la tecnica de referencia, o tambien cuando las consultas ya se vuelven lentas. Esto teniendo en cuenta que es de una relacion - entidad 1-1
Cómo se haría en caso de que el ID sea generado por el propio mongo?
Con objectID?
En mi caso tenia una rutina en el servicio:
Crear Doc1 (el return de la creacion de trae el objectID)
Crear Doc2 y en el dato que referencia 1:1 utiliza el objectID que capturaste en el paso 1 (esta creacion te devuelve el objectID del Doc2)
Actualizas Doc1 con la relacion 1:1 usando el objectID que capturaste en el paso 2