I have an array, and for each row I need to do findIfExist and save into mongodb. The code is here:
router.post('/namespaceUpload', function(req, res,next) {
var data=req.body;
var totalRows=data.allRows.length;
var conceptObject ={};
var existingConcept;
for (var i=0;i<totalRows;i++){
async.series([
conceptPrepare,
conceptFind,
conceptSave,
], function (err, result) {
console.log('kraj');
res.json('Ok');
});
}
function conceptPrepare(callback){
conceptObject.name= data.allRows[i].name;
conceptObject.user= data.userId;
callback();
}
function conceptFind(callback){
namespaces.find({name: conceptObject.name}, function(err, result) {
if (err)
next(err);
else {
if (result.length==0){
console.log('0');
existingConcept='';
} else {
console.log(result.length);
existingConcept=result[0];
}
}
callback();
});
}
function conceptSave(callback){
var namespace = new namespaces();
if (existingConcept==''){
namespace.name=conceptObject.name;
namespace.description=conceptObject.description;
namespace.lastUpdate.user=conceptObject.user;
namespace.save(function(err) {
if (err)
return next(err);
callback();
})
}
}
So I Used async.series, but only last record is written in database as much times as many array members i have. Also, I get an error " Can't set headers after they are sent." Any idea?
You're getting the Can't set headers after they are sent error message because you 're not allowed to return smtg eg : res.send,res.render more than one time but in your for loop, it goes totalRows times
try to return one value at the end of the loop
Related
Apologies if I'm just being thick. I've tried the search function, but being relatively new to all of this, I'm struggling to work out the solution. I think I'm probably not searching for the right keyword.
I have a route in my Node.js application that has two forEach loops in it. I want forEach loop 1 to finish, then start forEach loop 2. When that finishes, I then want to call my res.redirect. Currently the route is going straight to res.redirect, and doesn't appear to be completing the forEach loops.
Code:
// Auto-populate entries
router.post("/populate", middlewareObj.isLoggedIn, function(req, res) {
var baseData = []
//lookup Plan using ID
Plan.findById(req.params.id, function(err, foundPlan) {
if (err) {
console.log(err);
res.redirect("/plans");
} else {
BaseData.find({
"contributingRegion": foundPlan.contributingRegion
}, function(err, foundRecords) {
foundRecords.forEach(function(record) {
baseData.push(record)
baseData.save
});
//Create entries & push into plan
baseData.forEach(function(data) {
if (includes(req.body.orgs, data.org)) {
Entry.create(data, function(err, entry) {
if (err) {
console.log(err);
} else {
entry.author.id = req.user._id;
entry.author.username = req.user.username;
entry.save();
foundPlan.planEntries.push(entry);
foundPlan.save();
}
})
}
})
res.redirect('/plans/' + foundPlan._id);
});
}
});
});
There are many ways to achieve this, for example you could use promises or async module, you could also use recurrent functions, I will provide a solution with the async module because it let you understand how asynchronous functions work and how to control them:
async.each( baseData, function (data, next) {
if (includes(req.body.orgs, data.org)) {
Entry.create(data, function(err, entry) {
if (err) {
// stop iterating and pass error to the last callback
next(err);
} else {
entry.author.id = req.user._id;
entry.author.username = req.user.username;
entry.save();
foundPlan.planEntries.push(entry);
foundPlan.save();
// next iteration
next();
}
});
} else {
// next iteration
next();
}
}, function (err) {
// This function runs when all iterations are done
if (err) throw err;
res.redirect('/plans/' + foundPlan._id);
} );
I have written a function like this
function create(data, res){
var history= new historyData({
owner: data
});
historyData.save(function(err, historyData) {
//To identify the error
if (err) {
console.log(err);
}
//If no error pass the team details to databsae
else {
return historyData.id;
}
});
}
and I'm trying to get the return value like this
var history = create("saddsa", res);
but it gives undefined error
I want to get the id of the created collection and pass it to another function?
Try to return a callback and get that value
function create(data, res, callback) {
var history = new historyData({
owner: data
});
historyData.save(function(err, historyData) {
//To identify the error
if (err) {
console.log(err);
callback(err)
}
//If no error pass the team details to databsae
else {
return callback(null,historyData.id);
}
});
}
// call your function like this
create("saddsa", res,function(err,result){
if(!err){
console.log(result)
}
});
replace
return historyData.id;
with
if(res) res(historyData.id);
and call
create("saddsa", function (id) { var history = id; });
I am mew to node js, I have something like this,
get_contacts(data, function(contacts) {
if (contacts.length) {
var count = contacts.length;
for (var i = 0; i < count; i++) {
result = {
id: contacts[i].id,
name: contacts[i].name,
sent1: get_sent(data.userId, contacts[i].id, function(resp) {
result.sent = resp.count;
}),
}
result1[i] = result;
}
output = {
contacts: result1,
}
} else {
output = {
error: "No Contacts.",
}
}
res.writeHead(200, {'content-type': 'text/html'});
res.end(JSON.stringify(output));
});
get_contacts is a callback function which will return contact list.result1 & result are objects. Now value for sent should come from a function get_sent, and get sent is like this
function get_sent(userId, contactId, callback) {
pool.getConnection(function(err, connection) {
connection.query("my query here", function(err, rows) {
connection.release();
if (!err) {
callback(rows);
} else {
console.log(err)
}
});
});
}
But im not getting any value since nodejs. since nodejs is async it is not waiting for the function to return value. I know, im doing it in wrong way. Please help
You need to use a callback. In simple words is a function that you'll execute after something happens. You should read more about that. You should get a book about javascript but you can start reading here for example.
About your case, you could solve it like this
//Asumming that you object `result` is global.
result = {
id: contacts[i].id,
name: contacts[i].name,
sent: -1 //Some default value
}
//Just to put the code into a function, you have to put it where you need
function constructObject (){
get_sent(uId, cId, function(err, total){
if(err){
console.log("Something was wrong.", err);
}
result.sent = total;
//Here you have your object completed
console.log(result);
});
}
//You need to use a callback
function get_sent(uId, cId, callback) {
pool.getConnection(function(err, connection) {
//Note that I add an alias here
connection.query("SELECT count(*) as total FROM table_name", function(err, rows) {
connection.release();
if (!err) {
//I am returning the result of the query and a null error
callback(err, rows[0].total);
} else {
console.log(err);
//I am returning an error
callback(err);
}
});
});
}
//For example you could call this function here
constructObject();
And it depends of what are you doing exactly but Maybe you need a callback on your constructObject too.
With this as a URL:
'api/support-tag/name/myTagName'
This function works properly:
getByName: function (req, res) {
model.Shared_SupportTag.findOne({name: req.params.name}).exec(function (err, results) {
if (err) {
return res.status(400).send({
message: errMsg.Util_ErrorMsg.getErrorMessage(err)
});
}
res.send(results);
})
}
But when I try to call a similar function from within the node server:
supportDoc.category = GetById(item.category);
function GetById(name){
model.Shared_SupportTag.findOne({name: name}).exec(function(err, result){
if(err){
console.log(err)
}else{
console.log(result);
}
})
}
The function does not execute, nor does the error catch, intellisense shows:
err= Reference error; err is not defined
result = Reference error; result is not defined
All I am trying to accomplish is a function call from within the server and not via a URL.
Any solution here? Thanks in advance
In the case of the findOne() method, the positive response (sans error) will either hold a mongoose object or null.
If the same query had been sent using just find(), the result would have been an empty array.
function GetById(name){
model.Shared_SupportTag.findOne({name: name}).exec(function(err, result){
if(err){
console.log(err)
}else{
if (result) console.log(result); //Check whether object exists.
else console.log('Not found!');
}
})
}
Solved:
model.Shared_SupportDoc.find({}).exec(function (err, collection) {
var supportDocs = require('../../data/_seed/support/supportDocs.json');
if (collection.length === 0) {
supportDocs.forEach(function (item) {
var supportDoc = new model.Shared_SupportDoc;
supportDoc.title = item.title;
supportDoc.created = item.date;
supportDoc.icon = item.icon;
supportDoc.likeCount = item.likeCount || 7;
-----> // requires callback - ie asynchronous
GetByName(item.category, function(tagId) {
supportDoc.categoryId = tagId;
-----> // must put save in the callback
supportDoc.save(function (err) {
if (err) {
console.log(supportDoc.categoryId)
console.log('Error: ' + err);
} else {
console.log('Support Doc Seed Complete');
}
});
});
})
}
});}
function GetByName(name, next) {
model.Shared_SupportTag.findOne({name : name}).exec(function (err, result) {
if (!result) {
console.log('Not Found');
next();
} else {
console.log(result._id);
next(result._id);
}
});}
Assuming I have the following in a function:
exports.addnames = function(req, res) {
var names = ["Kelley", "Amy", "Mark"];
for(var i = 0; i < names.length; i++) {
(function (name_now) {
Person.findOne({ name: name_now},
function(err, doc) {
if(!err && !doc) {
var personDoc = new PersonDoc();
personDoc.name = name_now;
console.log(personDoc.name);
personDoc.save(function(err) {});
} else if(!err) {
console.log("Person is in the system");
} else {
console.log("ERROR: " + err);
}
}
);
)(names[i]);
}
My issue is after I save the names, I want to return the results:
Person.find({}, function(err, doc) {
res.json(200, doc);
})
Though I have a callback for names, it appears that the last block of code (Persons.find({})) gets executed before the calls to save all the names is complete... thusly when the user goes to the url in the browser, "doc" is empty... Is there some way I can ensure that the Persons.find({}) is called after the for loop completes?
The easiest way to do things like this is to use an async library like the aptly named async which can be found at https://github.com/caolan/async.
If you have a list of names that you want to save and then return when complete, it would look like:
// save each of the names asynchronously
async.forEach(names, function(name, done) {
Person.findOne({name: name},
function(err, doc) {
// return immediately if there was an error
if(err) return done(err);
// save the person if it doesn't already exist
if(!doc) {
var personDoc = new PersonDoc();
personDoc.name = name;
console.log(personDoc.name);
// the async call is complete after the save completes
return personDoc.save(done);
}
// or if the name is already there, just return successfully
console.log("Person is in the system");
done();
}
);
},
// this function is called after all of the names have been saved
// or as soon as an error occurs
function(err) {
if(err) return console.log('ERROR: ' + err);
Person.find({}, function(err, doc) {
res.json(200, doc);
})
});