3

How to write multiple queries in a row?

Like a

Space.findOne({ _id: id }, function(err, space) {
    User.findOne({ user_id: userid }, function(err, user) {
        res.json({ space: space, user: user});
    });
});

does not look good with more requests and logic

How is it done correctly?

I heard something about the promise, but I do not know.

Thanks

1
  • Would you like the functions to execute asynchronously of each other or together like in your sample code? Commented May 21, 2013 at 1:55

3 Answers 3

7

When I've had a similar issue, I've used the async library.

async.parallel([
    function(callback){
       Space.findOne({ _id: id }, callback);
    },
    function(callback){
        User.findOne({ user_id: userid },callback);
    }
],
function(err, results){
    res.json({space:results[0],user:results[1]});
});

You can also use async.series if you want sequential execution.

Sign up to request clarification or add additional context in comments.

2 Comments

Note, this code can be made shorter with usage of .bind but I'm avoiding that for clarity.
Note 2015, two years later - I'd do this with promises, they're a much more elegant solution for this problem. If anyone cares leave a comment and I'll add an answer with promises.
3

@Benjamin's approach is correct. Mongoose also provides the populate method, which fetches multiple objects that are related to each other by their id. This also happens in parallel and is a special case of multiple queries. See http://mongoosejs.com/docs/populate.html for more examples.

Comments

3

If you're using Node.js 4+ which supports Promises, you can wrap the query in a promise.

queryPromise = function(findQueryCursor) {
  return new Promise(function(resolve, reject) {
    findQueryCursor.toArray(function(err, data) {
      resolve(data);
    });
  });
};

Then, create an array of Promises consisting of queries:

promiseAr = [];
promiseAr.push(
  queryPromise(
    db.collection('dbname').find(query1)
  )
);
promiseAr.push(
  queryPromise(
    db.collection('dbname').find(query2)
  )
);

Then call

Promise.all(promiseAr)
.then(function(dataArray) {
   // results of query1 in dataArray[0]
   // results of query2 in dataArray[1]
})
.catch(function(err) {
   // catch errors
});

The queries will be sent in parallel to MongoDB, and the ".then" function will be called after all the queries have completed.

The reason you have to do this is because the .find() function returns a cursor, and then you have to call .toArray() to retrieve the data. .toArray() itself is asynchronous, so you have to wrap it in a Promise if you want to use Promise.all.

3 Comments

awesome code. Spent half hour looking for the answer and this is the only one updated. This should be upvoted to the top. Thanks!
should I use one connection instance for all the promises or one instance for each promise? when I create a connection instance in promise, I get error response "MongoClient must be connected before calling MongoClient.prototype.db"
I don't really deal with Mongo any more. My guess is that you need to wait for the connection to return first, before sending your queries. Wrap the connection in a Promise, then do connectPromise().then(result) ... and then send your queries. If you're using ES6, you can use the async / await syntax which some people find more straightforward. It's the same as a Promise with cleaner syntax.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.