Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I run multiple queries in sailsjs controller?

Tags:

orm

sails.js

It seems in sailsjs you can only run and pass one set of query data at a time. For example here is the controller for my homepage:

     module.exports = {

       index: function (req, res) {

        Blog.find()
        .limit(3)
        .sort('createdAt desc')
        .where({ isPublished: 1 })
        .exec(function(err, posts) {
          if (err) return next(err);
          res.view({
            layout: "homeLayout",
            posts:posts
          });    
        });  
      }

    };

How would I query data from some other model and pass it to my view along with the blog data Im already passing?

like image 962
Kory Avatar asked Nov 11 '13 18:11

Kory


3 Answers

You can use Promises to do so. It's actually an excellent usecase. I use Q, which is what Waterline (Sail's ORM) use behind the scene.

You can see below an example of code where I retrieve data from a first model, and then, using the data I retrieved, I query other models to get some more data (in parallel), and in the end, I send the result back to the view.

      SomeModel.findOne(criterias).then(function(result) {
        Q.all([
          SomeOtherModel.getSomething(result),
          YetAnotherModel.getSomethingElse(result)
        ]).spread(function(someOtherResult, yetAnotherResult) {
          var data = {
            thing: result,
            stuff: someOtherResult,
            otherthing: yetAnotherResult
          };
          return res.view(data);
        });
      }).fail(function(reason) {
        return res.view(reason);
      });

The getSomething() function should return a promise, standard finder from Sails will work transparently (just don't pass the callback). As per this other question it appears that standard finder do not behave exactly like Q promises, the answer I gave there should help get a more consistant behavior.

More on Q and how it works in the doc !

like image 108
Jérémie Parker Avatar answered Nov 09 '22 17:11

Jérémie Parker


You could also use async.auto (see below). Here's a link to the complete sails repo example.

var async = require('async'),
    _ = require('lodash');

module.exports = {


    index: function (req, res) {

        async.auto({

            // Get the blog posts
            posts: function (cb) {
                Blog.find()
                    .where({ isPublished: 1 })
                    .limit(5)
                    .sort('createdAt DESC')
                    .exec(cb);
            },


            // Get some more stuff
            // (this will happen AT THE SAME TIME as `posts` above)
            otherThings: function (cb) {
                OtherThing.find()
                    .limit(30)
                    .exec(cb);
            },


            // Get comments
            // (we'll wait until `posts` is finished first)
            comments: ['posts', function (cb, async_data) {

                // Get `posts`
                // (the second argument to cb() back in `posts`)
                // Used map to make sure posts are an array of ids and not just an object. 
                var posts = async_data.posts.map(function (item){ return item.id});

                // Get comments that whose `post_id` is equal to 
                // the id of one of the posts we found earlier
                Comment.find()
                    .where({ post_id: posts })
                    .exec(cb);
            }]

        },
        function allDone (err, async_data) {

            // If an error is passed as the first argument to cb
            // in any of the functions above, then the async block
            // will break, and this function will be called.
            if (err) return res.serverError(err);

            var posts = async_data.posts;
            var comments = async_data.comments;

            var otherThings = async_data.otherThings;

            // Fold the comments into the appropriate post
            // An in-memory join
            _.map(posts, function (post) {
                var theseComments =
                    _.where(comments, { post_id: post.id });
                post.comments = theseComments;

            });

            // Show a view using our data
            res.json({
                // layout: 'homeLayout',
                posts: posts,
                otherThings: otherThings
            });
        });

    }
};
like image 9
JohnGalt Avatar answered Nov 09 '22 17:11

JohnGalt


I have figured out a few ways to accomplish this. The first way is to nest your queries, eg.

Blog.find()
  .limit(30)
  .sort('createdAt desc')
  .where({ isPublished: 1 })
  .exec(function(err, posts) {

        SomeOtherModel.find()
        .limit(5)
        .sort('createdAt desc')
        .where({ isPublished: 1 })
        .exec(function(err, otherdata) {

          res.view({
            posts: posts,
            otherdata: otherdata
          });

      });

}); 

The second way is to use promises (I wasnt aware of this previously)

 User.findOne()
.where({ id: 2 })
.then(function(user){
    var comments = Comment.find({userId: user.id}).then(function(comments){
        return comments;
    });
    return [user.id, user.friendsList, comments];
}).spread(function(userId, friendsList, comments){
    // Promises are awesome!
}).fail(function(err){
    // An error occured
})  

The third way (I ended up going with this) is to create a policy (specific to sailsjs but is express middleware)

 // saved as /api/policies/recentPosts.js
 // also need to add a rule to /config/policies.js
 module.exports = function (req, res, ok) {

       Blog.find()
        .limit(3)
        .sort('createdAt desc')
        .where({ isPublished: 1 })
        .exec(function(err, footerposts) {

            res.footerposts = footerposts;
            return ok();
      });           
 };

Doing it this way you dont need to pass anything to your view however Im not sure if its good practice to randomly add data to the response object.

like image 8
Kory Avatar answered Nov 09 '22 17:11

Kory