Dans nos modèles Meurise, MCD, SGBDR, la relation entre tables est naturelle. Avec du NoSQL, ça l’est moins. Mongo nous propose des pistes de modélisation, mais je ne trouvais pas les solutions satisfaisantes.
Je m’explique. Imaginons, une collection d’auteurs et une collection d’articles. Avec un SGBDR classique on aurait :
D’après Mongo, il y a 2 solutions :
- On inclue le document auteur (tout ou partie) dans le document article
- Avantage : quand on veut la liste des articles, on a les infos de l’auteur avec
- Inconvénient : quand l’auteur change une de ces infos présente dans le document article, il faut faire 2 mise à jour des données
- On a une référence de l’auteur (son id) dans le document article.
- Avantage : les mises à jours sont simplifiées
- Inconvénient : on est forcé de récupérer la liste des articles, puis pour chacun d’eux, récupérer l’id de l’auteur, rechercher tous les auteurs dont l’id se trouve dans cette liste et enfin raccrocher l’article à son auteur. 2 requêtes et des manipulations de listes côté serveur.
Mais alors est-ce possible de tout récupérer en un seul appel avec une référence de l’auteur dans l’article? Après un peu de recherche oui. Reprenons nos structures Json :
Collection User :
{ "_id" : "1", "name" : "Marin", "forname" : "Xavier", "nickname" : "xav" }
Collection Blogs :
{ "_id" : "1", "timestamp" : NumberLong(1401867847649), "title" : "Mon article sur la vie trépidante des loutres", "content" : "Les loutres ont une vie absolument passionnante mais se droguent beaucoup trop.", "user_id" : "1" }
Il existe quelques pistes sur le Web, une a retenu mon attention et portait sur la fonction de MapReduce de Mongo, la voici :
- An alternate way to implement JOINs in MongoDB : un seul appel à Mongo, mais limité aux versions de Mongo < 2.4
- Easiest Way to Implement JOINs in MongoDB 2.4 : marche avec les dernières versions de Mongo, mais effectue 3 requêtes (c’est con)
Je me suis arraché les 3 cheveux qui me restent et je pense avoir trouver une solution satisfaisante :
db.runCommand({eval : function() { var f = db.blogs.find().sort({timestamp : -10}), out = []; for (var i = 0; i < f.length(); i++) { f[i].user_id = db.user.findOne({_id :f[i].user_id}); out.push(f[i]); } return out; } })
Qui dit mieux? Je suis ouvert au débat.