L’auteur a sélectionné le Fonds pour Internet Ouvert/Liberté d’expression pour recevoir un don dans le cadre du programme Écrire pour les Dons.
Introduction
Si vous avez beaucoup d’expérience avec les bases de données relationnelles, il peut être difficile de dépasser les principes du modèle relationnel, tels que la pensée en termes de tables et de relations. Les bases de données orientées documents comme MongoDB permettent de se libérer de la rigidité et des limitations du modèle relationnel. Cependant, la souplesse et la liberté qu’apporte la possibilité de stocker des documents autodescriptifs dans la base de données peuvent conduire à d’autres pièges et difficultés.
Cet article conceptuel décrit cinq directives courantes relatives à la conception de schéma dans une base de données orientée document et souligne diverses considérations à prendre en compte lors de la modélisation des relations entre les données. Il détaillera également plusieurs stratégies qu’on peut mettre en œuvre pour modéliser de telles relations, y compris l’intégration de documents dans des tableaux et l’utilisation de références enfants et parents, ainsi que les moments où ces stratégies seraient les plus appropriées à utiliser.
Ligne directrice 1 — Stocker ensemble ce qui doit être consulté ensemble
Dans une base de données relationnelle typique, les données sont conservées dans des tables, et chaque table est construite avec une liste fixe de colonnes représentant divers attributs qui composent une entité, un objet ou un événement. Par exemple, dans une table représentant les étudiants d’une université, vous pourriez trouver des colonnes contenant le prénom, le nom de famille, la date de naissance et un numéro d’identification unique de chaque étudiant.
Typiquement, chaque table représente un seul sujet. Si vous vouliez stocker des informations sur les études actuelles d’un étudiant, ses bourses d’études ou son éducation antérieure, il pourrait être judicieux de conserver ces données dans une table distincte de celle contenant ses informations personnelles. Vous pourriez ensuite connecter ces tables pour indiquer qu’il existe une relation entre les données de chacune, indiquant que les informations qu’elles contiennent ont une connexion significative.
Par exemple, une table décrivant le statut de bourse de chaque étudiant pourrait se référer aux étudiants par leur numéro d’identification, mais elle ne stockerait pas directement le nom ou l’adresse de l’étudiant, évitant ainsi la duplication des données. Dans un tel cas, pour obtenir des informations sur un étudiant avec toutes les informations sur ses comptes de médias sociaux, son éducation antérieure et ses bourses, une requête devrait accéder à plusieurs tables à la fois, puis compiler les résultats de différentes tables en une seule.
Cette méthode de description des relations par des références est connue sous le nom de modèle de données normalisé. Stocker les données de cette manière — en utilisant plusieurs objets distincts et concis liés les uns aux autres — est également possible dans les bases de données orientées documents. Cependant, la flexibilité du modèle de document et la liberté qu’il offre de stocker des documents intégrés et des tableaux dans un seul document signifient que vous pouvez modéliser les données différemment que vous ne le feriez dans une base de données relationnelle.
Le concept sous-jacent pour modéliser les données dans une base de données orientée document est de « stocker ensemble ce qui sera accédé ensemble ». En creusant davantage l’exemple de l’étudiant, disons que la plupart des étudiants de cette école ont plusieurs adresses e-mail. En raison de cela, l’université souhaite avoir la possibilité de stocker plusieurs adresses e-mail avec les informations de contact de chaque étudiant.
Dans un cas comme celui-ci, un exemple de document pourrait avoir une structure comme suit :
Notez que cet exemple de document contient une liste intégrée d’adresses e-mail.
Représenter plus d’un sujet dans un seul document caractérise un modèle de données dénormalisé. Il permet aux applications de récupérer et de manipuler toutes les données pertinentes pour un objet donné (ici, un étudiant) d’un seul coup, sans avoir besoin d’accéder à plusieurs objets et collections séparés. Cela garantit également l’atomicité des opérations sur un tel document sans avoir à utiliser des transactions multi-documents pour garantir l’intégrité.
Stocker ensemble ce qui doit être consulté ensemble en utilisant des documents intégrés est souvent la manière optimale de représenter les données dans une base de données orientée document. Dans les directives suivantes, vous apprendrez comment différentes relations entre objets, telles que les relations un-à-un ou un-à-plusieurs, peuvent être modélisées de la meilleure façon dans une base de données orientée document.
Directive 2 — Modélisation des Relations Un-à-Un avec des Documents Intégrés
Une relation un-à-un représente une association entre deux objets distincts où un objet est connecté à exactement un autre objet.
Poursuivant avec l’exemple de l’étudiant de la section précédente, chaque étudiant n’a qu’une seule carte d’identité étudiante valide à un moment donné. Une carte n’appartient jamais à plusieurs étudiants, et aucun étudiant ne peut avoir plusieurs cartes d’identification. Si vous deviez stocker toutes ces données dans une base de données relationnelle, il serait probablement judicieux de modéliser la relation entre les étudiants et leurs cartes d’identité en stockant les enregistrements des étudiants et les enregistrements des cartes d’identité dans des tables séparées liées par des références.
Une méthode courante pour représenter de telles relations dans une base de données de documents est d’utiliser des documents intégrés. Par exemple, le document suivant décrit un étudiant nommé Sammy et sa carte d’identité étudiante :
Remarquez que, au lieu d’une seule valeur, le champ id_card
de ce document exemple contient un document intégré représentant la carte d’identité de l’étudiant, décrite par un numéro d’identification, la date d’émission de la carte et la date d’expiration de la carte. La carte d’identité devient essentiellement une partie du document décrivant l’étudiant Sammy, même si c’est un objet séparé dans la réalité. En général, structurer le schéma de document de cette manière pour pouvoir récupérer toutes les informations liées via une seule requête est un choix judicieux.
Les choses deviennent moins simples si vous rencontrez des relations qui connectent un objet d’un type avec de nombreux objets d’un autre type, comme les adresses e-mail d’un étudiant, les cours qu’il suit ou les messages qu’il publie sur le forum du conseil des étudiants. Dans les prochaines lignes directrices, vous utiliserez ces exemples de données pour apprendre différentes approches pour travailler avec des relations un-à-plusieurs et plusieurs-à-plusieurs.
Ligne directrice 3 — Modélisation des relations un-à-peu avec des documents intégrés
Lorsqu’un objet d’un type est lié à plusieurs objets d’un autre type, cela peut être décrit comme une relation un-à-plusieurs. Un étudiant peut avoir plusieurs adresses e-mail, une voiture peut être composée de nombreuses pièces, ou une commande d’achat peut inclure plusieurs articles. Chacun de ces exemples représente une relation un-à-plusieurs.
Bien que la manière la plus courante de représenter une relation un-à-un dans une base de données de documents soit par un document intégré, il existe plusieurs façons de modéliser les relations un-à-plusieurs dans un schéma de documents. Lorsque vous envisagez vos options pour modéliser au mieux ces relations, il y a trois propriétés de la relation donnée que vous devriez prendre en compte :
- Cardinalité : La cardinalité est la mesure du nombre d’éléments individuels dans un ensemble donné. Par exemple, si une classe compte 30 étudiants, vous pourriez dire que cette classe a une cardinalité de 30. Dans une relation un-à-plusieurs, la cardinalité peut varier d’un cas à l’autre. Un étudiant peut avoir une seule adresse e-mail ou plusieurs. Il peut être inscrit à quelques cours ou avoir un emploi du temps complètement rempli. Dans une relation un-à-plusieurs, la taille de « plusieurs » influencera la manière dont vous pourriez modéliser les données.
- Accès indépendant: Certaines données connexes seront rarement, voire jamais, consultées indépendamment de l’objet principal. Par exemple, il pourrait être peu courant de récupérer l’adresse e-mail d’un seul étudiant sans autres détails sur l’étudiant. En revanche, les cours d’une université pourraient nécessiter d’être consultés et mis à jour individuellement, indépendamment de l’étudiant ou des étudiants inscrits pour les suivre. Le fait de savoir si vous accéderez jamais à un document connexe seul influencera également la manière dont vous pourriez modéliser les données.
- Si la relation entre les données est strictement une relation un-à-plusieurs: Prenons l’exemple des cours qu’un étudiant participe à une université. Du point de vue de l’étudiant, il peut participer à plusieurs cours. En surface, cela peut sembler une relation un-à-plusieurs. Cependant, les cours universitaires sont rarement suivis par un seul étudiant ; plus souvent, plusieurs étudiants suivront la même classe. Dans de tels cas, la relation en question n’est pas vraiment une relation un-à-plusieurs, mais une relation plusieurs-à-plusieurs, et donc vous adopteriez une approche différente pour modéliser cette relation que vous ne feriez pour une relation un-à-plusieurs.
Imaginez que vous décidiez comment stocker les adresses e-mail des étudiants. Chaque étudiant peut avoir plusieurs adresses e-mail, comme une pour le travail, une pour un usage personnel, et une fournie par l’université. Un document représentant une seule adresse e-mail pourrait prendre une forme similaire à celle-ci :
En termes de cardinalité, il n’y aura que quelques adresses e-mail pour chaque étudiant, car il est peu probable qu’un étudiant ait des dizaines — sans parler de centaines — d’adresses e-mail. Ainsi, cette relation peut être caractérisée comme une relation un-à-peu, ce qui est une raison convaincante d’intégrer directement les adresses e-mail dans le document de l’étudiant et de les stocker ensemble. Vous ne courez aucun risque que la liste des adresses e-mail grandisse indéfiniment, ce qui rendrait le document volumineux et inefficace à utiliser.
Note: Soyez conscient que le stockage des données dans des tableaux présente certains pièges. Par exemple, un document MongoDB ne peut pas dépasser 16 Mo en taille. Bien qu’il soit possible et courant d’intégrer plusieurs documents à l’aide de champs de tableau, si la liste des objets grandit de manière incontrôlée, le document pourrait rapidement atteindre cette limite de taille. De plus, le stockage d’une grande quantité de données à l’intérieur de tableaux intégrés a un impact significatif sur les performances des requêtes.
L’intégration de plusieurs documents dans un champ de tableau sera probablement adaptée dans de nombreuses situations, mais sachez qu’elle n’est pas toujours la meilleure solution.
En ce qui concerne l’accès indépendant, les adresses e-mail ne seront probablement pas consultées séparément de l’étudiant. Par conséquent, il n’y a pas d’incitation claire à les stocker en tant que documents séparés dans une collection séparée. C’est une autre raison convaincante de les intégrer dans le document de l’étudiant.
La dernière chose à considérer est de savoir si cette relation est vraiment une relation un-à-plusieurs plutôt qu’une relation plusieurs-à-plusieurs. Étant donné qu’une adresse e-mail appartient à une seule personne, il est raisonnable de décrire cette relation comme une relation un-à-plusieurs (ou, peut-être plus précisément, une relation un-à-peu) plutôt qu’une relation plusieurs-à-plusieurs.
Ces trois hypothèses suggèrent que l’intégration des différentes adresses e-mail des étudiants dans les mêmes documents qui décrivent les étudiants eux-mêmes serait un bon choix pour stocker ce type de données. Un exemple de document d’étudiant avec des adresses e-mail intégrées pourrait prendre cette forme :
En utilisant cette structure, chaque fois que vous récupérez un document d’étudiant, vous récupérerez également les adresses e-mail intégrées dans la même opération de lecture.
Si vous modélisez une relation du type un-à-peu, où les documents associés ne doivent pas être consultés indépendamment, l’intégration directe des documents de cette manière est généralement souhaitable, car cela peut réduire la complexité du schéma.
Comme mentionné précédemment, cependant, l’intégration de documents de cette manière n’est pas toujours la solution optimale. La section suivante fournit plus de détails sur les raisons pour lesquelles cela peut être le cas dans certains scénarios, et décrit comment utiliser les références aux enfants comme une alternative pour représenter les relations dans une base de données de documents.
Ligne directrice 4 — Modélisation des relations un-à-plusieurs et plusieurs-à-plusieurs avec des références enfants
La nature de la relation entre les étudiants et leurs adresses e-mail a influencé la manière dont cette relation pouvait être modélisée au mieux dans une base de données de documents. Il existe certaines différences entre cela et la relation entre les étudiants et les cours qu’ils suivent, donc la façon dont vous modélisez les relations entre les étudiants et leurs cours sera également différente.
Un document décrivant un seul cours auquel un étudiant participe pourrait suivre une structure comme celle-ci :
Supposons que vous ayez décidé dès le départ d’utiliser des documents intégrés pour stocker des informations sur les cours de chaque étudiant, comme dans cet exemple :
Ce serait un document MongoDB parfaitement valide et pourrait très bien servir à cette fin, mais considérez les trois propriétés de relation que vous avez apprises dans la ligne directrice précédente.
La première est la cardinalité. Un étudiant ne maintiendra probablement que quelques adresses e-mail, mais il peut suivre plusieurs cours pendant ses études. Après plusieurs années de fréquentation, il pourrait y avoir des dizaines de cours auxquels l’étudiant a participé. De plus, il suivrait ces cours avec de nombreux autres étudiants qui suivent également leur propre ensemble de cours sur leurs années de fréquentation.
Si vous décidiez d’intégrer chaque cours comme dans l’exemple précédent, le document de l’étudiant deviendrait rapidement ingérable. Avec une cardinalité plus élevée, l’approche du document intégré devient moins attrayante.
La deuxième considération est l’accès indépendant. Contrairement aux adresses e-mail, il est raisonnable de supposer qu’il y aura des cas où des informations sur les cours universitaires devront être récupérées individuellement. Par exemple, supposons que quelqu’un ait besoin d’informations sur les cours disponibles pour préparer un dossier de marketing. De plus, les cours devront probablement être mis à jour au fil du temps : le professeur enseignant le cours pourrait changer, son horaire pourrait fluctuer, ou ses prérequis pourraient devoir être mis à jour.
Si vous deviez stocker les cours sous forme de documents intégrés dans les documents des étudiants, récupérer la liste de tous les cours offerts par l’université deviendrait problématique. De plus, chaque fois qu’un cours nécessite une mise à jour, vous devriez parcourir tous les dossiers des étudiants et mettre à jour les informations sur le cours partout. Ces deux raisons justifient de stocker les cours séparément et de ne pas les intégrer complètement.
La troisième chose à considérer est de savoir si la relation entre l’étudiant et un cours universitaire est en réalité un-à-plusieurs ou plutôt plusieurs-à-plusieurs. Dans ce cas, c’est ce dernier, car plus d’un étudiant peut suivre chaque cours. La cardinalité de cette relation et les aspects d’accès indépendant suggèrent de ne pas intégrer chaque document de cours, principalement pour des raisons pratiques comme la facilité d’accès et de mise à jour. Compte tenu de la nature plusieurs-à-plusieurs de la relation entre les cours et les étudiants, il pourrait être judicieux de stocker les documents de cours dans une collection séparée avec des identifiants uniques.
Les documents représentant des classes dans cette collection séparée pourraient avoir une structure similaire à ces exemples :
Si vous décidez de stocker des informations sur les cours de cette manière, vous devrez trouver un moyen de connecter les étudiants à ces cours afin de savoir quels étudiants suivent quels cours. Dans des cas comme celui-ci, où le nombre d’objets liés n’est pas excessivement grand, surtout avec des relations plusieurs-à-plusieurs, une manière courante de procéder est d’utiliser des références enfants.
Avec les références enfants, un document d’étudiant référencera les identifiants des objets des cours que l’étudiant suit dans un tableau intégré, comme dans cet exemple :
Notez que ce document d’exemple possède toujours un champ cours
qui est également un tableau, mais au lieu d’intégrer des documents de cours complets comme dans l’exemple précédent, seuls les identifiants référençant les documents de cours dans la collection séparée sont intégrés. Maintenant, lors de la récupération d’un document d’étudiant, les cours ne seront pas immédiatement disponibles et devront être interrogés séparément. D’un autre côté, il est immédiatement connu quels cours récupérer. De plus, au cas où des détails d’un cours doivent être mis à jour, seul le document de cours lui-même doit être modifié. Toutes les références entre les étudiants et leurs cours resteront valides.
Note : Il n’y a pas de règle stricte pour savoir quand la cardinalité d’une relation est trop grande pour intégrer des références enfants de cette manière. Vous pourriez choisir une approche différente à une cardinalité plus basse ou plus élevée si cela convient le mieux à l’application en question. Après tout, vous voudrez toujours structurer vos données de manière à ce qu’elles conviennent à la manière dont votre application les interroge et les met à jour.
Si vous modélisez une relation un-à-plusieurs où la quantité de documents associés est dans des limites raisonnables et que les documents associés doivent être accessibles de manière indépendante, préférez stocker les documents associés séparément et intégrez des références aux enfants pour les connecter.
Maintenant que vous avez appris à utiliser des références aux enfants pour signifier des relations entre différents types de données, ce guide présentera un concept inverse : les références aux parents.
Directive 5 — Modélisation de relations un-à-plusieurs illimitées avec des références aux parents
L’utilisation de références aux enfants fonctionne bien lorsqu’il y a trop d’objets associés pour les intégrer directement dans le document parent, mais la quantité est toujours dans des limites connues. Cependant, il existe des cas où le nombre de documents associés pourrait être illimité et continuer à croître avec le temps.
Par exemple, imaginez que le conseil des étudiants de l’université dispose d’un tableau d’affichage où n’importe quel étudiant peut poster ce qu’il veut, y compris des questions sur les cours, des récits de voyage, des annonces d’emploi, des matériaux d’étude ou juste une discussion libre. Un exemple de message dans ce cas consiste en un sujet et un corps de message :
Vous pourriez utiliser l’une des deux approches discutées précédemment — l’intégration et les références aux enfants — pour modéliser cette relation. Si vous décidiez d’opter pour l’intégration, le document de l’étudiant pourrait prendre une forme comme celle-ci :
Cependant, si un étudiant est prolifique dans l’écriture de messages, son document deviendra rapidement incroyablement long et pourrait facilement dépasser la limite de taille de 16 Mo, ce qui suggère contre l’intégration. De plus, les messages pourraient devoir être consultés séparément de l’étudiant, comme cela pourrait être le cas si la page du forum est conçue pour afficher les derniers messages postés par les étudiants. Cela suggère également que l’intégration n’est pas le meilleur choix pour ce scénario.
Note : Vous devez également considérer si les messages du forum sont fréquemment consultés lors de la récupération du document de l’étudiant. Si ce n’est pas le cas, avoir tous ces messages intégrés dans ce document entraînerait une pénalité de performance lors de la récupération et de la manipulation de ce document, même si la liste des messages ne serait pas utilisée fréquemment. L’accès peu fréquent des données connexes est souvent un autre indice que vous ne devriez pas intégrer des documents.
Maintenant, envisagez d’utiliser des références aux enfants au lieu d’intégrer des documents complets comme dans l’exemple précédent. Les messages individuels seraient stockés dans une collection séparée, et le document de l’étudiant pourrait alors avoir la structure suivante :
Dans cet exemple, le champ message_board_messages
stocke désormais les références aux messages écrits par Sammy. Cependant, changer d’approche ne résout qu’un des problèmes mentionnés précédemment, car il est désormais possible d’accéder aux messages de manière indépendante. Bien que la taille du document de l’étudiant augmenterait plus lentement en utilisant l’approche des références aux enfants, la collection d’identifiants d’objet pourrait également devenir ingérable compte tenu de la cardinalité illimitée de cette relation. Après tout, un étudiant pourrait facilement écrire des milliers de messages pendant ses quatre années d’études.
Dans de tels scénarios, une manière courante de connecter un objet à un autre est par le biais de références parentales. Contrairement aux références aux enfants décrites précédemment, ce n’est plus le document de l’étudiant qui fait référence aux messages individuels, mais plutôt une référence dans le document du message pointant vers l’étudiant qui l’a écrit.
Pour utiliser les références parentales, vous devriez modifier le schéma du document de message pour contenir une référence à l’étudiant qui a rédigé le message:
Remarquez que le nouveau champ posted_by
contient l’identifiant d’objet du document de l’étudiant. Maintenant, le document de l’étudiant ne contiendra aucune information sur les messages qu’il a postés:
Pour récupérer la liste des messages écrits par un étudiant, vous effectueriez une requête sur la collection des messages et filtreriez sur le champ posted_by
. Leur présence dans une collection séparée permet de laisser la liste des messages s’étendre sans affecter les documents des étudiants.
Note : Lors de l’utilisation de références parent, la création d’un index sur le champ référençant le document parent peut considérablement augmenter les performances des requêtes chaque fois que vous filtrez par rapport à l’identifiant du document parent.
Si vous modélisez une relation un-à-plusieurs où la quantité de documents connexes est illimitée, indépendamment de savoir si les documents doivent être accessibles indépendamment, il est généralement conseillé de stocker les documents connexes séparément et d’utiliser des références parent pour les connecter au document parent.
Conclusion
Grâce à la flexibilité des bases de données orientées documents, déterminer la meilleure façon de modéliser les relations dans une base de données de documents est moins une science stricte qu’elle ne l’est dans une base de données relationnelle. En lisant cet article, vous vous êtes familiarisé avec l’intégration de documents et l’utilisation de références enfants et parent pour stocker des données connexes. Vous avez appris à considérer la cardinalité des relations et à éviter les tableaux non bornés, ainsi qu’à prendre en compte si le document sera consulté séparément ou fréquemment.
Ce ne sont là que quelques directives qui peuvent vous aider à modéliser des relations typiques dans MongoDB, mais la modélisation des schémas de base de données n’est pas une solution universelle. Prenez toujours en compte votre application et la manière dont elle utilise et met à jour les données lors de la conception du schéma.
Pour en savoir plus sur la conception des schémas et les modèles courants pour stocker différents types de données dans MongoDB, nous vous encourageons à consulter la documentation officielle de MongoDB sur ce sujet.
Source:
https://www.digitalocean.com/community/tutorials/how-to-design-a-document-schema-in-mongodb