Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how can run mongoose query in forEach loop

can anyone help me for how can run mongoose query in forEach loop in nodejs and suggest for inner join result need of both collections

like below details

userSchema.find({}, function(err, users) {
    if (err) throw err;
    users.forEach(function(u,i){
        var users = [];
        jobSchema.find({u_sno:s.u.sno}, function(err, j) {
            if (err) throw err;
            if (!u) {
                res.end(JSON.stringify({
                    status: 'failed:Auction not found.',
                    error_code: '404'
                }));
                console.log("User not found.");
                return 
            }
            users.push(j);
        })
    })
    res.send(JSON.stringify({status:"success",message:"successfully done",data:{jobs:j,users:u}}));
})
like image 358
lakshmankashyap Avatar asked Jan 19 '17 14:01

lakshmankashyap


3 Answers

A nice elegant solution is to use the cursor.eachAsync() function. Credit to https://thecodebarbarian.com/getting-started-with-async-iterators-in-node-js.

The eachAsync() function executes a (potentially async) function for each document that the cursor returns. If that function returns a promise, it will wait for that promise to resolve before getting the next document. This is the easiest way to exhaust a cursor in mongoose.

  // A cursor has a `.next()` function that returns a promise. The promise
  // will resolve to the next doc if there is one, or null if they are no
  // more results.
  const cursor = MyModel.find().sort({name: 1 }).cursor();

  let count = 0;
  console.log(new Date());
  await cursor.eachAsync(async function(doc) {
    // Wait 1 second before printing first doc, and 0.5 before printing 2nd
    await new Promise(resolve => setTimeout(() => resolve(), 1000 - 500 * (count++)));
    console.log(new Date(), doc);
  });
like image 52
pengz Avatar answered Oct 08 '22 06:10

pengz


Schema.find() is an async function. So your last line of code will execute while you wait for the first job search is executed in your loop. I suggest change it to Promises and use Promise.all(array).

To do so, first you have to change to use Promise with mongoose. you can do this with bluebird like this:

var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');

Then you can use Promises instead of callbacks like this:

userSchema.find({}).then(function(users) {
  var jobQueries = [];

  users.forEach(function(u) {
    jobQueries.push(jobSchema.find({u_sno:s.u.sno}));
  });

  return Promise.all(jobQueries );
}).then(function(listOfJobs) {
    res.send(listOfJobs);
}).catch(function(error) {
    res.status(500).send('one of the queries failed', error);
});

EDIT How to list both jobs and users

If you want to have a structure like:

[{ 
  user: { /* user object */,
  jobs: [ /* jobs */ ]
}]

you could merge the lists together. listOfJobs is in the same order as the jobQueries list, so they are in the same order as the users. Save users to a shared scope to get access to the list in the 'then function' and then merge.

..
}).then(function(listOfJobs) {
  var results = [];

  for (var i = 0; i < listOfJobs.length; i++) {
    results.push({
      user: users[i],
      jobs: listOfJobs[i]
    });
  }

  res.send(results);
}).catch(function(error) {
  res.status(500).send('one of the queries failed', error);
});
like image 14
R. Gulbrandsen Avatar answered Oct 08 '22 06:10

R. Gulbrandsen


No need to use forEach() which is synchronous and being called in an asynchronous fashion, that will give you wrong results.

You can use the aggregation framework and use $lookup which performs a left outer join to another collection in the same database to filter in documents from the "joined" collection for processing.

So the same query can be done using a single aggregation pipeline as:

userSchema.aggregate([
    {
        "$lookup": {
            "from": "jobs", /* underlying collection for jobSchema */
            "localField": "sno",
            "foreignField": "u_sno",
            "as": "jobs"
        }
    }
]).exec(function(err, docs){
    if (err) throw err;
    res.send(
        JSON.stringify({
            status: "success",
            message: "successfully done",
            data: docs
        })
    );
})
like image 2
chridam Avatar answered Oct 08 '22 07:10

chridam