Sails.js populate nested associations

sails populate select
associations in sails
sails js in
sails sort populate
sails js pagination
sails js distinct
sails js mongoose
sails js projection

I've got myself a question regarding associations in Sails.js version 0.10-rc5. I've been building an app in which multiple models are associated to one another, and I've arrived at a point where I need to get to nest associations somehow.

There's three parts:

First there's something like a blog post, that's being written by a user. In the blog post I want to show the associated user's information like their username. Now, everything works fine here. Until the next step: I'm trying to show comments which are associated with the post.

The comments are a separate Model, called Comment. Each of which also has an author (user) associated with it. I can easily show a list of the Comments, although when I want to display the User's information associated with the comment, I can't figure out how to populate the Comment with the user's information.

In my controller i'm trying to do something like this:

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.
  });

In my Post's 'show' action i'm trying to retrieve the information like this (simplified):

<ul> 
  <%- _.each(post.comments, function(comment) { %>
    <li>
      <%= comment.user.name %>
      <%= comment.description %>
    </li>
  <% }); %>
</ul>

The comment.user.name will be undefined though. If I try to just access the 'user' property, like comment.user, it'll show it's ID. Which tells me it's not automatically populating the user's information to the comment when I associate the comment with another model.

Anyone any ideals to solve this properly :)?

Thanks in advance!

P.S.

For clarification, this is how i've basically set up the associations in different models:

// 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'
}

Or you can use the built-in Blue Bird Promise feature to make it. (Working on Sails@v0.10.5)

See the codes below:

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);
  });

Some explanation:

  1. I'm using the Lo-Dash to deal with the arrays. For more details, please refer to the Official Doc
  2. Notice the return values inside the first "then" function, those objects "[post, commentUsers]" inside the array are also "promise" objects. Which means that they didn't contain the value data when they first been executed, until they got the value. So that "spread" function will wait the acture value come and continue doing the rest stuffs.

populate(), The name of the association to populate. e.g. snacks . 2, subcriteria, Dictionary? Optional. When populating collection associations between two models which  association: The name of the association to populate. e.g. snacks. 2: subcriteria: Optional. When populating collection associations between two models which reside in the same database, a Waterline criteria may be specified as a second argument to populate. This will be used for filtering, sorting, and limiting the array of associated records (e.g. snacks) associated with each primary record.

At the moment, there's no built in way to populate nested associations. Your best bet is to use async to do a 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);
   }
);

It's not as pretty as nested population (which is in the works, but probably not for v0.10), but on the bright side it's actually fairly efficient.

Sails.js populate nested associations. See https://stackoverflow.com , I understand that there is no built-in way in Sails.js/Waterline of populating deep nested associations yet, so I am trying to use bluebird  Sails.js populate nested associations I've got myself a question regarding associations in Sails.js version 0.10-rc5. I've been building an app in which multiple models are associated to one another, and I've arrived at a point where I need to get to nest associations somehow.

I created an NPM module for this called nested-pop. You can find it at the link below.

https://www.npmjs.com/package/nested-pop

Use it in the following way.

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;
);

Sails.js/Waterline populate deep nested association, I've got myself a question regarding associations in Sails.js version 0.10-rc5. I've been building an app in which multiple models are associated to one another,  Clone via HTTPS Clone with Git or checkout with SVN using the repository’s web address.

Worth saying there's a pull request to add nested population: https://github.com/balderdashy/waterline/pull/1052

Pull request isn't merged at the moment but you can use it installing one directly with

npm i Atlantis-Software/waterline#deepPopulate

With it you can do something like .populate('user.comments ...)'.

Sails.js populate nested associations - node.js - html, I understand that there is no built-in way in Sails.js/Waterline of populating deep nested associations, so I am trying to use bluebird promises to  aggregate in nested ng-repeat; Aggregate nested array in Mongodb; Sails.js : Waterline OR filter not working; Waterline find model where RegExp; Associations with combination foreign key in Sails.js/Waterline; mongodb aggregate nested nested array; Query in Waterline using a model as a criteria; Sails.js populate nested associations

 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);
   }
);

Populate nested one-to-many associations, I've got myself a question regarding associations in Sails.js version 0.10-rc5. I've been building an app in which multiple models are associated  Instead, you declare which associations to retrieve by using the .populate() method: // Find a single user, including its pets var userWithPets = await User.findOne(123).populate('pets'); How an association attribute is represented in a returned record depends on the type of association, whether there are actual records linked, and whether .populate() is chained to the query.

Sails.js populate nested associations, Sails.js populate nested associations. I've got myself a question regarding associations in Sails.js version 0.10-rc5. I've been building an app in which multiple  In a one-to-many association, the "one" side must have a collection attribute, and the "many" side must contain a model attribute. This allows the "many" side to know which records it needs to get when populate is used. Because you may want a model to have multiple one-to-many associations on another model, a via key is needed on the collection

Sails.js populate nested associations, I've got myself a question regarding associations in Sails.js version 0.10-rc5. I've been building an app in which multiple models are associated  association: The name of the association. e.g. 'cashier' (in GET /purchase/47/cashier) or 'products' (in GET /purchase/47/products) where: Instead of filtering based on a specific attribute, you may instead choose to provide a where parameter with the WHERE piece of a Waterline criteria, encoded as a JSON string.

nested-pop, 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.

Comments
  • Could you give some explanation of this line: _.pluck(results.post.comments, 'user')? I don't see where results is ever declared.
  • I just had a look at some of your other answers. I can tell you're knowledgeable but I'd ask that you elaborate a bit more. Don't just provide code, provide explained answers. Welcome to the community.
  • Thank you @colepanike for your kindly advice, I just fixed my code snippet to make it proper and added some explanation for it.
  • I followed your logic for a more complex deep association, everything seems alright but there is a problem when stringifying the JSON object, it is omitting some fields. Can you please take a look: stackoverflow.com/questions/25142731/… ?
  • How would you do this for many-to-many associations? In that case I won't even get the ids of the objects, just an undefined value. Is there no way to .populate() after you fetched an object?
  • Nice async.auto() usage.
  • Wouldn't async.waterfall be a better fit here?
  • Can anyone please help me on: stackoverflow.com/questions/29588582/…
  • Can I ask you to review How to offer personal open-source libraries? please?
  • @Jam Risser How can I use your NPM module to get 5 levels associated data?
  • Does it support for MSSQL adaptor ?
  • @RajAdroit don't know, I haven't been using waterline wuth mssql