0

I need to make a MongoDB query and add an extra field to each one of my results. The field values depend of the result of another query.

I'm trying to call the nested query in my server.js code. It's not working because res.json(data) is called before data elements are modified. I suspect there's a way to do what I want using a nested query (or using something like forEach or aggregate) in my db.js code, but my lack of experience with JavaScript/Mongo didn't let me to find a good solution.

Any help would be appreciated.

server.js

app.get("/get_users_list", function(req, res) {
    db.getUsersByCity(req.query.city, function(err, data) {
        if (err) {
            res.send(err);
        } else {
            for(var i in data) {
                var rec = data[i];
                db.checkVisitation({'user_id': req.query.user_id, 'friend_id': rec.user_id},
                    function(inner_err, inner_data) {
                        if (inner_data) {
                            rec["visited"] = "true";
                        } else {
                            rec["visited"] = "false";
                        }            
                });
            }
            res.json(data);
        }
    });
});

db.js

 var checkVisitation = function(visitJSON, callback) {
    db.visitations.findOne(visitJSON, callback);
 }

var getUsersByCity = function(city, callback) {
    db.users.find({'city': city}).toArray(callback);
}

EDIT:

I was able to fix the problem by adding a check inside my inner callback:

if (i == data.length - 1) {
    res.json(data);
} 
Amaury Medeiros
  • 2,093
  • 4
  • 26
  • 42

2 Answers2

2

You need to call res.json only after you get all responses from db. Since requests are asynchronous you need to organize code to wait for them to finish. There are two widely adopted approaches to organize code with asynchronous calls:

  1. Use async.js https://github.com/caolan/async
  2. Use promises https://github.com/kriskowal/q

I recommend you to start with async since it is closer to current code

Andrei Beziazychnyi
  • 2,897
  • 2
  • 16
  • 11
0

You have res.json(data) inside your for loop, so it is going to send the data in the first increment of the for loop - not when the loop is finished.

You need a callback or check to make sure the for loop is finished before you do res.json(data);

also you should probably set data[i]["visited"] = "true" or false rather than setting rec, as rec is a separate variable that you are not returning

Kev Price
  • 799
  • 9
  • 21
  • Sorry. My res.json is outside the for loop. I did something wrong when changing the fields names. I edited the code now. – Amaury Medeiros May 16 '14 at 14:41
  • I edited my code to use data[i] instead of rec, but it didn't solve the problem. I believe checkVisitation's callback is asynchronous, so res.json(data) is called before checkVisitation is finished. – Amaury Medeiros May 16 '14 at 14:45
  • this will still fire the res.json(data) too early because of the asynchronous nature of node. unless you check for the for loop completing or provide a callback it will go into the else statement and do the loop as well as the res.json(data) – Kev Price May 16 '14 at 14:46
  • 1
    A hacky/quick way to do it would be with a loop counter that checked the iteration against the length of the array and if they matched then send the data. Someone else may be able to provide a more graceful approach – Kev Price May 16 '14 at 14:47