La première chose à comprendre à propos de la population de mangoustes est qu'il ne s'agit pas de magie, mais simplement d'une méthode pratique qui vous permet de récupérer des informations connexes sans tout faire vous-même.
Le concept est essentiellement destiné à être utilisé lorsque vous décidez que vous allez devoir placer des données dans une collection distincte plutôt que d'intégrer ces données, et vos principales considérations doivent généralement porter sur la taille du document ou lorsque ces informations connexes sont soumises à des mises à jour fréquentes qui rendraient maintenir les données intégrées peu maniables.
La partie "pas magique" est que, essentiellement, ce qui se passe sous les couvertures, c'est que lorsque vous "référez" une autre source, la fonction de peuplement effectue une ou plusieurs requêtes supplémentaires à cette collection "liée" afin de "fusionner" ces résultats du parent objet que vous avez récupéré. Vous pourriez le faire vous-même, mais la méthode est là pour simplifier la tâche. La considération de "performance" évidente est qu'il n'y a pas un seul aller-retour vers la base de données (instance MongoDB) afin de récupérer toutes les informations. Il y en a toujours plus d'un.
À titre d'exemple, prenez deux collections :
{
"_id": ObjectId("5392fea00ff066b7d533a765"),
"customerName": "Bill",
"items": [
ObjectId("5392fee10ff066b7d533a766"),
ObjectId("5392fefe0ff066b7d533a767")
]
}
Et les objets :
{ "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 }
{ "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 }
Le "meilleur" que l'on peut faire avec un modèle "référencé" ou l'utilisation de populate (sous le capot) est ceci :
var order = db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });
order.items = db.items.find({ "_id": { "$in": order.items } ).toArray();
Il y a donc clairement "au moins" deux requêtes et opérations pour "joindre" ces données.
Le concept d'intégration est essentiellement la réponse de MongoDB à la façon de gérer les "jointures" non prises en charge. Ainsi, plutôt que de diviser les données en collections normalisées, vous essayez d'intégrer les données "liées" directement dans le document qui les utilise. Les avantages ici sont qu'il y a une seule opération de "lecture" pour récupérer les informations "liées", et aussi un seul point d'opérations "d'écriture" pour mettre à jour les entrées "parent" et "enfant", bien qu'il soit souvent impossible d'écrire dans "plusieurs" enfants à la fois sans traiter les "listes" sur le client ou autrement accepter des opérations d'écriture "multiples", et de préférence en traitement "par lots".
Les données ressemblent alors plutôt à ceci (par rapport à l'exemple ci-dessus) :
{
"_id": ObjectId("5392fea00ff066b7d533a765"),
"customerName": "Bill",
"items": [
{ "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 },
{ "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 }
]
}
Par conséquent, la récupération des données consiste simplement à :
db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });
Les avantages et les inconvénients de l'un ou l'autre dépendront toujours en grande partie du modèle d'utilisation de votre application. Mais en un coup d'œil :
Intégrer
-
La taille totale du document avec des données intégrées ne dépassera généralement pas 16 Mo de stockage (la limite BSON) ou sinon (à titre indicatif) avoir des tableaux contenant 500 entrées ou plus.
-
Les données intégrées ne nécessitent généralement pas de modifications fréquentes. Vous pourriez donc vivre avec une "duplication" qui provient de la dénormalisation n'entraînant pas la nécessité de mettre à jour ces "doublons" avec les mêmes informations dans de nombreux documents parents juste pour invoquer un changement.
-
Les données connexes sont fréquemment utilisées en association avec le parent. Ce qui signifie que si vos cas "lecture/écriture" ont presque toujours besoin de "lecture/écriture" à la fois pour le parent et l'enfant, il est logique d'intégrer les données pour les opérations atomiques.
Référencement
-
Les données associées dépasseront toujours la limite de 16 Mo BSON. Vous pouvez toujours envisager une approche hybride de "bucketing", mais la limite stricte générale du document principal ne peut pas être dépassée. Les cas courants sont "post" et "commentaires" où l'activité "commentaire" devrait être très importante.
-
Les données connexes nécessitent une mise à jour régulière. Ou essentiellement le cas où vous "normalisez" parce que ces données sont "partagées" entre de nombreux parents et que les données "liées" sont modifiées assez fréquemment pour qu'il ne soit pas pratique de mettre à jour les éléments intégrés dans chaque "parent" où cet élément "enfant" se produit . Le cas le plus simple consiste à simplement référencer "l'enfant" et à effectuer le changement une seule fois.
-
Il y a une séparation claire des lectures et des écritures. Dans le cas où vous n'allez peut-être pas toujours exiger ces informations "connexes" lors de la lecture du "parent" ou autrement pour ne pas avoir besoin de toujours modifier le "parent" lors de l'écriture à l'enfant, il pourrait y avoir une bonne raison de séparer le modèle comme référencé. De plus, s'il y a un désir général de mettre à jour de nombreux "sous-documents" à la fois dans lesquels ces "sous-documents" sont en fait des références à une autre collection, alors assez souvent la mise en œuvre est plus efficace lorsque les données sont dans un fichier séparé. collecte.
Il y a donc en fait une discussion beaucoup plus large des "avantages/inconvénients" pour l'une ou l'autre position sur la documentation MongoDB sur la modélisation des données, qui couvre divers cas d'utilisation et façons d'aborder soit l'intégration soit le modèle référencé tel qu'il est pris en charge par la méthode de peuplement. /P>
J'espère que les "points" sont utiles, mais la recommandation générale est de considérer les modèles d'utilisation des données de votre application et de choisir ce qui est le mieux. Avoir la "option" d'intégrer "devrait" être la raison pour laquelle vous avez choisi MongoDB, mais ce sera en fait la façon dont votre application "utilise les données" qui décidera quelle méthode convient à quelle partie de votre modélisation de données (car ce n'est pas "tout ou rien") le meilleur.
- Notez que depuis que cela a été écrit à l'origine, MongoDB a introduit le
$lookup
opérateur qui effectue bien des "jointures" entre collections sur le serveur. Aux fins de la discussion générale ici, whist "mieux" dans la plupart des cas que la surcharge de "requêtes multiples" encourue parpopulate()
et les "requêtes multiples" en général, il y a toujours un "overhead significatif" engagés avec n'importe quel$lookup
opération.Le principe de conception de base est "intégré" signifie "déjà là" par opposition à "aller chercher ailleurs". Essentiellement la différence entre "dans votre poche" et "sur l'étagère", et en termes d'E/S généralement plus comme "sur l'étagère de la bibliothèque du centre-ville" , et notamment plus loin pour les requêtes basées sur le réseau.