El autor seleccionó el Fondo de Internet Abierto/Libertad de Expresión para recibir una donación como parte del programa Write for DOnations.
Introducción
Si tienes mucha experiencia trabajando con bases de datos relacionales, puede ser difícil superar los principios del modelo relacional, como pensar en términos de tablas y relaciones. Bases de datos orientadas a documentos como MongoDB permiten romper con la rigidez y las limitaciones del modelo relacional. Sin embargo, la flexibilidad y la libertad que conlleva poder almacenar documentos autodescriptivos en la base de datos puede llevar a otros escollos y dificultades.
Este artículo conceptual describe cinco pautas comunes relacionadas con el diseño del esquema en una base de datos orientada a documentos y destaca varias consideraciones que se deben tener en cuenta al modelar las relaciones entre los datos. También se recorrerán varias estrategias que se pueden emplear para modelar dichas relaciones, incluyendo la inclusión de documentos dentro de matrices y el uso de referencias de hijos y padres, así como cuándo sería más apropiado utilizar estas estrategias.
Directriz 1 — Almacenar Juntos lo que Debe Accederse Juntos
En una base de datos relacional típica, los datos se guardan en tablas, y cada tabla se construye con una lista fija de columnas que representan varios atributos que conforman una entidad, objeto o evento. Por ejemplo, en una tabla que representa estudiantes en una universidad, podrías encontrar columnas que contienen el nombre, apellido, fecha de nacimiento y un número de identificación único de cada estudiante.
Normalmente, cada tabla representa un solo tema. Si quisieras almacenar información sobre los estudios actuales de un estudiante, sus becas o su educación anterior, podría tener sentido mantener esos datos en una tabla separada de la que contiene su información personal. Luego, podrías conectar estas tablas para indicar que existe una relación entre los datos en cada una, señalando que la información que contienen tiene una conexión significativa.
Por ejemplo, una tabla que describe el estado de beca de cada estudiante podría referirse a los estudiantes por su número de identificación estudiantil, pero no almacenaría directamente el nombre o la dirección del estudiante, evitando así la duplicación de datos. En tal caso, para recuperar información sobre cualquier estudiante con todos los datos de las cuentas de redes sociales del estudiante, la educación previa y las becas, sería necesario acceder a más de una tabla a la vez y luego compilar los resultados de diferentes tablas en uno.
Este método de describir relaciones a través de referencias se conoce como un modelo de datos normalizado. Almacenar datos de esta manera, utilizando múltiples objetos separados y concisos relacionados entre sí, también es posible en bases de datos orientadas a documentos. Sin embargo, la flexibilidad del modelo de documento y la libertad que ofrece para almacenar documentos y matrices incrustados dentro de un solo documento significa que puedes modelar datos de manera diferente a como lo harías en una base de datos relacional.
El concepto subyacente para modelar datos en una base de datos orientada a documentos es “almacenar juntos lo que se accederá juntos”. Al profundizar más en el ejemplo del estudiante, digamos que la mayoría de los estudiantes en esta escuela tienen más de una dirección de correo electrónico. Debido a esto, la universidad desea la capacidad de almacenar múltiples direcciones de correo electrónico con la información de contacto de cada estudiante.
En un caso como este, un documento de ejemplo podría tener una estructura como la siguiente:
Observa que este documento de ejemplo contiene una lista incrustada de direcciones de correo electrónico.
Representar más de un solo tema dentro de un solo documento caracteriza un modelo de datos denormalizado. Permite a las aplicaciones recuperar y manipular todos los datos relevantes para un objeto determinado (aquí, un estudiante) de una sola vez, sin necesidad de acceder a múltiples objetos y colecciones separadas. Hacerlo también garantiza la atomicidad de las operaciones en dicho documento sin tener que utilizar transacciones multi-documento para garantizar la integridad.
Almacenar juntos los datos que necesitan ser accedidos juntos utilizando documentos embebidos suele ser la forma óptima de representar datos en una base de datos orientada a documentos. En las siguientes pautas, aprenderás cómo se pueden modelar de la mejor manera las diferentes relaciones entre objetos, como las relaciones uno a uno o uno a muchos, en una base de datos orientada a documentos.
Pauta 2 — Modelado de Relaciones Uno a Uno con Documentos Embebidos
Una relación uno a uno representa una asociación entre dos objetos distintos donde un objeto está conectado con exactamente uno de otro tipo.
Continuando con el ejemplo del estudiante de la sección anterior, cada estudiante tiene solo una tarjeta de identificación de estudiante válida en cualquier momento dado. Una tarjeta nunca pertenece a múltiples estudiantes, y ningún estudiante puede tener múltiples tarjetas de identificación. Si tuvieras que almacenar todos estos datos en una base de datos relacional, probablemente tendría sentido modelar la relación entre estudiantes y sus tarjetas de identificación almacenando los registros de estudiantes y los registros de tarjetas de identificación en tablas separadas que están unidas a través de referencias.
Un método común para representar tales relaciones en una base de datos de documentos es mediante el uso de documentos embebidos. Como ejemplo, el siguiente documento describe a un estudiante llamado Sammy y su tarjeta de identificación de estudiante:
Observa que en lugar de un solo valor, el campo id_card
de este documento de ejemplo contiene un documento embebido que representa la tarjeta de identificación del estudiante, descrita por un número de ID, la fecha de emisión de la tarjeta y la fecha de expiración de la tarjeta. La tarjeta de identificación se convierte esencialmente en parte del documento que describe al estudiante Sammy, aunque es un objeto separado en la vida real. Por lo general, estructurar el esquema del documento de esta manera para que puedas recuperar toda la información relacionada a través de una sola consulta es una elección sólida.
Las cosas se vuelven menos directas si encuentras relaciones que conectan un objeto de un tipo con muchos objetos de otro tipo, como las direcciones de correo electrónico de un estudiante, los cursos que asiste o los mensajes que publica en el tablón de mensajes del consejo estudiantil. En las próximas pautas, utilizarás estos ejemplos de datos para aprender diferentes enfoques para trabajar con relaciones uno a muchos y muchos a muchos.
Directriz 3 — Modelado de Relaciones Uno a Pocos con Documentos Embebidos
Cuando un objeto de un tipo está relacionado con múltiples objetos de otro tipo, se puede describir como una relación uno a muchos. Un estudiante puede tener múltiples direcciones de correo electrónico, un automóvil puede tener numerosas partes o un pedido de compra puede constar de varios artículos. Cada uno de estos ejemplos representa una relación uno a muchos.
Si bien la forma más común de representar una relación uno a uno en una base de datos de documentos es a través de un documento embebido, existen varias formas de modelar relaciones uno a muchos en un esquema de documentos. Al considerar sus opciones para modelar estos, sin embargo, hay tres propiedades de la relación dada que debe considerar:
- Cardinalidad: La cardinalidad es la medida del número de elementos individuales en un conjunto dado. Por ejemplo, si una clase tiene 30 estudiantes, podría decirse que esa clase tiene una cardinalidad de 30. En una relación uno a muchos, la cardinalidad puede ser diferente en cada caso. Un estudiante podría tener una dirección de correo electrónico o múltiples. Podrían estar inscritos en solo unas pocas clases o podrían tener un horario completamente lleno. En una relación uno a muchos, el tamaño de “muchos” afectará cómo podría modelar los datos.
- Acceso independiente: Algunos datos relacionados rara vez, si acaso, se acceden de forma separada del objeto principal. Por ejemplo, podría ser poco común recuperar la dirección de correo electrónico de un solo estudiante sin otros detalles del estudiante. Por otro lado, los cursos de una universidad podrían necesitar ser accedidos y actualizados individualmente, independientemente del estudiante o estudiantes que estén registrados para asistir a ellos. Si alguna vez accederás a un documento relacionado de forma aislada también afectará cómo podrías modelar los datos.
- Si la relación entre los datos es estrictamente una relación uno a muchos: Considera los cursos que un estudiante ejemplo asiste en una universidad. Desde la perspectiva del estudiante, puede participar en múltiples cursos. A primera vista, esto puede parecer una relación uno a muchos. Sin embargo, los cursos universitarios rara vez son asistidos por un solo estudiante; más comúnmente, múltiples estudiantes asistirán a la misma clase. En casos como este, la relación en cuestión no es realmente una relación uno a muchos, sino una relación muchos a muchos, y por lo tanto tomarías un enfoque diferente para modelar esta relación que el que tomarías para una relación uno a muchos.
Imagina que estás decidiendo cómo almacenar las direcciones de correo electrónico de los estudiantes. Cada estudiante puede tener múltiples direcciones de correo electrónico, como una para el trabajo, una para uso personal y una proporcionada por la universidad. Un documento que representa una sola dirección de correo electrónico podría tomar una forma similar a esta:
En términos de cardinalidad, solo habrá unos pocos correos electrónicos por estudiante, ya que es poco probable que un estudiante tenga docenas, y mucho menos cientos, de direcciones de correo electrónico. Por lo tanto, esta relación puede caracterizarse como una relación uno-a-pocos, lo que es una razón convincente para incrustar direcciones de correo electrónico directamente en el documento del estudiante y almacenarlos juntos. No corre ningún riesgo de que la lista de direcciones de correo electrónico crezca indefinidamente, lo que haría que el documento fuera grande e ineficiente de usar.
Nota: Tenga en cuenta que existen ciertos inconvenientes asociados con el almacenamiento de datos en matrices. Por ejemplo, un documento de MongoDB no puede exceder los 16MB de tamaño. Si bien es posible y común incrustar varios documentos utilizando campos de matriz, si la lista de objetos crece de manera incontrolable, el documento podría alcanzar rápidamente este límite de tamaño. Además, almacenar una gran cantidad de datos dentro de matrices incrustadas tiene un gran impacto en el rendimiento de las consultas.
Incrustar múltiples documentos en un campo de matriz probablemente sea adecuado en muchas situaciones, pero también debe saber que no siempre puede ser la mejor solución.
En cuanto al acceso independiente, es probable que las direcciones de correo electrónico no se accedan de forma independiente del estudiante. Por lo tanto, no hay un incentivo claro para almacenarlas como documentos separados en una colección separada. Esta es otra razón convincente para incrustarlas dentro del documento del estudiante.
Lo último que hay que considerar es si esta relación es realmente una relación uno a muchos en lugar de una relación muchos a muchos. Debido a que una dirección de correo electrónico pertenece a una sola persona, es razonable describir esta relación como una relación uno a muchos (o, quizás de manera más precisa, una relación uno a pocos) en lugar de una relación muchos a muchos.
Estas tres suposiciones sugieren que sería una buena opción almacenar las diversas direcciones de correo electrónico de los estudiantes dentro de los mismos documentos que describen a los estudiantes en sí. Un documento de muestra de un estudiante con direcciones de correo electrónico incrustadas podría tener esta forma:
Utilizando esta estructura, cada vez que recuperes un documento de un estudiante, también recuperarás las direcciones de correo electrónico incrustadas en la misma operación de lectura.
Si modelas una relación del tipo uno a pocos, donde los documentos relacionados no necesitan ser accedidos de forma independiente, incrustar documentos directamente como este suele ser deseable, ya que puede reducir la complejidad del esquema.
Como se mencionó anteriormente, sin embargo, incrustar documentos de esta manera no siempre es la solución óptima. La siguiente sección proporciona más detalles sobre por qué esto podría ser el caso en algunos escenarios, y describe cómo utilizar referencias a hijos como una forma alternativa de representar relaciones en una base de datos de documentos.
Directriz 4 — Modelado de Relaciones Uno a Muchos y Muchos a Muchos con Referencias Hijas
La naturaleza de la relación entre estudiantes y sus direcciones de correo electrónico informó cómo esa relación podría modelarse mejor en una base de datos de documentos. Hay algunas diferencias entre esto y la relación entre estudiantes y los cursos que asisten, por lo que la forma en que modelas las relaciones entre estudiantes y sus cursos también será diferente.
Un documento que describe un solo curso al que asiste un estudiante podría seguir una estructura como esta:
Digamos que decidiste desde el principio utilizar documentos embebidos para almacenar información sobre los cursos de cada estudiante, como en este ejemplo:
Esto sería un documento de MongoDB perfectamente válido y podría servir muy bien el propósito, pero considera las tres propiedades de relación que aprendiste en la directriz anterior.
La primera es la cardinalidad. Un estudiante probablemente solo mantendrá unas pocas direcciones de correo electrónico, pero puede asistir a múltiples cursos durante su estudio. Después de varios años de asistencia, podría haber docenas de cursos en los que el estudiante participó. Además, asistirían a estos cursos junto con muchos otros estudiantes que también asisten a su propio conjunto de cursos a lo largo de sus años de asistencia.
Si decidieras incrustar cada curso como en el ejemplo anterior, el documento del estudiante se volvería rápidamente engorroso. Con una cardinalidad más alta, el enfoque del documento incrustado se vuelve menos atractivo.
La segunda consideración es el acceso independiente. A diferencia de las direcciones de correo electrónico, es razonable suponer que habría casos en los que se necesitaría recuperar información sobre los cursos universitarios por separado. Por ejemplo, supongamos que alguien necesita información sobre los cursos disponibles para preparar un folleto de marketing. Además, es probable que los cursos necesiten ser actualizados con el tiempo: el profesor que imparte el curso podría cambiar, su horario podría fluctuar o sus prerrequisitos podrían necesitar ser actualizados.
Si almacenaras los cursos como documentos incrustados dentro de los documentos de los estudiantes, recuperar la lista de todos los cursos ofrecidos por la universidad se volvería problemático. Además, cada vez que un curso necesite una actualización, tendrías que revisar todos los registros de estudiantes y actualizar la información del curso en todos los lugares. Ambas son buenas razones para almacenar los cursos por separado y no incrustarlos por completo.
La tercera cosa a considerar es si la relación entre el estudiante y un curso universitario es realmente uno a muchos o en cambio muchos a muchos. En este caso, es lo último, ya que más de un estudiante puede asistir a cada curso. La cardinalidad y los aspectos de acceso independiente de esta relación sugieren en contra de incrustar cada documento de curso, principalmente por razones prácticas como la facilidad de acceso y actualización. Considerando la naturaleza muchos a muchos de la relación entre cursos y estudiantes, podría tener sentido almacenar los documentos de los cursos en una colección separada con identificadores únicos propios.
Los documentos que representan clases en esta colección separada podrían tener una estructura como estos ejemplos:
Si decides almacenar información del curso de esta manera, necesitarás encontrar una forma de conectar a los estudiantes con estos cursos para que sepas qué estudiantes asisten a qué cursos. En casos como este, donde el número de objetos relacionados no es excesivamente grande, especialmente con relaciones muchos a muchos, una forma común de hacerlo es usar referencias hijas.
Con referencias hijas, el documento de un estudiante hará referencia a los identificadores de los objetos de los cursos a los que el estudiante asiste en un arreglo embebido, como en este ejemplo:
Observa que este documento de ejemplo aún tiene un campo courses
que también es un arreglo, pero en lugar de embeber documentos completos de cursos como en el ejemplo anterior, solo se embeben los identificadores que referencian los documentos de cursos en la colección separada. Ahora, al recuperar un documento de estudiante, los cursos no estarán disponibles de inmediato y necesitarán ser consultados por separado. Por otro lado, se sabe de inmediato qué cursos recuperar. Además, en caso de que se necesite actualizar algún detalle de un curso, solo el documento del curso en sí necesita ser alterado. Todas las referencias entre estudiantes y sus cursos seguirán siendo válidas.
Nota: No hay una regla fija para cuando la cardinalidad de una relación es demasiado grande para embeber referencias hijas de esta manera. Podrías elegir un enfoque diferente en una cardinalidad menor o mayor si es lo que mejor se adapta a la aplicación en cuestión. Después de todo, siempre querrás estructurar tus datos para adaptarse a la manera en que tu aplicación los consulta y actualiza.
Si modelas una relación uno a muchos donde la cantidad de documentos relacionados está dentro de límites razonables y los documentos relacionados necesitan ser accedidos de manera independiente, es preferible almacenar los documentos relacionados por separado y utilizar referencias a hijos para conectarlos.
Ahora que has aprendido cómo usar referencias a hijos para señalar relaciones entre diferentes tipos de datos, esta guía describirá un concepto inverso: referencias a padres.
Directriz 5 — Modelado de Relaciones Uno a Muchos Ilimitadas con Referencias a Padres
El uso de referencias a hijos funciona bien cuando hay demasiados objetos relacionados para incrustarlos directamente dentro del documento padre, pero la cantidad sigue estando dentro de límites conocidos. Sin embargo, hay casos en los que el número de documentos asociados podría ser ilimitado y continuar creciendo con el tiempo.
Como ejemplo, imagina que el consejo estudiantil de la universidad tiene un tablón de mensajes donde cualquier estudiante puede publicar lo que desee, incluyendo preguntas sobre cursos, historias de viajes, ofertas de empleo, materiales de estudio, o simplemente charlas libres. Un mensaje de ejemplo en este escenario consiste en un asunto y un cuerpo del mensaje:
Podrías utilizar cualquiera de los dos enfoques discutidos previamente — incrustación y referencias a hijos — para modelar esta relación. Si decidieras optar por la incrustación, el documento del estudiante podría adoptar una forma similar a esta:
Sin embargo, si un estudiante es prolífico escribiendo mensajes, su documento se volverá increíblemente largo rápidamente y podría fácilmente exceder el límite de tamaño de 16MB, por lo que la cardinalidad de esta relación sugiere en contra de la inserción. Además, los mensajes podrían necesitar ser accedidos separadamente del estudiante, como podría ser el caso si la página del tablón de mensajes está diseñada para mostrar los mensajes más recientes publicados por los estudiantes. Esto también sugiere que la inserción no es la mejor opción para este escenario.
Nota: También deberías considerar si los mensajes del tablón de mensajes son accedidos con frecuencia al recuperar el documento del estudiante. Si no es así, tenerlos todos insertados dentro de ese documento incurriría en una penalización de rendimiento al recuperar y manipular este documento, incluso cuando la lista de mensajes no se usaría con frecuencia. El acceso infrecuente de datos relacionados a menudo es otra señal de que no deberías insertar documentos.
Ahora considera usar referencias a hijos en lugar de insertar documentos completos como en el ejemplo anterior. Los mensajes individuales se almacenarían en una colección separada, y el documento del estudiante podría entonces tener la siguiente estructura:
En este ejemplo, el campo message_board_messages
ahora almacena las referencias hijas a todos los mensajes escritos por Sammy. Sin embargo, cambiar el enfoque solo resuelve uno de los problemas mencionados anteriormente, ya que ahora sería posible acceder a los mensajes de forma independiente. Pero aunque el tamaño del documento del estudiante crecería más lentamente utilizando el enfoque de referencias hijas, la colección de identificadores de objetos también podría volverse engorrosa dada la cardinalidad ilimitada de esta relación. Después de todo, un estudiante podría escribir fácilmente miles de mensajes durante sus cuatro años de estudio.
En tales escenarios, una forma común de conectar un objeto con otro es a través de referencias padres. A diferencia de las referencias hijas descritas anteriormente, ahora no es el documento del estudiante el que se refiere a los mensajes individuales, sino una referencia en el documento del mensaje que apunta hacia el estudiante que lo escribió.
Para usar referencias padres, necesitarías modificar el esquema del documento del mensaje para contener una referencia al estudiante que lo autorizó:
Observa que el nuevo campo posted_by
contiene el identificador de objeto del documento del estudiante. Ahora, el documento del estudiante no contendrá ninguna información sobre los mensajes que ha publicado:
Para recuperar la lista de mensajes escritos por un estudiante, usarías una consulta en la colección de mensajes y filtrarías contra el campo posted_by
. Tenerlos en una colección separada hace que sea seguro dejar que la lista de mensajes crezca sin afectar ninguno de los documentos del estudiante.
Nota: Al utilizar referencias padre, crear un índice en el campo que referencia el documento padre puede mejorar significativamente el rendimiento de las consultas cada vez que filtra por el identificador del documento padre.
Si modela una relación uno a muchos donde la cantidad de documentos relacionados es ilimitada, independientemente de si los documentos necesitan ser accedidos de forma independiente, generalmente se recomienda almacenar los documentos relacionados por separado y usar referencias padre para conectarlos al documento padre.
Conclusión
Gracias a la flexibilidad de las bases de datos orientadas a documentos, determinar la mejor manera de modelar relaciones en una base de datos de documentos es menos una ciencia estricta que en una base de datos relacional. Al leer este artículo, te has familiarizado con la inclusión de documentos y el uso de referencias hijo y padre para almacenar datos relacionados. Has aprendido a considerar la cardinalidad de la relación y a evitar matrices ilimitadas, así como a tener en cuenta si el documento será accedido de forma separada o con frecuencia.
Estas son solo algunas pautas que pueden ayudarte a modelar relaciones típicas en MongoDB, pero modelar el esquema de la base de datos no es algo que tenga una solución universal. Siempre considera tu aplicación y cómo utiliza y actualiza los datos al diseñar el esquema.
Para obtener más información sobre el diseño de esquemas y patrones comunes para almacenar diferentes tipos de datos en MongoDB, te animamos a consultar la documentación oficial de MongoDB sobre ese tema.
Source:
https://www.digitalocean.com/community/tutorials/how-to-design-a-document-schema-in-mongodb