Mongoose promise in foreach failed in my case - javascript

How to do promise with forEach? I want to get all jobs, but get the data of applicants. My Job schema already have the applicant id, but how to query the user and merge their detail in the output?
Job.find({}).then(result => {
result.forEach(obj =>{
const applicant_id = obj.applicant._id
if(applicant_id){
User.findOne({_id: applicant_id})
.then(user=>{
return res.json({
status: 1,
data: {
...obj,
applicant: {
...user
}
}
})
})
}
})
}).catch(err => {
if(err){
return res.status(400).send({
msg: err
})
}
})
I tried Promise but I'm stuck merging user into the Job obj,
Job.find({}).then(result => {
let promiseArray = []
result.forEach(obj =>{
const applicant_id = obj.applicant._id
if(applicant_id){
promiseArray.push(
User.findOne({_id: applicant_id}))
}
})
return Promise.all(promiseArray)
}).then(user => {
console.log(user)
//this work but this is only the user's data,
//I need it to be within obj which is Job data
})

You first need to filter items in result to exclude those without applicant id, then map this array to array of promises, and finally pass it to Promise.all. This should do it:
Job.find({}).then(result => {
const promises = result
.filter(obj => obj.applicant._id)
.map(obj => {
const applicant_id = obj.applicant._id
return User.findOne({ _id: applicant_id })
.then(user => {
return res.json({
status: 1,
data: {
...obj,
applicant: {
...user
}
}
})
})
})
return Promise.all(promises)
}).catch(err => {
if (err) {
return res.status(400).send({
msg: err
})
}
})

Here's a tested and working solution:
Job.find({ applicant: { $ne: null } }).populate('applicant').then(result => {
res.send(result);
}).catch(err => {
return res.status(400).send({
msg: err
})
});

Related

Mongoose 'Query was already executed' error

As the name states, I keep getting a "Query was already executed" while running Mongoose.find queries. Using '.clone' does not seem to be fixing the issue...
My calling code is:
let result = mongo.isValidUsername(req.body.username).then((data) => {
return data;
});
if ((await result) == false) {
res.send("Sorry, that username is unavailable");
} else {
mongo
.addUser(
req.body.username,
req.body.password,
req.body.firstName,
req.body.lastName,
req.body.email,
req.body.phoneNumber
)
.then(() => {
let profileData = mongo.getProfileData(req.body.username);
profileData
.then((data) => {
res.render("accountDisplay", {
results: data,
trans: [9.99],
});
})
.catch((err) => {
console.log(err);
});
});
}
I call a query twice - Once in isValidUsername() at the beginning (where I have not used .clone) and then again in getProfileData( where I HAVE used .clone).
I keep getting this error. Any idea what could be causing it?
Here is the code for isValidUsername() and getProfileData(), just in case...
async function isValidUsername(usernameToQuery) {
//connect to mongoose database
mongoose.connect("mongodb://localhost:27017/bankDB");
try {
let isValid = UserModel.findOne({ username: usernameToQuery }).then(
(data) => {
if (data == null) {
return true;
} else {
return false;
}
}
);
return await isValid;
} catch (err) {
return err;
}
}
async function getProfileData(usernameToQuery) {
mongoose.connect("mongodb://localhost:27017/bankDB");
let profileData = UserModel.findOne({ username: usernameToQuery }).clone();
console.log(await profileData);
let profileArray = await profileData.then((data) => {
return [
data._doc.firstName,
data._doc.lastName,
data._doc.email,
data._doc.phoneNumber,
];
});
return await profileArray;
}

Passing the errors to the outermost return

I currently have a check I'm running to see if a username is taken or not. I am querying to see if the username has been taken, and if so provide a value to my errors object. I want to pass my errors defined within my if statement to the outer return statement. Is there a way to go about this?? Im unsure of what to do here.
exports.reduceUserDetails = data => {
let errors = {}
const userRef = db.collection('users').where('username', '==', data.username)
userRef.get().then(snapshot => {
if (!snapshot.empty) {
errors.username = 'username taken'
} else {
console.log('im not taken')
}
})
return {
errors,
valid: Object.keys(errors).length === 0 ? true : false
}
}
here is where I'm using the reduce user details:
exports.profileUpdate = (req, res) => {
let userDetails = req.body
const { valid, errors } = reduceUserDetails(userDetails)
if (!valid) return res.status(400).json(errors)
let document = db
.collection('users')
.where('username', '==', req.user.username)
document
.get()
.then(snapshot => {
snapshot.forEach(doc => {
const data = doc.id
db.collection('users').doc(data).update(req.body)
})
res.json({ message: 'Updated Successfully' })
})
.catch(error => {
console.error(error)
return res.status(400).json({
message: 'Cannot Update the value'
})
})
}
May be abstracting the call in a new function and awaiting it in caller might work, otherwise you will need to add await before reduceUserDetails() wherever you will call
exports.reduceUserDetails = async data => {
let check = await dupChk(data);
return {
check.errors,
valid: check.result
}
}
var dupChk = (data) => (
new Promise((resolve, reject) => {
let errors = {}
const userRef = db.collection('users').where('username', '==', data.username)
userRef.get().then(snapshot => {
if (!snapshot.empty) {
errors.username = 'username taken'
resolve({result:false,errors:errors})
} else {
resolve({result:true, errors: errors});//console.log('im not taken')
}
})
})
);
UPDATE:
Ok no need to do the above stuff just change the reduceUserDetails() like this
exports.reduceUserDetails = data => {
return new Promise((resolve, reject) => {
let errors = {}
const userRef = db.collection('users').where('username', '==', data.username)
userRef.get().then(snapshot => {
if (!snapshot.empty) {
errors.username = 'username taken'
resolve({valid:false,errors:errors})
} else {
resolve({valid:true, errors: errors});//console.log('im not taken')
}
})
.catch(()=>resolve({result:false,errors:errors}))
})
}
And in profileUpdate() add await keyword before the reduceUserDetails() call
const { valid, errors } = await reduceUserDetails(userDetails)

Query return as undefined using knex

I need to register a new user, when receiving the parameters make a query using the city name to get the state and city id (both are foreign keys). I implemented a function to find the ids. Inside the function using data.id the id is returned correctly. But at the time of insert in database is being inserted "undefined".
Apparently the save operation is being executed before the findCity and findState functions return the value.
execution flow
cidade = city, estado = city
module.exports = app => {
const obterHash = (senha, callback) => {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(senha, salt, null, (err, hash) => callback(hash))
})
}
var idCidade;
var idEstado
function findCidade(cidade, ) {
app.db('cidades')
.where({ nome: cidade })
.first()
.then(data => {
idCidade = data.id
console.log('inside findCity. data.id: '+data.id)
}).catch((err) => console.log("erro cidade", err));
return
}
function findEstado(uf) {
app.db('estados')
.where({ uf: uf })
.first()
.then(data => {
idEstado = data.id
console.log('inside findState. data.id: '+data.id)
}).catch((err) => console.log("erro estado", err));
}
const save = (req, res) => {
console.log("\n")
findCidade(req.body.cidade)
findEstado(req.body.uf)
obterHash(req.body.senha, hash => {
const senha = hash
console.log("Will be inserted. idCity: "+idCidade+" idState: "+idEstado)
app.db('salao')
.insert({ idcidade: idCidade,
idestado: idEstado,
senha})
.then(_ => res.status(200).send())
.catch(err =>{res.status(400).json(err)})
})
}
return { save }
}
I'm from Brazil and I'm using a translator, sorry for the spelling mistakes.
You are welcome to the asynchronous world!
General explanation: You are going to use results of a database querying before it will happen. Your program have to wait the results (idCidade, idEstado) before you can use it. Because of it you can find the record Will be inserted... first in your logs.
For the explanation I'm going to use Minimal Reproducible Example.
function findCidade(cidade) {
return Promise.resolve(1);
}
function findEstado(uf) {
return Promise.resolve(1);
}
Promise.all([findCidade(), findEstado()])
.then((data) => console.log(data));
The output is:
[ 1, 1 ]
To solve the issue you have to:
Return the promise explicitly with return statement.
Await the results by async/await or Promise interface methods. Or use callbacks if it is more suitable to you.
module.exports = app => {
const obterHash = (senha, callback) => {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(senha, salt, null, (err, hash) => callback(hash))
})
};
function findCidade(cidade, ) {
return app.db('cidades')
.where({ nome: cidade })
.first()
.then(data => {
idCidade = data.id
console.log('inside findCity. data.id: '+data.id)
}).catch((err) => console.log("erro cidade", err));
}
function findEstado(uf) {
return app.db('estados')
.where({ uf: uf })
.first()
.then(data => {
idEstado = data.id
console.log('inside findState. data.id: '+data.id)
}).catch((err) => console.log("erro estado", err));
}
const save = (req, res) => {
console.log("\n");
Promise.all([findCidade(req.body.cidade), findEstado(req.body.uf)])
.then((data) => {
const [idCidade, idEstado] = data;
obterHash(req.body.senha, hash => {
const senha = hash;
console.log("Will be inserted. idCity: "+idCidade+" idState: "+idEstado);
app.db('salao')
.insert({ idcidade: idCidade,
idestado: idEstado,
senha})
.then(_ => res.status(200).send())
.catch(err =>{res.status(400).json(err)})
})
})
.catch((err) => console.log("general error", err));
};
return { save }
}

NodeJS - Cannot set Headers after they are sent to the client

So I've searched around and found out that to fix the said issue, I have to return after sending a response. But my problem is, even though I have return, I still have the error.
const dbEditCourse = (req, res, db, logger) => {
let {
origCourse, code, description, type
} = req.body;
if (!code || !description || !type) {
res.json({
haveEmpty: true
});
return;
}
db.transaction((trx) => {
db.select('*').from('course_strand').where('code', '=', code)
.then(data => {
if (data[0]) {
//error happens in this block of code
res.json({
isSuccess: false
});
return;
//i also tried return res.json({ isSuccess: false });
}
//wrapping this in 'else' also does not work
return db('course_strand')
.returning('*')
.where('code', '=', origCourse)
.update({ code, description, type })
})
.then(course => {
return db('activity_logs')
.returning('*')
.insert({
date: new Date(),
employee_id: req.session.emp_id,
module: "COURSE / STRAND",
activity: "EDIT"
})
})
.then(activity => {
if (activity[0]) {
res.json({
isSuccess: true
});
return;
} else {
res.json({
isSuccess: false
});
return;
}
})
.then(trx.commit)
.catch(err => {
logger.error(err);
trx.rollback;
res.render('pages/error-500');
});
})
.catch(err => logger.error(err));
}
module.exports = {
dbEditCourse
}
What I'm doing to produce the error is, If the record is existing, it will go into the block of code above. Aside from that specific block of code, I don't encounter the error elsewhere. And the code is working fine even though I have the error.
You cannot break a promise chain with return keyword, all .then statements will be executed (exclude you throw an error in a .then), the res.json has been called many times.
Handler all errors (include your error and system error) in catch block.
In catch block, check the error is throwing by you or not to return the response.
const dbEditCourse = (req, res, db, logger) => {
let {
origCourse, code, description, type
} = req.body;
if (!code || !description || !type) {
res.json({
haveEmpty: true
});
return;
}
// util throw a error
const breakWithMyError = () => {
throw new Error("MY_ERROR");
}
db.transaction((trx) => {
db.select('*').from('course_strand').where('code', '=', code)
.then(data => {
if (data[0]) {
//error happens in this block of code
breakWithMyError();
//i also tried return res.json({ isSuccess: false });
}
//wrapping this in 'else' also does not work
return db('course_strand')
.returning('*')
.where('code', '=', origCourse)
.update({ code, description, type })
})
.then(course => {
return db('activity_logs')
.returning('*')
.insert({
date: new Date(),
employee_id: req.session.emp_id,
module: "COURSE / STRAND",
activity: "EDIT"
})
})
.then(activity => {
// revert logic, we check for error case first
if (!activity[0]) {
breakWithMyError();
}
})
.then(trx.commit)
.then(() => {
// finally you can run to here without any error
res.json({
isSuccess: true
});
})
.catch(err => {
// If you any error, the error comes form `breakWithMyError` or any things.
if (err.message === "MY_ERROR") {
// the error throw by `breakWithMyError`
return res.json({
isSuccess: false
});
}
logger.error(err);
trx.rollback;
// Why you return a html page in failed case? `res.status(500).json({message: "Internal server!"});`
res.render('pages/error-500');
});
})
.catch(err => logger.error(err));
}
module.exports = {
dbEditCourse
}
const dbEditCourse = (req, res, db, logger) => {
let {
origCourse, code, description, type
} = req.body;
if (!(code && description && type)) {
res.json({
haveEmpty: true
});
return;
} else { // Please Try this.
db.transaction((trx) => {
db.select('*').from('course_strand').where('code', '=', code)
.then(data => {
if (data[0]) {
//error happens in this block of code
res.json({
isSuccess: false
});
return;
//i also tried return res.json({ isSuccess: false });
}
//wrapping this in 'else' also does not work
return db('course_strand')
.returning('*')
.where('code', '=', origCourse)
.update({ code, description, type });
})
.then(course => {
return db('activity_logs')
.returning('*')
.insert({
date: new Date(),
employee_id: req.session.emp_id,
module: "COURSE / STRAND",
activity: "EDIT"
});
})
.then(activity => {
if (activity[0]) {
res.json({
isSuccess: true
});
return;
} else {
res.json({
isSuccess: false
});
return;
}
})
.then(trx.commit)
.catch(err => {
logger.error(err);
trx.rollback;
res.render('pages/error-500');
});
})
.catch(err => logger.error(err));
}
};
module.exports = {
dbEditCourse
};

mongoose findByIdAndUpdate update objects and push other onto existing field

Hello I would like to update a document in my db using findByIdAndUpdate only doing minimal calls, but some values have to be pushed onto an array and other updated.
i'm sure there must be an easy way to make this into one route instead of using the two
router.put('/notes/:id', (req, res) => {
Player.findByIdAndUpdate({
_id: req.params.id
}, {
$push: {
notes: req.body.notes
}
}, {
new: true
})
.then(player => res.status(200).json(player))
.catch(err => res.status(400).json({
'err': 'updating went wrong'
}))
})
router.put('/:id', (req, res) => {
let updates = {};
if (req.body.first) {
updates.first = req.body.first;
}
if (req.body.last) {
updates.last = req.body.last;
}
if (req.body.school) {
updates.school = req.body.school;
}
if (req.body.rank) {
updates.rank = req.body.rank;
}
Player.findByIdAndUpdate({
_id: req.params.id
}, updates, {
new: true
})
.then(player => res.status(200).json(player))
.catch(err => res.status(400).json({
'err': 'updating went wrong'
}))
})

Categories