Getting data from MongoDB with Mongoose - node.js

I am trying to put a number of documents from a MongoDB collection into an array, using node.js&mongoose. Logging the userDoc in the _.each-loop works fine, but not appending them to an array.
What am I doing wrong?
My best guess is that I have misunderstood something regarding node's asynchronous design, but I have no idea on what I should change.
The code with comments:
returnObject.list = [];
Users.find({}, function (err, user){
_.each(user, function(userDoc){
console.log(userDoc); // Works
returnObject.list.push(userDoc); // No errors, but no users appended
});
});
console.log(returnObject); // No users here!
res.send(JSON.stringify(returnObject)); // Aint no users here either!

Ah this is a nice one, you're trying to do something in an synchronous style:
Users.find({}, function (err, user){
// here you are iterating through the users
// but you don't know when it will finish
});
// no users here because this gets called before any user
// is inserted into the array
console.log(returnObject);
Instead you should do something like this:
var callback = function (obj) {
console.log(obj);
}
Users.find({}, function (err, user){
var counter = user.length;
_.each(user, function(userDoc) {
if (counter) {
returnObject.list.push(userDoc);
// we decrease the counter until
// it's 0 and the callback gets called
counter--;
} else {
// since the counter is 0
// this means all the users have been inserted into the array
callback(returnObject);
}
});
});

do a util.inspect(user) to see what you have before the each loop.

Related

Mongoose manipulate documents in pre('save) when its called from create()

Hello guys I am trying to manipulate the documents that I am inserting to my collection with the create().Essentially I am calling a function that increments a letter field.
My pre hook is like
baseAttribute.pre('save',async function(next){
var att=this;
const query=await mongoose.models.BaseAttributes.find({},{},{sort:{_id:-1}}).limit(1)
console.log(query)
if(query.length===0)
{
att.code="AA"
}else{
att.code= Codes.GetAlphaCode(query[0].code);
}
next()
})
The result is that all the documents inserted by the create function are getting the same code
I found a solution to the problem.
// asyncs because I am not sure if it will cause a conflict with my async functions
var asyncs = require("async");
asyncs.eachOfSeries(newArray,function (item, i, next){
// console.log(item)
console.log("In each async")
// item.save(next);
BaseAttribute.find({},{},{sort:{_id:-1}}).limit(1).then(result=>{
console.log("In find")
if(!result[0]){
item.code="AA"
}else{
item.code=Codes.GetAlphaCode(result[0].code)
}
item.save(next);
})
}, function(err) {
if (err) return console.log(err);
res.json({done:"true"})
});
This is the way save documents one by one (in a serial order).

What is the best way to query mongodb with mongoose with an array of IDs?

I query one collection (messages) with mongoose. The result is an array of documents. Each document contains an ID for a different collection (users). Now I want to query the users collection for each ID from the messages collection.
The idea is to update each message object with the information from the user collection before returning it to the front end.
I tried using async.each. For some reason the final function is never called even though I am making sure the callback() function is called after each iteration.
app.get('/getmsg', function(req, res){
messages.find({query})
.exec(function(err, ms){
if(err) throw err;
async.each(ms, function(m, callback){
users.findOne({_id : m.userId})
.lean()
.exec(function(err, user){
if(err) {
console.log('error' , err);
callback();
} else {
m.userName = user.name;
// everything is working up to here
callback();
}
}), function(err){
res.send(ms); // this is never returned!
}
});
});
});
Is there a better way of achieving this? I assume this must be a common issue.
Thanks!
You can't use res.send. Instead create a function to get notified about it. Something like this.
// 1st para in async.each() is the array of items
async.each(items,
// 2nd param is the function that each item is passed to
function(item, callback){
// Call an asynchronous function, often a save() to DB
item.someAsyncCall(function (){
// Async call is done, alert via callback
callback();
});
},
// 3rd param is the function to call when everything's done
function(err){
// All tasks are done now
doSomethingOnceAllAreDone();
}
);

NodeJs - Nested Functions with a for loop which does not work properly inside

today my question is actually about the problem which happens due to loop which is not working properly but anyway, i am quite open to ideas if i have issues about designing.
app.post('/admin/earning/:multiplier', function (req, res) {
var multiplier = req.params.multiplier;
var now = new Date();
var List=[];
var dailySaving;
// Getting the investors emails(key) from the collection.
db.getInvestorsFromValidatedInvests(function(err,result){
if(err){console.log(err);}
console.log(result);
List = result;
console.log('Inside the List scope'+List[0]);
for(var i=0;i<List.length;i++){
db.returnTotalInvestingfromValidatedInvests(List[i],function(err,rst){
if(err){console.log(err);}
console.log(rst);
var dailySaving = ((rst*multiplier)/100);
console.log('Daily Savings which is going to be added is that : ' +dailySaving);
console.log(List[i]);
db.multiplyInvestAndAddSavings(List[i],dailySaving,function(err,rest){
if(err){console.log(err);}
console.log(rest);
});
});
}
});
res.json('Going to be Prepared...');
});
Here i am sharing you each of these database functions :
First function gets the owner e-mails from db and returns an array and then, i want to do some operations with each elements of this array and i decided to use for loop but it does not work properly.
Db.prototype.getInvestorsFromValidatedInvests = function(callback){
MongoClient.connect("mongodb://xxxxxxx:xxxxxxx#ds9999.mlab.com:99986/xxxxxx", function (err, db) {
if (err) { return console.dir(err); }
db.collection('validatedInvests').distinct("owner",function(err,result){
if(err){console.log(err); callback(err,null);}
callback(null,result);
});
db.close();
});
}
Db.prototype.returnTotalInvestingfromValidatedInvests = function(owner,callback){
MongoClient.connect("mongodb://xxxxxxxx:xxxxxxx#ds09999.mlab.com:9996/aslanservices", function (err, db) {
if (err) { return console.dir(err); }
db.collection('validatedInvests').find({"owner":owner}).toArray(function(err,result){
var sum=0;
if(err){console.log(err); callback(err,null);}
for(var j=0;j<result.length;j++){
sum+=parseInt(result[j].amount);
}
callback(null,sum);
});
db.close();
});
}
Db.prototype.multiplyInvestAndAddSavings = function(owner,amount,callback){
MongoClient.connect("mongodb://xxxxxx:xxxxxxx#ds99996.mlab.com:39996/aslanservices", function (err, db) {
if (err) { return console.dir(err); }
console.log('Db talking amount is'+amount);
db.collection('investClients').updateOne({"email":owner},
{
$inc : {
"savingsAccount" : +amount
},
},
{
upsert : true
}
,function(err,result){
if(err){callback(err,null);}
callback(null,result);
});
db.close();
});
}
The output is :
Inside the List Scope > lets say blablabla#gmail.com
220
Daily Savings which is going to be added is that : 8.8
undefined
150
Daily Savings which is going to be added is that : 6
undefined
Db talking amount is 8.8
Db talking amount is 6
Expected ouput is that :
220
Daily Savings which is going to be added is that : 8.8
blablabla#gmail.com
Db talking amount is 8.8
150
Daily Savings which is going to be added is that : 6
secondblablabla#gmail.com
Db talking amount is 6
The problem is that you're using a for loop with async functionality inside. Let me try to explain:
for(var i=0;i<List.length;i++){
// first iteration, the variable i is
}
now the first async function gets executed:
db.returnTotalInvestingfromValidatedInvests(List[i],function(err,rst){
/*this is an async callback which will be executed in a
different context in a later time, when the response is received*/
}
So what happens is the main execution where the for loop is being executed doesn't wait for the response (since async) from the above function, and moves forward incrementing the variable i executing the next iteration with your function db.returnTotalInvestingfromValidatedInvests and it onwards.
Very soon the whole for loop is ended whether or not all or any of your responses have arrived.
The reason you're getting undefined is because at the end of the loop the variable i has value of List.length which means that it is accessing List[list.length]. Which will always be undefined since the max index is always length-1.
Summary: You will have to keep a track of the element at List[i] somehow so that when the final (nested) function's async callback is triggered, you can update the correct element. Some people do that by creating hashes, some do it by converting their logic to synchronous in terms of loop.
Advice:
Try logging out i in each of the function callbacks to see the variable being changed quickly.
Hope the explanation helps :)
As requested, here's one way to do it:
function onInvestsDone(err, obj){
if(err!=null){
// something went wrong
}
else{
// do anything here with your final response
}
}
for(var i=0;i<List.length;i++){
returnInvests(List, i, onInvestsDone);
}
function returnInvests(listArray, index, cb){
db.returnTotalInvestingfromValidatedInvests(listArray[index],function(err,rst){
if(err){
console.log(err);
cb(err); // call callback with error
}
console.log(rst);
var dailySaving = ((rst*multiplier)/100);
console.log('Daily Savings which is going to be added is that : ' +dailySaving);
console.log(listArray[index]);
db.multiplyInvestAndAddSavings(listArray[index],dailySaving,function(err,rest){
if(err){
console.log(err);
cb(err); // call callback with error
}
console.log(rest);
// successfully done everything
cb(null, rest);
});
});
}

save updated models to mongodb

I have following code to fetch some data from the db (mongo).
function getAllUsers(){
var UsersPromise = Q.defer();
UserSchema.find({}, function(err, data){
if(err){
UsersPromise .reject(err);
}else {
UsersPromise .resolve(data);
}
});
return UsersPromise .promise;
}
Then I modify each of these models. I add certain fields to the model depending on the type of user. (This is working correctly).
function buildUsers(users){
// my code iterates over users and adds
// properties as required.
// Working fine.
return users; // updated users.
}
Now I want to save these updated models back to mongo and this is where it's making me pull my hair.
function saveUsers(users){
// here, the users are received correctly. But the following line to save the users fails.
var SaveUsersPromise = Q.defer();
UserSchema.save(users, function(err, data){
if(err){
SaveUsersPromise .reject(err);
} else {
SaveUsersPromise .resolve(data);
}
});
return SaveUsersPromise .promise;
}
Lastly I call these functions like:
DB.connect()
.then(getAllUsers)
.then(buildUsers)
.then(saveUsers)
.catch(errorHandler);
Everything works correctly untill I call UserSchema.save. What could be the problem?
PS: I am using mongoose.
TIA.
UserSchema.save accepts single instance, you have to loop through users and save each. Mongoose doesn't have bulk inserts implemented yet (see issue #723).
Here's simple implementation using async.eachSeries
function saveUsers(users){
var async = require('async'); // <== npm install async --save
var SaveUsersPromise = Q.defer();
async.eachSeries(users, function(user, done){
UserSchema.save(user, done);
// or
user.save(done); // if user is Mongoose-document object
}, function(err){
if(err){
SaveUsersPromise.reject(err);
} else {
SaveUsersPromise.resolve();
}
});
return SaveUsersPromise.promise;
}

Node JS + Express + Mongoose not filling array from findById

I need to edit a Game object by adding a User object to it. This is done by the mongoose query findById. This query works. But however, when I want to add the modified game to the resulting array, something goes wrong. The 'console.log(game);' returns the right output, but the 'console.log(result);' is always empty. What is going wrong? Is it something with inner functions?
var result = [];
games.forEach(function(game){
User.findById(game.user1_id, function(err, user){
if(err)
console.log(err);
game.user1 = user;
result.push(game);
console.log(game);
});
});
console.log(result);
You have run into a class callback problem. When you call forEach on games the code will actually continue outside the callback and the result will therefore be the value you first assigned to it, which is []. This is due to the code being evaluated asynchronous.
Solve this by moving your code into a function with a callback that is called when the loop is done, like this:
var utils = require('restberry-utils');
var getResult = function(next) {
var result = [];
utils.forEachAndDone(games, function(game, iter) {
User.findById(game.user1_id, function(err, user) {
if (err) console.log(err);
game.user1 = user;
result.push(game);
iter();
});
}, function() {
next(result);
});
};
getResult(function(result) {
console.log(result);
});
Notice I've imported the restberry-utils package and used the forEachAndDone method. This method will loop through the objects but won't continue unless you call the iter method. When it has looped through all the objects, the last callback is called which is where I'm returning the result.
Hope this makes sense.

Resources