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'
}
47
demandé sur Brad Larson 2014-05-03 19:39:33

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:

  1. j'utilise le Lo-Dash pour s'occuper des tableaux. Pour plus de détails, reportez-vous à la Document Officiel
  2. 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.
44
répondu Fermin Yang 2015-11-27 21:27:40

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.

26
répondu sgress454 2014-05-04 19:16:27

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 ...)'.

3
répondu Glen Swift 2016-01-27 23:08:45
 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);
   }
);
3
répondu Aravind Kumar 2016-03-25 11:23:11

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;
);
3
répondu Jam Risser 2016-10-18 00:30:04

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);
            });
        });
2
répondu Mariusz Wiazowski 2016-05-17 08:46:40

à 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');
}
1
répondu har-wradim 2018-05-14 12:06:39