I am new in nodejs and I want to make a json array by comparing id inside a loop but the MongoDB function does not wait for the loop to complete, so data is not coming out properly. It displays the data before the loop ends, below is the code:
router.get('/getallcountrydataup',function(req, res) {
Country
.where('isDeleted').equals(false)
.exec(function(err,cData){
if (!cData) {
res.json({'status':0,'message': 'No data found','data':[]});
} else{
async.waterfall([
function (done) {
var countryalldata = [];
for (var i = 0; i < cData.length; i++) {
var country_s = cData[i];
State.where('s_c_id').equals(country_s._id)
.exec(function(err, statedata){
country_s.statecount = statedata.length;
//console.log(country_s._id);
console.log(country_s.statecount);
});
countryalldata.push(country_s);
}
done(err, countryalldata);
// console.log(countryalldata);
},
function (countryalldata, done) {
console.log(countryalldata);
res.json({
'status': 1,
'message': 'Data found',
'data': countryalldata
});
}
]);
}
});
});
Here is output of the countryalldata variable printed before the loop will complete. I want to print its output after loop execution is complete.
State.where is asynchronous so it should be synchronized. For example with async.map
router.get('/getallcountrydataup',function(req, res) {
Country
.where('isDeleted').equals(false)
.exec(function(err,cData){
if (!cData) {
res.json({'status':0,'message': 'No data found','data':[]});
} else {
async.waterfall([
function (done) {
async.map(cData, function (country_s, done2) {
// Convert country_s into plain object. It's not a mongoose
// model anymore
country_s = country_s.toObject();
State.where('s_c_id')
.equals(country_s._id)
.exec(function(err,statedata){
if (err) {
done2(err);
return;
}
country_s.statecount = statedata.length;
console.log(country_s.statecount);
done2(null, country_s)
});
}, function(err, countryalldata) {
if (err) {
done(err);
}
else {
done(null, countryalldata)
}
});
},
function (countryalldata, done) {
console.log(countryalldata);
res.json({'status':1,'message': 'Data found','data':countryalldata});
}
]);
}
});
});
Related
In the code below, when I run in debug mode with a break-point at this line: content.push(data.Body.toString()); I can see that data is inserted to the content array.
However when I run the code normally, content comes back empty.
How can I get it to populate the array for downstream use?
var params = { Bucket: "thebucket", Prefix: "theprefix/" }
var content = [];
function getS3Data()
{
var s3 = new aws.S3();
s3.listObjects(params, function (err, data)
{
if (err) throw err; // an error occurred
else
{
var i;
for (i = 0; i < data.Contents.length; i++)
{
var currentValue = data.Contents[i];
if(currentValue.Key.endsWith(params.Prefix) == false)
{
var goParams = { Bucket: params.Bucket, Key: currentValue.Key };
s3.getObject(goParams, function(err, data)
{
if (err) throw err; //error
content.push(data.Body.toString());
});
};
};
}//else
});//listObjects
}//getS3Data
getS3Data();
console.log(content); //prints empty here when run in non-debug.
The line:
console.log(content)
is being executed before the line:
content.push(data.Body.toString());
the function you are passing as a 2nd argument to s3.listObjects will be executed asynchronously. If you want to log out content you need to do it within the callback function meaning:
s3.listObjects(params, function (err, data) {
if (err) throw err;
else {
// ...
console.log(content)
}
});
A better approach would be to implement getS3Data with Promise so you can run code after the object listing is done for sure.
function getS3Data() {
return new Promise((resolve, reject) => {
if (err) {
reject(err)
} else {
const promises = []
for (const i = 0; i < data.Contents.length; i++) {
const currentValue = data.Contents[i];
if (currentValue.Key.endsWith(params.Prefix) == false) {
const goParams = { Bucket: params.Bucket, Key: currentValue.Key };
promises.push(new Promise((res, rej) => {
s3.getObject(goParams, function (err, data) {
if (err) {
rej(err); //error
} else {
res(data.Body.toString());
}
});
}));
}
}
Promise.all(promises).then(resolve);
}
});
}
getS3Data()
.then(result => { // this will actually be `content` from your code example
console.log(result);
}).catch(error => {
console.error(error);
})
Node.js' documentation has an example very similar to the problem you are experiencing:
Dangers of Mixing Blocking and Non-Blocking Code
The issue arises because the variable content is not set as soon as getS3Data has finished, because it is an asynchronous function. content will be set some time later. But your call to console.log(content); will execute immediately after getS3Data has finished, so at that point content has not been set yet.
You can test that by adding an extra log:
s3.getObject(goParams, function(err, data)
{
if (err) throw err; //error
content.push(data.Body.toString());
console.log("Content has been assigned");
});
And then change the bottom to:
getS3Data();
console.log("getS3Data has finished", content);
It's likely you'll get the messages in this order:
getS3Data has finished
Content has been assigned
See my code below.
exports.myexports = (req, res) => {
var arrayname = new Array();
Hello.find({},function(error,fetchAllHellos)
{
if(fetchAllHellos)
{
async.eachSeries(fetchAllHellos, function(Hello, callback)
{
var hArr = {};
var image = {};
hArr['_id'] = Hello._id;
hArr['myname'] = Hello.name;
/* Use asyn Parallel method for waiting those functions value */
async.parallel
(
[
function(callback)
{
fetchingDetails(Hello._id, function(err, fetchAllDetails)
{
bArr['address'] = fetchAllDetails;
async.eachSeries(fetchAllDetails, function(fetchAllDetails, callback)
{
async.parallel
(
[
function(callback)
{
fetchingMyImage(fetchAllDetails._id, function(err, wer)
{
image[fetchAllDetails._id] = wer;
callback(err); //Forgot to add
})
}
],
function(err)
{
//console.log(image);
arrayname.push(image);
//bArr['image'] = image
callback(err);
}
);
});
callback(err); //Forgot to add
});
}
],
function(err)
{
arrayname.push(hArr);
callback(err);
}
)
},
function(err)
{
console.log(arrayname); //This should give you desired result
});
}
else
{
return res.json({"status":'error'})
}
});
};
function fetchingMyImage(mid, callback)
{
UserImage.find({myid:mid},function(error,fetchallImages)
{
callback(error,fetchallImages);
});
}
I want like this array
user
[
id = 'lkjlk',
myname = 'helloname'
address = [
object,
]
image = [
myid = image.png
]
]
Made changes in your code. Let me know if it helps you.
Please go through the code and let me know, whether you understood the changes or not.
exports.myexports = (req, res) => {
var arrayname = new Array();
Hello.find({},function(error,fetchAllHellos)
{
if(fetchAllHellos)
{
async.eachSeries(fetchAllHellos, function(Hello, callback)
{
var hArr = {};
var image = [];
hArr['_id'] = Hello._id;
hArr['myname'] = Hello.name;
//Read doc before you can start using.
async.parallel
([
function(callback)
{
fetchingDetails(Hello._id, function(err, fetchAllDetails)
{
bArr['address'] = fetchAllDetails;
async.eachSeries(fetchAllDetails, function(eachDetail, callback)
{
fetchingMyImage(eachDetail._id, function(err, wer)
{
image.push({eachDetail._id : wer;
callback(err);
})
}, function(err)
{
console.log(image);
arrayname.push({images :image});
callback(err);
});
});
}
],
function(err)
{
arrayname.push(hArr);
callback(err);
})
},
function(err)
{
console.log(arrayname); //This should give you desired result
console.log(arrayname.images)// Array of images will be consoled here.
//callback(err);
});
}
else
{
return res.json({"status":'error'})
}
});
};
function fetchingMyImage(mid, callback)
{
UserImage.find({myid:mid},function(error,fetchallImages)
{
callback(error,fetchallImages);
});
}
I have an emails object that contains an array in a mongodb database. However, when I try to use $set to make the array empty it doesn't work. How am I supposed to clear the array?
exports.clearEmails = function(req, res, next) {
var listId = req.params.id;
var errors = req.validationErrors();
if (errors) {
return res.status(400).send(errors);
}
EmailList.update({'_id': listId}, {$set: {'emails': []}}, function(err,results) {
if (err) {
return res.status(400).send(err);
} else {
return res.status(200).send(results);
}
});
}
I'am using below code to get data from a collection
Marketing.find({
shopId: req.params.shopId,
locationId: req.params.locationId,
}).exec(function (err, campaigns) {
if (err) {
return next(err);
} else if (!campaigns) {
return next(new Error('Failed to load Campaigns '+ req.params.shopId));
}
I want to make a api call on campaigns object i use the below code
campaigns.forEach(function(item) {
async.waterfall([
function (done) {
item.opens = "-";
item.requests = "-";
var currentDate = new Date();
var formatedDate = currentDate.toISOString().slice(0,10);
var request = sg.emptyRequest();
request.queryParams.aggregated_by = 'day';
request.queryParams.limit = '1';
request.queryParams.start_date = '2016-01-01';
request.queryParams.end_date = formatedDate;
request.queryParams.offset = '1';
request.queryParams.categories = item._id;
request.method = 'GET';
request.path = '/v3/categories/stats';
sg.API(request, function (response) {
response.body = JSON.parse(response.body);
done(err,response.body)
});
},
function (data,done) {
for(var i=0;i<data.length;i++){
unique_opens = parseInt(unique_opens)+parseInt(data[i].stats[0].metrics.unique_opens);
opens = parseInt(opens)+parseInt(data[i].stats[0].metrics.opens);
requests = parseInt(requests)+parseInt(data[i].stats[0].metrics.requests);
}
if(unique_opens>=1 && requests>=1){
item.clickrate = (unique_opens/opens)*100;
}
else{
item.clickrate = 0;
}
item.opens = opens;
item.requests = requests;
console.log(item.opens);
opens = 0;
unique_opens = 0;
requests = 0;
console.log(item);
},
], function (error) {
if (error) {
//handle readFile error or processFile error here
}
});
});
And at the end i do
res.json(campaigns);
But it doesn't add the two new keys in each index [ opens & request ]
Use map() as follows:
Marketing.find({
shopId: req.params.shopId,
locationId: req.params.locationId,
}).lean().exec(function (err, campaigns) {
if (err) {
return next(err);
} else if (!campaigns) {
return next(new Error('Failed to load Campaigns '+ req.params.shopId));
}
campaigns = campaigns.map(function(item){
return {
opens: 'SOMELOGIC',
requests: 'SOMELOGIC',
opens: item.opens,
requests: item.requests,
};
});
res.json(campaigns);
}
I want to return a boolean value from middleware defined as
module.exports = {
authenticatepracticename: function(pname) {
ecollection.find({ $and: [{'name':pname},{'status' : 'active'}] }).exec(function (err, result) {
if (err) return false;
if(result.length == 1){
// console.log('true');
return true;
}
else{
// console.log('false');
return false;
}
});
},
// ...
}
to my express controller defined as
exports.checkcredentails = function (req, res) {
var result = practice.authenticatepracticename(practiceName);
}
but result is coming undefined even though middleware function is getting called.
The reason why you are getting undefined result from practice.authenticatepracticename is because ecollection.find performs asynchronous action and authenticatepracticename end without returning any value (which is undefined in JavaScript).
In order to improve that, you would need to provide a callback function from your checkcredentails to authenticatepracticename.
Example:
exports.checkcredentails = function (req, res) {
practice.authenticatepracticename(practiceName, function(err, result){
// you can handle error and result here e.g. by sending them back to a customer
res.send("Result: " + result);
});
}
And your authenticatepracticename:
authenticatepracticename: function(pname, cb) {
ecollection.find({ $and: [{'name':pname},{'status' : 'active'}] }).exec(function (err, result) {
if (err) return cb(err)
if(result.length == 1){
// console.log('true');
cb(null, true)
}
else {
// console.log('false');
cb(null, false)
}
});
}
I hope that will help.