Voile.js peuplent les associations imbriquées
j'ai moi-même une question concernant les associations dans les voiles.js la version 0.10-rc5. J'ai construit une application dans laquelle plusieurs modèles sont associés les uns aux autres, et je suis arrivé à un point où j'ai besoin d'obtenir des associations de nid d'une façon ou d'une autre.
Il y a trois parties:
tout d'abord il y a quelque chose comme un billet de blog, qui est écrit par un utilisateur. Dans le post de blog je veux montrer les informations de l'utilisateur associé comme leur nom d'utilisateur. Maintenant, tout fonctionne très bien ici. Jusqu'à la prochaine étape: j'essaye d'afficher les commentaires qui sont associées avec le post.
Les commentaires sont un Modèle distinct, appelé Commentaire. Chacune dispose également d'un auteur (de l'utilisateur). Je peux facilement afficher une liste des commentaires, bien que lorsque je veux afficher les informations de L'utilisateur associées au commentaire, Je ne peux pas comprendre comment remplir le commentaire avec les informations de l'utilisateur.
dans mon contrôleur j'essaie de faire quelque chose comme ceci:
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments') // I want to populate this comment with .populate('user') or something
.exec(function(err, post) {
// Handle errors & render view etc.
});
Dans mon Post du "show" de l'action, je suis en train de récupérer les informations de ce type (simplifié):
<ul>
<%- _.each(post.comments, function(comment) { %>
<li>
<%= comment.user.name %>
<%= comment.description %>
</li>
<% }); %>
</ul>
le commentaire.utilisateur.le nom ne sera pas défini cependant. Si j'essaie d'accéder à la propriété' user', comme commentaire.utilisateur, ça montrera son identité. Ce qui me dit qu'il n'est pas automatiquement peupler l'information de l'utilisateur au commentaire quand j'associe le commentaire avec un autre modèle.
Personne idéaux pour résoudre ce correctement :)?
Merci d'avance!
P.S.
pour clarifier, c'est comme ça que j'ai mis en place les associations dans différents modèles:
// User.js
posts: {
collection: 'post'
},
hours: {
collection: 'hour'
},
comments: {
collection: 'comment'
}
// Post.js
user: {
model: 'user'
},
comments: {
collection: 'comment',
via: 'post'
}
// Comment.js
user: {
model: 'user'
},
post: {
model: 'post'
}
7 réponses
Ou vous pouvez utiliser le haut -L'Oiseau Bleu Promesse de fonction pour le faire. (Travaillant sur Sails@v0.10.5)--4-->
Voir les codes ci-dessous:
var _ = require('lodash');
...
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments')
.then(function(post) {
var commentUsers = User.find({
id: _.pluck(post.comments, 'user')
//_.pluck: Retrieves the value of a 'user' property from all elements in the post.comments collection.
})
.then(function(commentUsers) {
return commentUsers;
});
return [post, commentUsers];
})
.spread(function(post, commentUsers) {
commentUsers = _.indexBy(commentUsers, 'id');
//_.indexBy: Creates an object composed of keys generated from the results of running each element of the collection through the given callback. The corresponding value of each key is the last element responsible for generating the key
post.comments = _.map(post.comments, function(comment) {
comment.user = commentUsers[comment.user];
return comment;
});
res.json(post);
})
.catch(function(err) {
return res.serverError(err);
});
Quelques explications:
- j'utilise le Lo-Dash pour s'occuper des tableaux. Pour plus de détails, reportez-vous à la Document Officiel
- notez les valeurs de retour à l'intérieur de la première fonction "puis", ces objets "[post, commentUsers] " à l'intérieur du tableau sont aussi des objets "prometteurs". Ce qui signifie qu'ils ne contiennent pas les données de valeur quand ils ont été exécutés pour la première fois, jusqu'à ce qu'ils obtiennent la valeur. De sorte que la fonction" spread " attendra la valeur d'acture venir et continuer à faire les étoffes de repos.
pour le moment, il n'y a pas de façon de peupler les associations imbriquées. Votre meilleur pari est d'utiliser asynchrone à faire un mapping:
async.auto({
// First get the post
post: function(cb) {
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments')
.exec(cb);
},
// Then all of the comment users, using an "in" query by
// setting "id" criteria to an array of user IDs
commentUsers: ['post', function(cb, results) {
User.find({id: _.pluck(results.post.comments, 'user')}).exec(cb);
}],
// Map the comment users to their comments
map: ['commentUsers', function(cb, results) {
// Index comment users by ID
var commentUsers = _.indexBy(results.commentUsers, 'id');
// Get a plain object version of post & comments
var post = results.post.toObject();
// Map users onto comments
post.comments = post.comments.map(function(comment) {
comment.user = commentUsers[comment.user];
return comment;
});
return cb(null, post);
}]
},
// After all the async magic is finished, return the mapped result
// (or an error if any occurred during the async block)
function finish(err, results) {
if (err) {return res.serverError(err);}
return res.json(results.map);
}
);
ce n'est pas aussi joli que la population nichée (qui est en préparation, mais probablement pas pour v0.10), mais du bon côté, c'est en fait assez efficace.
il vaut la peine de dire qu'il y a une demande de pull pour ajouter la population nichée: https://github.com/balderdashy/waterline/pull/1052
Pull request n'est pas fusionné pour le moment mais vous pouvez l'utiliser en installant un directement avec
npm i Atlantis-Software/waterline#deepPopulate
Avec elle, vous pouvez faire quelque chose comme .populate('user.comments ...)'
.
sails v0.11 doesn't support _.pluck and _.indexBy use sails.util.pluck and sails.util.indexBy instead.
async.auto({
// First get the post
post: function(cb) {
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments')
.exec(cb);
},
// Then all of the comment users, using an "in" query by
// setting "id" criteria to an array of user IDs
commentUsers: ['post', function(cb, results) {
User.find({id:sails.util.pluck(results.post.comments, 'user')}).exec(cb);
}],
// Map the comment users to their comments
map: ['commentUsers', function(cb, results) {
// Index comment users by ID
var commentUsers = sails.util.indexBy(results.commentUsers, 'id');
// Get a plain object version of post & comments
var post = results.post.toObject();
// Map users onto comments
post.comments = post.comments.map(function(comment) {
comment.user = commentUsers[comment.user];
return comment;
});
return cb(null, post);
}]
},
// After all the async magic is finished, return the mapped result
// (or an error if any occurred during the async block)
function finish(err, results) {
if (err) {return res.serverError(err);}
return res.json(results.map);
}
);
j'ai créé un module NPM pour cela appelé imbriqué-pop. Vous pouvez le trouver sur le lien ci-dessous.
https://www.npmjs.com/package/nested-pop
Utilisez-le de la façon suivante.
var nestedPop = require('nested-pop');
User.find()
.populate('dogs')
.then(function(users) {
return nestedPop(users, {
dogs: [
'breed'
]
}).then(function(users) {
return users
}).catch(function(err) {
throw err;
});
}).catch(function(err) {
throw err;
);
Vous pouvez utiliser async bibliothèque qui est très propre et simple à comprendre. Pour chaque commentaire un message, vous pouvez remplir plusieurs champs que vous le souhaitez avec des tâches, exécuter en parallèle et de récupérer les résultats lorsque toutes les tâches sont effectuées. Enfin, vous n'avez qu'à retourner le résultat final.
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments') // I want to populate this comment with .populate('user') or something
.exec(function (err, post) {
// populate each post in parallel
async.each(post.comments, function (comment, callback) {
// you can populate many elements or only one...
var populateTasks = {
user: function (cb) {
User.findOne({ id: comment.user })
.exec(function (err, result) {
cb(err, result);
});
}
}
async.parallel(populateTasks, function (err, resultSet) {
if (err) { return next(err); }
post.comments = resultSet.user;
// finish
callback();
});
}, function (err) {// final callback
if (err) { return next(err); }
return res.json(post);
});
});
à partir de sailsjs 1.0 le "profonde remplir" pull request est toujours ouvert, mais la solution de fonction async suivante semble assez élégante IMO:
var post = await Post
.findOne({ id: req.param('id') })
.populate('user')
.populate('comments');
if (post && post.comments.length > 0) {
post.comments = await Comment
.find({ id: post.comments.map((comment) => comment.id) })
.populate('user');
}