Le(s) problème(s)
Comme écrit avant , il y a plusieurs problèmes lors de la sur-intégration :
Problème 1 :taille limite de BSON
Au moment d'écrire ces lignes, les documents BSON sont limités à 16 Mo . Si cette limite est atteinte, MongoDB lèvera une exception et vous ne pourrez tout simplement pas ajouter plus de commentaires et dans le pire des cas, ne même pas changer le nom (d'utilisateur) ou l'image si le changement augmente la taille du document.
Problème 2 :Limitations et performances des requêtes
Il n'est pas facile d'interroger ou de trier le tableau de commentaires sous certaines conditions. Certaines choses nécessiteraient une agrégation assez coûteuse, d'autres des déclarations plutôt compliquées.
Bien que l'on puisse affirmer qu'une fois les requêtes en place, ce n'est pas vraiment un problème, je ne suis pas d'accord. Premièrement, plus une requête est compliquée, plus elle est difficile à optimiser, à la fois pour le développeur et par la suite pour l'optimiseur de requête MongoDBs. J'ai obtenu les meilleurs résultats en simplifiant les modèles de données et les requêtes, en accélérant les réponses d'un facteur 100 dans une instance.
Lors de la mise à l'échelle, les ressources nécessaires pour les requêtes compliquées et/ou coûteuses peuvent même se résumer à des machines entières par rapport à un modèle de données plus simple et selon les requêtes.
Problème 3 :maintenabilité
Enfin, vous pourriez bien rencontrer des problèmes pour maintenir votre code. En règle générale
Dans ce contexte, "coûteux" fait à la fois référence à l'argent (pour les projets professionnels) et au temps (pour les projets de loisirs).
(Ma !) Solution
C'est assez simple :simplifiez votre modèle de données. Par conséquent, vos requêtes deviendront moins compliquées et (espérons-le) plus rapides.
Étape 1 :Identifiez vos cas d'utilisation
Cela va être une supposition folle pour moi, mais l'important ici est de vous montrer la méthode générale. Je définirais vos cas d'utilisation comme suit :
- Pour une publication donnée, les utilisateurs doivent pouvoir commenter
- Pour un article donné, afficher l'auteur et les commentaires, ainsi que le nom d'utilisateur des commentateurs et des auteurs et leur photo
- Pour un utilisateur donné, il devrait être facilement possible de changer le nom, le nom d'utilisateur et l'image
Étape 2 :Modélisez vos données en conséquence
Utilisateurs
Tout d'abord, nous avons un modèle utilisateur simple
{
_id: new ObjectId(),
name: "Joe Average",
username: "HotGrrrl96",
picture: "some_link"
}
Rien de nouveau ici, ajouté juste pour être complet.
Messages
{
_id: new ObjectId()
title: "A post",
content: " Interesting stuff",
picture: "some_link",
created: new ISODate(),
author: {
username: "HotGrrrl96",
picture: "some_link"
}
}
Et c'est à peu près tout pour un post. Il y a deux choses à noter ici :premièrement, nous stockons les données de l'auteur dont nous avons immédiatement besoin lors de l'affichage d'un article, car cela nous évite une requête pour un cas d'utilisation très courant, voire omniprésent. Pourquoi ne sauvegardons-nous pas les commentaires et les données des commentateurs en conséquence ? En raison de la limite de taille de 16 Mo , nous essayons d'empêcher le stockage des références dans un seul document. Nous stockons plutôt les références dans des documents de commentaires :
Commentaires
{
_id: new ObjectId(),
post: someObjectId,
created: new ISODate(),
commenter: {
username: "FooBar",
picture: "some_link"
},
comment: "Awesome!"
}
Comme pour les publications, nous avons toutes les données nécessaires pour afficher une publication.
Les requêtes
Ce que nous avons réalisé maintenant, c'est que nous avons contourné la limite de taille BSON et nous n'avons pas besoin de nous référer aux données de l'utilisateur pour pouvoir afficher les publications et les commentaires, ce qui devrait nous éviter de nombreuses requêtes. Mais revenons aux cas d'utilisation et à quelques requêtes supplémentaires
Ajouter un commentaire
C'est tout à fait simple maintenant.
Obtenir tout ou partie des commentaires pour une publication donnée
Pour tous les commentaires
db.comments.find({post:objectIdOfPost})
Pour les 3 derniers commentaires
db.comments.find({post:objectIdOfPost}).sort({created:-1}).limit(3)
Donc, pour afficher un message et tout (ou certains) de ses commentaires, y compris les noms d'utilisateur et les images, nous en sommes à deux requêtes. Plus que ce dont vous aviez besoin auparavant, mais nous avons contourné la limite de taille et, en gros, vous pouvez avoir un nombre indéfini de commentaires pour chaque publication. Mais passons à quelque chose de réel
Obtenir les 5 derniers articles et leurs 3 derniers commentaires
Il s'agit d'un processus en deux étapes. Cependant, avec une indexation correcte (nous y reviendrons plus tard), cela devrait toujours être rapide (et donc économiser des ressources) :
var posts = db.posts.find().sort({created:-1}).limit(5)
posts.forEach(
function(post) {
doSomethingWith(post);
var comments = db.comments.find({"post":post._id}).sort("created":-1).limit(3);
doSomethingElseWith(comments);
}
)
Obtenir tous les messages d'un utilisateur donné triés du plus récent au plus ancien et leurs commentaires
var posts = db.posts.find({"author.username": "HotGrrrl96"},{_id:1}).sort({"created":-1});
var postIds = [];
posts.forEach(
function(post){
postIds.push(post._id);
}
)
var comments = db.comments.find({post: {$in: postIds}}).sort({post:1, created:-1});
Notez que nous n'avons ici que deux requêtes. Bien que vous deviez établir "manuellement" le lien entre les publications et leurs commentaires respectifs, cela devrait être assez simple.
Modifier un nom d'utilisateur
Il s'agit vraisemblablement d'un cas d'utilisation rare exécuté. Cependant, ce n'est pas très compliqué avec ledit modèle de données
Tout d'abord, nous changeons le document utilisateur
db.users.update(
{ username: "HotGrrrl96"},
{
$set: { username: "Joe Cool"},
$push: {oldUsernames: "HotGrrrl96" }
},
{
writeConcern: {w: "majority"}
}
);
Nous poussons l'ancien nom d'utilisateur vers un tableau correspondant. Il s'agit d'une mesure de sécurité en cas de problème avec les opérations suivantes. De plus, nous plaçons le problème d'écriture à un niveau assez élevé afin de garantir la pérennité des données.
db.posts.update(
{ "author.username": "HotGrrrl96"},
{ $set:{ "author.username": "Joe Cool"} },
{
multi:true,
writeConcern: {w:"majority"}
}
)
Rien de spécial ici. La déclaration de mise à jour pour les commentaires est à peu près la même. Bien que ces requêtes prennent un certain temps, elles sont rarement exécutées.
Les indices
En règle générale, on peut dire que MongoDB ne peut utiliser qu'un seul index par requête. Bien que ce ne soit pas tout à fait vrai puisqu'il existe des intersections d'index, c'est facile à gérer. Une autre chose est que les champs individuels d'un index composé peuvent être utilisés indépendamment. Ainsi, une approche simple de l'optimisation des index consiste à trouver la requête avec le plus de champs utilisés dans les opérations qui utilisent des index et à en créer un index composé. Notez que l'ordre d'occurrence dans la requête est important. Alors, allons-y.
Messages
db.posts.createIndex({"author.username":1,"created":-1})
Commentaires
db.comments.createIndex({"post":1, "created":-1})
Conclusion
Certes, un document entièrement intégré par message est le moyen le plus rapide de le charger et de ses commentaires. Cependant, il ne s'adapte pas bien et en raison de la nature des requêtes éventuellement complexes nécessaires pour le traiter, cet avantage en termes de performances peut être exploité ou même éliminé.
Avec la solution ci-dessus, vous échangez une certaine vitesse (si !) contre une évolutivité pratiquement illimitée et une manière beaucoup plus simple de traiter les données.
Hth.