the returned counter value is always 0. Why? How can I solve this problem?
In messages.findOne there the correct counter value. In conversation.forEach the counter value is always null.
router.get('/isNewMessages', auth, async (req, res) => {
try {
const query = { usernames: req.user.username }
Conversation.find(query, (err, conversations) => {
var counterNewMessages = 0
conversations.forEach(conversation => {
console.log(counterNewMessages) // Here is always 0
Messages.findOne({ _id: conversation.messages }, (err, messages) => {
counterNewMessages += messages.messages.filter(message => !message.isRead && message.receiver === req.user.username).length
console.log(counterNewMessages) // Here is value is correct
})
})
res.status(201).send({ counterNewMessages })
})
} catch (e) {
res.status(400).send(e)
}
})
Solution (Explanation in accepted answer):
router.get('/isNewMessages', auth, async (req, res) => {
try {
const query = { usernames: req.user.username }
Conversation.find(query, async (err, conversations) => {
let counterNewMessages = 0
for (const conversation of conversations) {
await Messages.findOne({ _id: conversation.messages }, (err, messages) => {
counterNewMessages += messages.messages.filter(message => !message.isRead && message.receiver === req.user.username).length
})
}
res.status(201).send({ counterNewMessages })
})
} catch (e) {
res.status(400).send(e)
}
})
It's because you are incrementing within an async function. Therefore, res.send is happening before the call to findOne has actually returned a value; before the incrementing happens.
I had similar issue before and here is how I fixed it.
const roomPromises = [];
tourPackagesParams.roomPax.forEach(room => {
if (
<conditional statement>
) {
roomPromises.push(fetchHotelRoomByRoomId(room.roomId));
} else {
roomPromises.push(Promise.resolve(null));
}
});
const roomUpgrades = [];
Promise.all([...roomPromises]).then(response => {
Related
I'm new to javascript and i'm having a hard time making my response return to wait for my mongodb query finish running inside a forEach loop.
My code is currrently:
exports.applyThesaurus = (req, res, next) => {
let numModified = 0;
var prom = new Promise((resolve,reject) => {
req.file.forEach((obj,idx) => {
wos.updateMany(
{ creator: req.userData.userId},
{ $set: { [req.body.field+".$[element]"] : obj.replaceWith } },
{ arrayFilters: [ { "element": { $in: obj.replaced } } ] }
).then((result) => {
console.log(result.nModified)
numModified += result.nModified
})
.catch((err) => {
res.status(500).json('There was an error while applying the thesaurus.');
})
if( idx === req.file.length -1) {
resolve()
}
})
})
prom.then(() => {
console.log('im returning');
res.status(200).json({message: numModified + ' itens replaced successfully'});
})
}
What happens is that the "i'm returning" console log triggers before the one logging result.nModified
I need to be able to run all the updateMany queries and then respond with the number of updated itens.
Any tips? Thank you very much!
your code is trying to return resolve before your updateMany executes.
if (idx === req.file.length - 1) {
resolve() // this resolve get's executed befour updateMany get executed
}
this might give you a better understanding of the callbacks and why it is happening. as said if you want to resolve the promise after your updateMany finishes execution you need to update your code as below:
exports.applyThesaurus = (req, res, next) => {
let numModified = 0;
var prom = new Promise((resolve, reject) => {
let updateManyPromisesArray = []
req.file.forEach((obj, idx) => {
updateManyPromisesArray.push(wos.updateMany({
creator: req.userData.userId
}, {
$set: {
[req.body.field + ".$[element]"]: obj.replaceWith
}
}, {
arrayFilters: [{
"element": {
$in: obj.replaced
}
}]
}))
Promise.all(updateManyPromisesArray)
.then((result) => {
if (idx === req.file.length - 1) {
resolve()
}
})
.catch((err) => {
res.status(500).json('There was an error while applying the thesaurus.');
})
})
})
prom.then(() => {
console.log('im returning');
res.status(200).json({
message: numModified + ' itens replaced successfully'
});
})
}
Also, you should start using async and await for avoiding these kinds of callback hells situations.
I am new to unit testing. I am trying to write test cases for controller.js files for nodejs microservices files. I am unable to understand where I am going wrong. Always throws an error "TypeError: Cannot read property 'empId' of undefined" for 2 of these properties.
This is the controller code:
const crmgDetails = db.crmgResource_details;
const employeeProposal = db.employee_Proposal;
const Op = db.Sequelize.Op;
const raDetails = db.crmgRaSheet_entity;
let results = [];
Sequelize = require('sequelize')
exports.findOne = (req, res) => {
console.log(req.body.empId);
crmgDetails.findAll({where: {
resEmployeeNumber: req.body.empId
}
})
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving tutorials."
});
});
};
exports.findMatchingDemandsForRmg = (req,res) => {
let proposedDemands = [];
employeeProposal.findAll({
where: {
emp_id: req.body.empId,
demandSbu : req.body.sbu
}
}).then(proposedEmployee => {
console.log('proposedEmployee',proposedEmployee);
if(proposedEmployee.length === 0){
crmgDetails.findAll({
where: {
resEmployeeNumber: req.body.empId,
demandSbu: req.body.sbu
}
}).then(matchingDemands => {
console.log('matchingDemands ',matchingDemands)
proposedDemands = matchingDemands;
})
}
else{
console.log("crmg Employee")
console.log(proposedEmployee)
for(let employee of proposedEmployee){
crmgDetails.findOne({
where: {
demandUid: employee.demandUid,
resEmployeeNumber: req.body.empId,
demandSbu: req.body.sbu
}
}).then( crmgProposed=> {
proposedDemands.push(crmgProposed);
})
}
}
setTimeout(() => {
console.log(proposedDemands)
res.send(proposedDemands);
}, 3000);
}).catch((err)=>{
res.status(500).send({
message:
err.message || "Some error occurred while retrieving tutorials."
});
})
}
exports.getResourceAllocationDetails = (req,res) => {
employeeProposal.findAll({
include: {
model: raDetails
},
where: Sequelize.and(
{activeFlag : true},
Sequelize.or({status:"Accepted By RMG"},
{status:"Rejected"}
))
}).then(employees => {
res.send(employees)
})
}
This is the test file I tried to write without my head:
const CrmgRaSheetModel = require('../controllers/crmgResource_Details.controller')
describe('Check for succcessful fetech API call', () => {
it('property getResourceAllocationDetails should be called', async () => {
CrmgRaSheetModel.getResourceAllocationDetails((res) => {
expect(res).to.be.an('object')
return res.json()
})
});
it('property findMatchingDemandsForRmg should be called', async () => {
CrmgRaSheetModel.findMatchingDemandsForRmg((res) => {
expect(res).to.be.an('object')
return res.json()
})
});
it('property findOne should be called', async () => {
CrmgRaSheetModel.findOne((res) => {
expect(res).to.be.an('object')
return res.json()
})
})
})
from test file you are calling controller method with only res, so no chance to send your input as your body.
So pass req,res both and pass your input value in req
This is my code for sending data to a database:
app.post('/thanks', function(req, res) {
if (atendees.checkin === req.body.dbstring) {
dbConn.then(client => {
delete req.body._id;
const db = client.db('mydata')
db.collection(atendees.checkin).insertOne(req.body);
})
(...)
This is how I display on the page after clicking on a href link:
app.get('/view-feedbacks', function(req, res) {
dbConn.then(client => {
const db = client.db('mydata')
db.collection(atendees.checkin).find({}).toArray().then(function(feedbacks) {
res.status(200).json(feedbacks);
atendees.checkin = ' '
}).catch(err => {
throw(err);
})
});
});
That works fine. How can I do something similar to display all collections from the database instead of just the individual ones?
This is what I tried to do:
app.get('/view-history', function(req, res) {
dbConn.then(client => {
const db = client.db('mydata')
db.listCollections().toArray().then(function(collInfos) {
res.status(200).json(collInfos);
atendees.checkin = ' '
}).catch(err => {
throw(err);
})
});
});
But it just gives me the name of each collection. I want to show all collections and all of their elements.
Edit: my question is different from this one: MongoDB Show all contents from all collections .I'm trying to do this on express.js, not on the terminal
Edit2: Using db.collection:
app.get('/view-history', function(req, res) {
dbConn.then(client => {
const db = client.db('mydata')
db.collections().then(function(feedbacks) {
res.status(200).json(feedbacks);
atendees.checkin = ' '
}).catch(err => {
throw(err);
})
});
But this gives the error: TypeError: converting circular structure to JSON
With async/await, this could be done:
app.get('/view-history', async (req, res) => {
try {
const client = await dbConn;
const db = client.db('mydata');
let collections = await db.collections();
let documents = await Promise.all(collections.map(async (collection) => {
let documents = await collection.find({}).toArray();
return Promise.resolve([collection.collectionName, documents]); // Retain collectionName
}));
// Format into an object that looks like `collectionName: documents`
let formatted = documents.reduce((obj, collection) => {
obj[collection[0]] = collection[1];
return obj;
}, {});
res.json(formatted);
} catch (e) {
console.error(e);
res.sendStatus(500);
}
});
A Promise-only approach:
app.get('/view-history', (req, res) => {
dbConn.then((client) => {
const db = client.db('mydata');
return db.collections();
}).then((collections) => {
return Promise.all(collections.map((collection) => {
return new Promise((resolve, reject) => {
collection.find({}).toArray().then((documents) => {
resolve([collection.collectionName, documents]);
}).catch(reject);
});
}));
}).then((documents) => {
let formatted = documents.reduce((obj, collection) => {
obj[collection[0]] = collection[1];
return obj;
}, {});
res.json(formatted);
}).catch((e) => {
console.error(e);
res.sendStatus(500);
});
});
The main reason this code is unnecessarily verbose is because instead of just returning a big array filled with arrays of documents, you probably want an object that retains the name of the collection, like so:
{
collection1: [...documents...],
collection2: [...documents...],
...
}
Instead of:
[
[...documents...],
[...documents...],
...
]
If you do want just a big array of each collection without caring about the names of the collections, it becomes much simpler:
async/await version:
app.get('/view-history', async (req, res) => {
try {
const client = await dbConn;
const db = client.db('mydata');
let collections = await db.collections();
let documents = await Promise.all(collections.map((collection) => collection.find({}).toArray()));
res.json(documents);
} catch (e) {
console.error(e);
res.sendStatus(500);
}
});
Promise-only version:
app.get('/view-history', (req, res) => {
dbConn.then((client) => {
const db = client.db('mydata');
return db.collections();
}).then((collections) => {
return Promise.all(collections.map((collection) => collection.find({}).toArray()));
}).then((documents) => {
res.json(documents);
}).catch((e) => {
console.error(e);
res.sendStatus(500);
});
});
Have you tried just db.collections()? If that also doesn't give what you need, you might have to invoke db.collection(<name>) on each of the names you get from listCollections.
Been hunting the internet trying to find an answer to why the following doesn't work.
I am trying to pass in the req object when I add the job so that I have access to it when the job is processed.
But the process is never executed when the whole req object is passed to job.data. Yet I can pass parts of the req object.
What I'm trying to do maybe anti-pattern and a big no no. But, I am trying to understand why it won't work. It seems strange that it just continues without any error.
Below is an example, hopefully it is clear.
My kue is abstracted into a separate file, and initialised onto app.locals.Q as follows:
// Q.js
class Q {
constructor(options) {
this.q = kue.createQueue(options)
}
addJob = (name, data) => {
return Queue.create({
queue_job: name,
queue_route: data.route,
queue_user: data.user,
queue_added: new Date(),
})
.then(response => {
this.q.create(name, {
id: response.get('queue_id'),
route: data.route,
request: data.request
})
.save();
return Promise.resolve(response);
})
.catch(error => {
return Promise.reject(error);
});
processJob = (name, work, options = {}) => {
const {concurrency} = options;
this.q.process(name, concurrency || 1, (job, done) => {
const {data: {id, route, request}} = job;
Queue.update({
queue_running: true
}, {
where: {
queue_id: id
}
})
.then(() => {
if (process.env.NODE_ENV !== 'production') {
console.log(`running job ${id} from ${route}`);
}
return new Promise((resolve, reject) => {
return work(resolve, reject, request);
});
})
.then(results => {
return Queue.update({
queue_running: false,
queue_completed: new Date(),
queue_results_path: results || null
}, {
where: {
queue_id: job.data.id
}
});
})
.then(() => {
if (process.env.NODE_ENV !== 'production') {
console.log(`completed job ${id} from ${route}`);
}
done();
})
.catch((error) => {
if (process.env.NODE_ENV !== 'production') {
console.log(`failed job ${id} from ${route}`);
console.log(error);
}
Queue.update({
queue_running: false,
queue_error: `${error}`
}, {
where: {
queue_id: id
}
})
.then(() => {
done(error);
})
.catch(err => {
console.error(err);
done(err);
});
});
});
};
};
// example route
queue = (req, res) => {
const {locals: {Q}} = req.app;
Q.addJob('foo', {
route: req.path,
user: req.user.get('username'),
request: req
})
.then(queue_id => {
Q.processJob('foo', (resolve, reject, request) => {
console.log(request)
resolve('complete')
})
res.json({sucess: true})
})
}
redis can't serialize the req object.
kue simply silently fails.
I am getting an error that seems to suggest I'm not returning some of my statements, but I think I'm doing everything correctly. Here's the warning:
Warning: a promise was created in a handler at /src/api/podcasts.js:51:18 but was not returned from it
This is the code of the function in question:
'findPodcastById': (db, podcastId, callback) => {
var queryString = "SELECT * FROM podcasts WHERE id=$1;";
db.one(queryString, [podcastId])
.then((result) => {
return callback(null, result);
})
.catch((err) => {
return callback(err, null);
});
},
And the parent function that it's called from:
app.post('/getepisodes', (req, res, next) => {
var podcastId = req.body.podcastId;
var userId = req.body.userId;
var podcast;
podcasts.findPodcastByIdAsync(db, podcastId)
.then((result) => {
podcast = result;
return request(podcast.rss);
})
.then((result) => {
return podcastParser.parseAsync(result, {})
})
.then((result) => {
return Promise.resolve(result.channel.items);
})
.map((item) => {
var date = new Date(item.pubDate).toLocaleString();
return podcasts.addEpisodeAsync(db, podcast.id, item.title, item.enclosure.url.split('?')[0], striptags(item.description), date, item.duration);
})
.map((episode) => {
return posts.addPostAsync(db, 'podcast', episode.id, episode.title, episode.description);
})
.then(() => {
return podcasts.findEpisodesByPodcastIdAsync(db, podcastId, userId);
})
.then((result) => {
return res.json(result);
})
.catch((err) => {
next(err);
});
});
I have a return statement in each promise block, so I'm not sure what I'm doing wrong, I would really appreciate some help!
findPostCastBy id is not returning the promise, try this
'findPodcastById': (db, podcastId) => {
return db.one("SELECT * FROM podcasts WHERE id=$1;", [podcastId])
}