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);
} 
2
  • are you using mongodb-native-client? Commented May 16, 2014 at 14:36
  • I'm using mongojs (github.com/mafintosh/mongojs), which wraps it Commented May 16, 2014 at 15:24

2 Answers 2

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

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

Comments

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

4 Comments

Sorry. My res.json is outside the for loop. I did something wrong when changing the fields names. I edited the code now.
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.
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)
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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.