Express api with conditions in query params - node.js

I am working on express Api with mongoose to create get Api for my project. I was able to make one get call successfully. But I am not sure how to make api for sorting data by different fields
Model
id,
productName,
costPrice,
soldPrice
router.get("/sellProduct",
(req, res, next) => {
// condition
if(req.query.product){
Product.find({prodName:req.query.product} ).then(data => {
if (data) {
res.status(200).send(data)
}
})
}
// WHAT SHOULD BE THE SORT LOGIC TO SORT BY DIFF FIELD
else if(req.query.sortBy){
Product.find({}).sort().then(data => {
if (data) {
res.status(200).send(data)
}
})
}
else{
Product.find().then(data => {
if (data) {
res.status(200).send(data)
}
})
}
});
I am beigneer and trying my best but any help will be appreciated

You can build the parameters for .find and .sort dynamically:
router.get("/sellProduct", (req, res, next) => {
const findParams = {};
const sortParams = {
lowerCostPrice: { costPrice: 1 },
higherCostPrice: { costPrice: -1 },
lowerSoldPrice: { soldPrice: 1 },
higherSoldPrice: { soldPrice: -1 },
/* add more sort options ... */
}[req.query.sortBy];
if (req.query.product) findParams.prodName = req.query.product
/* add more search options ... */
Product.find(findParams).sort(sortParams).then(data => {
if (data) {
res.status(200).send(data);
} else {
res.status(404);
}
}).catch(err => {
console.log(err);
res.status(500);
});
});

If I understand you question correctly, you can add a switch block and depending on the passed value, sort the products:
router.get('/sellProduct', (req, res, next) => {
let result;
// ...
if (req.query.sortBy) {
switch (req.query.sortBy) {
case 'lowerCostPrice': {
result = await Product.find({}).sort({ price: 'asc' });
break;
}
case 'higherCostPrice': {
result = await Product.find({}).sort({ price: 'desc' });
break;
}
// and so on...
}
}
// ...
res.status(200).send(result);
});

Related

How to get data from multiple model in single controller in NodeJS?

I am working on nodejs and i am using "Express js" framework, I am working on Controller and i am trying to get data from "two model functions" but i am getting message "anonymous", How can i get data from multiple "model functions", Here is my code,
This is my homeController
homeController.index = function (req, res, next) {
// fetching data into 'data variable' from FetchRecords model
homeModel.FetchRecords(function (err, data) {
if (err) {
req.flash('error', 'There was no records.');
} else {
res.render('home/index', { data: data });
}
});
homeModel.getAverage(function (average) {
console.log(average);
// res.render('home/index',{data:average});
});
};
This is my homeMOdel
homeModel.FetchRecords = function (data) {
sql.query('SELECT * FROM rating', function (err, res) {
if (err) {
return data(err, null);
} else {
return data(null, res);
}
});
};
homeModel.getAverage = function (average) {
console.log(average);
sql.query('SELECT avg(value) FROM rating', function (err, res) {
if (err) {
return average(err, null);
} else {
return average(null, res);
}
});
};
Inside homeModel just create 1 function instead of 2 separate. You can combine both MySQL queries into one like this.
const FetchRecordsAndAverage = function (data) {
sql.query('SELECT * FROM rating; SELECT avg(value) FROM rating', function (err, res) {
if (err) {
return data(err, null);
} else {
return data(null, res);
}
});
};
module.exports = {
FetchRecordsAndAverage
}
With this you will get combined data of both queries as arrays inside array.
Result of queries can be accessed as data[0] & data[1].
You should export the function from the home model:
const FetchRecords = function (data) {
sql.query('SELECT * FROM rating', function (err, res) {
if (err) {
return data(err, null);
} else {
return data(null, res);
}
});
};
const getAverage = function (average) {
console.log(average);
sql.query('SELECT avg(value) FROM rating', function (err, res) {
if (err) {
return average(err, null);
} else {
return average(null, res);
}
});
};
module.exports = {
FetchRecords,
getAverage
}
And retrieve them in your application by calling:
const { FetchRecords, getAverage } = require('./path/to/home_model');

Node Express pass function results outside of forEach

I am attempting to pass results from a forEach function (that needs to be async, but that's a different story) to ejs. Any help is greatly appreciated
router.get('/', (req, res) => {
Block.find({}).populate('servers').exec((err, foundBlocks) => {
if(err){
req.flash('error', 'Block not found. Please try again.');
res.redirect('back');
} else {
MyDB.find({}, (err, foundDB) => {
if(err){
console.log(err)
} else {
foundDB.forEach((DB) => {
pinger(DB.object, (output) => {
if(output){
const string = output;
DB.status = true
console.log(output)
} else {
DB.status = false;
}
})
});
res.render('settings/index', {
blocks: foundBlocks,
});
}
});
}
});
async/await does not works with forEach loop because forEach does not wait for the response to finish, using normal for loop is the key,
your code after replacing forEach with for loop should be like this
router.get('/', (req, res) => {
Block.find({}).populate('servers').exec((err, foundBlocks) => {
if(err){
req.flash('error', 'Block not found. Please try again.');
res.redirect('back');
} else {
MyDB.find({}, (err, foundDB) => {
if(err){
console.log(err)
} else {
for(let DB of foundDB){
pinger(DB.object, (output) => {
if(output){
const string = output;
DB.status = true
console.log(output)
} else {
DB.status = false;
}
})
});
res.render('settings/index', {
blocks: foundBlocks,
});
}
});
}
});

How to handle async when making mongoose query in each array element in express?

On the following post method, I'm having some issues due to moongose async. res.send(suggestions) is executed first then Expense.findOne.exec
app.post('/suggestions', async function(req, res) {
const suggestions = await req.body.map((description) => {
Expense.findOne({ description: new RegExp(description, 'i') }).exec((err, result) => {
if (result) {
console.log(result.newDescription);
return {
description,
newDescription: result.newDescription,
category: result.category,
subcategory: result.subcategory
};
}
});
});
res.send(suggestions);
});
The result is a array of null values. How can I executed a query for each item, then execute res.send(suggestion)?
Found solution with the following code:
app.post('/suggestions', async function(req, res) {
try {
if (req.body.length > 0) {
const suggestions = req.body.map((description) =>
Expense.findOne({ description: new RegExp(description, 'i') })
);
const results = await Promise.all(suggestions);
return res.send(results);
}
} catch (e) {
console.log('error', e);
}
});

mongoose express counter does not increment, scope problem

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 => {

How to multiple fetch data expressJS

I want to display chatbot and facebook data at the same time. how to display it? because when I try to run in the browser but it does not appear anything. I've tried to look it up on stackoverflow but did not get the right reference
route.js
app.get('/cpanel/facebook', function(req, res) {
if (req.session.user == null) {
res.redirect('/cpanel/login');
} else {
CB.getAllRecords( function(e, chatbot) {
res.render('cpanel/facebook', { udata : req.session.user, chatbot : chatbot });
});
FBM.getAllRecords( function(e, facebook) {
res.render('cpanel/facebook', { udata : req.session.user, facebook : facebook });
});
}
});
facebook.js
var facebook = db.collection('facebook');
exports.addNewFacebook = function(newData, callback) {
facebook.findOne({accesstoken:newData.accesstoken}, function(e, o) {
if (o) {
callback('accesstoken-taken');
} else {
facebook.insert(newData, {safe: true}, callback);
}
});
}
exports.getAllRecords = function(callback) {
facebook.find().toArray(
function(e, res) {
if (e) callback(e)
else callback(null, res)
}
);
}
chatbot.js
var chatbot = db.collection('chatbot');
exports.addNewChatBot = function(newData, callback) {
chatbot.insert(newData, {safe: true}, callback);
}
exports.getAllRecords = function(callback) {
chatbot.find().toArray(
function(e, res) {
if (e) callback(e)
else callback(null, res)
}
);
}
The easier way to manage asynchronous operations in node.js, especially when you have more than one operation that you want to coordinate is to use Promises instead of plain callbacks. And, fortunately, mongodb supports a promise-based interface for all its asynchronous operations now.
So, first change your methods to return a promise instead of taking a callback:
var chatbot = db.collection('chatbot');
exports.getAllRecords = function() {
return chatbot.find().toArray();
}
var facebook = db.collection('facebook');
exports.getAllRecords = function() {
return facebook.find().toArray();
}
Then, you can use those promises with Promise.all() to coordinate:
app.get('/cpanel/facebook', function(req, res) {
if (req.session.user == null) {
res.redirect('/cpanel/login');
} else {
Promise.all([CB.getAllRecords(), FBM.getAllRecords()]).then(results => {
res.render('cpanel/facebook', { udata : req.session.user, chatbot : results[0], facebook: results[1]});
}).catch(err => {
// render some error page here
res.sendStatus(500);
});
}
});
For a call to just a single function that returns a promise, you can use .then():
app.get('/cpanel/facebook', function(req, res) {
if (req.session.user == null) {
res.redirect('/cpanel/login');
} else {
FBM.getAllRecords().then(results => {
res.render('cpanel/facebook', { udata : req.session.user, facebook: results});
}).catch(err => {
// render some error page here
res.sendStatus(500);
});
}
});

Resources