get all passing student and count - node.js

i want to getting document above mark 35, but i didn't success.
my api
router.get('/pass_student', async function(req, res, next){
await mark.find()
.exec()
.then(result => {
for(var j = 0; j < result.length; j++){
for(var i = 0; i < result[0].mark.length; i++){
console.log(result[j].mark[i].mark)
if(result[j].mark[i].mark >= 35){
const response = {
count: result.length,
}
res.send(response)
}
}
}
})
.catch(err => {
res.send(err);
})
})
my model
const mongoose = require('mongoose');
const markSchema = new mongoose.Schema({
mark:[{
mark: String
}],
subject:{
type: mongoose.Schema.Types.ObjectId,
ref: 'subject'
},
student:{
type: mongoose.Schema.Types.ObjectId,
ref: 'student'
}
})
module.exports = mongoose.model("mark", markSchema);
node:6044) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

Try this,
router.get('/pass_student', function(req, res, next){
mark.find()
.exec()
.then(result => {
let response;
for(var j = 0; j < result.length; j++){
for(var i = 0; i < result[0].mark.length; i++){
console.log(result[j].mark[i].mark)
if(result[j].mark[i].mark >= 35){
response = {
count: result.length,
}
}
}
res.send(response);
}
})
.catch(err => {
res.send(err);
})
});

Few observations I'd like to share based on your code are:
1. You are mixing async/await and promises, if you're calling an asynchronous method with await, then there is no need to use then/catch blocks. Also two things to be remember always while using await
the immediate parent function in which you are using await should be declared with async and
Always use try catch block to handle errors with async/await
2. The Error you are getting is because the res.send(response); statement is written inside For loop. In express you can send the response only once for a single request. In your case the server sends response after the first iteration in loop and on the next iteration it throws the error you got.
3. The response you are sending is not the actual result you want. It will always give you the length of the records returned from db i.e your count calculation logic is wrong.
Now lets come to the solution:
The easiest and best solution would be to change the type of marks in your schema to array of numbers instead of string, which actually makes sense. And by changing it to number you can easily get the count using mongoose query only. In that case your code and query will be:
router.get('/pass_student', async (req, res) => {
try {
const count = mark.find({mark: {$gt: 35}}).count();
return res.status(200).json({count});
/*
OR you can do;
const result = mark.find({mark: {$gt: 35}}).lean();
return res.status(200).json({count: result.length});
*/
} catch(err) {
return res.status(500).json({err});
}
});
Or if you don't wanna change the type of your field, change your code like:
router.get('/pass_student', async (req, res) => {
try {
const result = mark.find({}).lean();
const count = 0;
result.forEach(elem => {
const temp = elem.mark.find(i => Number(i) >= 35);
if(temp) count = count + 1;
});
return res.status(200).json({count});
} catch(err) {
return res.status(500).json({err});
}
});
Do let me know if you have any issues.
Hope this helps :)

Related

Update and get lastest data in one function

I have the following javaScript code for Nodejs to update data in MongoDB.
I have a list of products that I want update (called itemsToBeUpdated). (Update the preference value in the order that they are passed-in)
Once all the updates have been completed, I would like to go and retrieve the product list by calling the function getProducts(req, res);
where should I put the getProducts(req, res)? Not position A because Product.findByIdAndUpdate is async so it will get to position A before the findByIdAndUpdate is completed.
Not position B because there are more items to loop through.
const updateAndRefreshProducts = (req,res) => {
const itemsToBeUpdated = req.body;
const counter = someValue
for(let i = 0; i<itemsToBeUpdated.length; i++){
const newPreference = counter;
counter++;
condition= {_id: itemsToBeUpdated[i]._id};
Product.findByIdAndUpdate(condition, {preference:newPreference})
.then(result => {
console.log('performing update completed for result' + result.name +" : ", result.preference);
//position B
})
.catch(error =>{
console.log('error', error);
});
}
//position A
}
thanks
There are a couple of way to handle this, the easiest way to accomplish this will be to utilize Promise.all().
You may want to read on this documentation
const updateAndRefreshProducts = (req,res) => {
const itemsToBeUpdated = req.body;
const productUpdatePromises = []
const counter = someValue
for(let i = 0; i<itemsToBeUpdated.length; i++){
const newPreference = counter;
counter++;
condition= {_id: itemsToBeUpdated[i]._id};
const productUpdatePromise = Product.findByIdAndUpdate(condition{preference:newPreference})
productUpdatePromises.push(productUpdatePromise)
}
await Promise.all(productUpdatePromises).then((results) => {
console.log(results);
//Called your get all products here
})
.catch((error) => {
console.error(error.message);
});
}

Saving with mongoose to mongodb in a loop

I want to save an object multiple times, with a change to the date field - adding one month per iteration.
A for loop doesn't work due to node async nature.
const saveMany = (req, res, obj, data) => {
let saves = [];
if (data.frequency === 'monthly') {
let i = 0;
for (let i = 0; i < data.reccuring_length; i++) {
const newEntry = new obj(data);
if (i != 0) newEntry.created_date.addMonths(1) //using datejs
newEntry.save((err, entry) => {
if (err) {
return res.status(400).send({
message: err
});
}
saves.push(entry);
})
}) //end of for loop
return res.json(saves)
} //end of if
}
I've seen stuff about promises / the async library but can't make a working implementation (I am new to this though so could be missing something obvious).
Any help is appreciated :)
EDIT:
Saving To MongoDB In A Loop
Found this link which is relevant, but if anyone has other suggestions that would be great.
EDIT 2:
Just realised my code has camelcase and snake case, changing in my code to make all object data snake case.
I think you can do somethings like that:
const saveMany = async (req, res, obj, data) => {
let saves = [];
if (data.frequency === 'monthly') {
let i = 0;
for (let i = 0; i < data.reccuring_length; i++) {
const newEntry = new obj(data);
if (i != 0) newEntry.created_date.addMonths(1) //using datejs
try{
const entry= await newEntry.save();
saves.push(entry);
} catch(err) {
return res.status(400).send({ message: err });
}
}) //end of for loop
return res.json(saves)
} //end of if
}

How to Loop Data and Validate in Mongodb

I have a dynamic input field where user can add multiple category at once. Data sent at backend is like
['ELECTRONIC','TOYS','GAMES']
Now I want to check for each element of the array ,if they are already present on mongodb . If its present i want to store it in errors object as
errors={ 0: 'Duplicate Data found'}
I am attaching my code for validation which is not working please help . .
const Category = require('../../models/Category');
const fieldCheck = (req, res, next) => {
const data = req.body;
const errors = [];
for( i = 0; i < data.length ; i++){
Category.findOne({ category_name : data[i]})
.then(user => {
if(user){
// # If a reqistered User ID is found ,then move ahead
errors[i] = 'Duplicate Entry Found';
errors.push(errors[i]);
}
}).catch(err =>{
return res.json(err);
}
)
}
console.log(errors);
};
module.exports = fieldCheck;
My Category Schema is ....
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const categorySchema = new Schema ({
category_name:{
type:String,
unique:true,
isRequired:true,
},
date:{
type:Date,
default:Date.now()
}
});
module.exports = mongoose.model('Category',categorySchema);
You are trying to call an asynchronous method (findOne) inside a synchronous loop (for). As you experience, this is like oil and water.
An easy fix is to make your method asynchronous and use the await keyword, example:
const fieldCheck = async (req, res, next) => {
const data = req.body;
const errors = [];
try {
for( i = 0; i < data.length ; i++) {
let user = await Category.findOne({ category_name : data[i]});
if (user) {
// # If a reqistered User ID is found ,then move ahead
errors[i] = 'Duplicate Entry Found';
errors.push(errors[i]);
}
}
// I assume you wanted to respond to res.json here?
console.log(errors);
} catch (err) {
return res.json(err);
}
};
module.exports = fieldCheck;

Nodejs wait for query

I'm using Nodejs with MongoDB(mongoose along with express).
Since I don't trust the user data, I need to verify it from the database.
input data:
{
"id": "someid",
"nottrusteddata": [ {"id": "1"}, {"id" :"2"}]
}
In my function, I'm verifying the data:
router.post("/validate", (req, res,next) =>{
let validated_data = validate_data(req);
console.log(JSON.stringify(validated_data));
const mydata = new Mydata({
id: req.body.id,
lst : validated_data
});
console.log("mydata: " + JSON.stringify(mydata));
/* Some Usefull stuff is here */
res.status(200).json();
}
function validate_data(req){
let validated_data = []
for(let i = 0; i < req.body.nottrusteddata.length; i++)
{
Databaseobject.findOne({'id': req.body.nottrusteddata[i].id})
.exec()
.then(dbobject =>{
if(dbobject) // not undefined, it exists in the database
{
// Some logic with the object returned from the database
let tmp_object = {};
tmpobject.id = dbobject.id;
// Append it to the list, so that the upper function can use it
validated_data.push(tmp_object);
}
})
}
return validated_data;
}
The desired output should contain the correct information coming from the database, however, due to the async nature of the nodejs, validated_data returns null.
I have also tried using Promise. I couldn't succeed it.
const validate_data = function(req){
return new Promise(function(resolve,reject){
let validated_data = []
for(let i = 0; i < req.body.nottrusteddata.length; i++)
{
Databaseobject.findOne({'id': req.body.nottrusteddata[i].id})
.exec()
.then(dbobject =>{
if(dbobject) // not undefined, it exists in the database
{
let tmp_object = {};
tmpobject.id = dbobject.id;
validated_data.push(tmp_object);
}
})
}
resolve(validated_data);
}
}
What am I doing wrong? How can I wait for the database query to finish, then execute the main part? If there is only one validation, I could've used .then(). However, the list might have contained many elements and I need to wait for all of them to be verified.
Your Databaseobject.findOne() calls are asynchronous so your promise will resolve before any of them complete.
You can make use of Promise.all to wait until all of your promises resolve.
Hopefully, this will work for you:
router.post("/validate", (req, res) => {
validate_data(req.body.nottrusteddata)
.then(validated_data => {
const mydata = new Mydata({
id: req.body.id,
lst: validated_data
})
// Some useful stuff is here
res.status(200).json()
})
.catch(err => {
// Handle error
})
}
function validate_data(nottrusteddata) {
// Create array of pending promises
const promises = nottrusteddata
.map(item => {
return Databaseobject
.findOne({ 'id': item.id })
.exec()
})
// Wait for all promises to resolve
return Promise.all(promises)
.then(docs => {
return docs
.filter(dbobject => dbobject) // Filter out undefined
.map(dbobject => {
return { id: dbobject.id }
})
})
}
If you want, you could also use async-await here:
router.post("/validate", async (req, res) => {
try {
const validated_data = await validate_data(req.body.nottrusteddata)
const mydata = new Mydata({
id: req.body.id,
lst: validated_data
})
// Some useful stuff is here
res.status(200).json()
}
catch(err) {
// Handle error
}
})

using express to loop through a mongoose schema function ,collecting data into an array and rendering the page finally with all the data

Nodejs +Express+Mongoose beginner's question:
*So I came across this problem where I had an array of categories (clothes category). For each category, I wanted to fetch results from the database. So I did what a normal person would do. I used a for loop, fetched the product, stored in an array, then fetched next product, stored in into the same array and so on....... (same process for men and women both ). Now i want to render my view with the data but it isn't accessible coz of the asynchronous nature of node js. I totally understand it. So what I am looking for is an alternative/solution for my problem *
My express code explains my agony better. Please have a look:
//INDEX PAGE
router.get('/', (req, res, next) => { //anonymous callback function
let allCategories;
let womenFashion = [],
menFashion = [];
Clothing.groupByBodyPart(function(err, categories) {
if (categories) {
allCategories = categories.slice(0);
for (let i = 0; i < allCategories.length; i++) { //looping
let category = allCategories[i]._id; //gives a category, eg.,footwear,bottomwear
Clothing.getCategoryWise(category, 'M', function(err, products) { //products here will be an array of objects
if (products) {
menFashion[i] = products.slice(0); //storing products into an array for future use
console.log(menFashion[i]); //accessible here one at a time Eg.,[{slippers},{shoes}]
}
});
Clothing.getCategoryWise(category, 'F', function(err, products) {
if (products) {
womenFashion[i] = products.slice(0); //same as above
console.log(womenFashion[i]); //same as above
}
});
}
}
console.log(menFashion[0]); // not accessible here, so I can't render my page
res.render('index.pug', {
allCategories,
menFashion,
womenFashion
}); //won't work as menFashion and womenFashion aren't available
});
});
Here you go with the mongoose static function:
//get categorywise products
clothingSchema.statics.getCategoryWise = function(bodyPart,gender,callback){
Clothing.aggregate([
{ $match: {'category.bodyPart': bodyPart,
'category.gender': gender
}
},
{ $group: {_id: "$category.type" }
},
{ $sort: {_id: 1 }
}
])
.exec((err,products)=>{
if(err){
return callback(err);
}else if(!products){
let err = new Error('No Product Found!');
err.status = 401;
return callback(err);
}
return callback(null,products);
});
}
Just for the record
Everything is working great, I am just having trouble rendering my page because the menFashion and womenFashion array aren't accesible outside the callback.
A thanks in advance :)
UPDATE:
I solved it myself,but still thnks guys (specially #faiz)
My solution basically includes nesting:
//INDEX PAGE
router.get('/',(req,res,next)=>{ //anonymous callback function
let allCategories;
let womenFashion = [], menFashion = [];
Clothing.groupByBodyPart(function(err,categories){
if(categories){
allCategories = categories.slice(0);
for(let i = 0; i < allCategories.length; i++){ //looping
let category = allCategories[i]._id; //gives a category, eg.,footwear,bottomwear
Clothing.getCategoryWise(category,'M',function(err,products){ //products here will be an array of objects
if(products){
menFashion.push(products);
menFashion[i] = products.slice(0); //storing products into an array for future use
//console.log(menFashion[i]); //accessible here on at a time Eg.,[{slippers},{shoes}]
}
Clothing.getCategoryWise(category,'F',function(err,products){
if(products){
womenFashion[i] = products.slice(0); //same as above
//console.log(womenFashion[i]); //same as above
}
if(i == allCategories.length-1){
console.log('men',menFashion); //everything accessible
console.log('men',menFashion); //everything accessible
res.render('index.pug',{allCategories,menFashion,womenFashion}); //tadaaaaaaaa
}
});
});
}
}
});
});
You can use something like this.
I used two Promise.all calls to make sure that you have two results that you can use. You can just as well use one Promise.all
router.get('/', (req, res, next) => { //anonymous callback function
let allCategories;
let womenFashion = [],
menFashion = [];
Clothing.groupByBodyPart(function (err, categories) {
if (categories) {
allCategories = categories.slice(0);
const menPromises = [];
const womenPromises = [];
for (let i = 0; i < allCategories.length; i++) { //looping
let category = allCategories[i]._id; //gives a category, eg.,footwear,bottomwear
menPromises.push(
new Promise((resolve, reject) => {
Clothing.getCategoryWise(category, 'M', function (err, products) { //products here will be an array of objects
if (products) {
menFashion[i] = products.slice(0); //storing products into an array for future use
resolve(menFashion[i]);
console.log(menFashion[i]); //accessible here one at a time Eg.,[{slippers},{shoes}]
}
})
})
);
womenPromises.push(
new Promise((resolve, reject) => {
Clothing.getCategoryWise(category, 'F', function (err, products) { //products here will be an array of objects
if (products) {
womenFashion[i] = products.slice(0); //storing products into an array for future use
resolve(womenFashion[i]);
console.log(womenFashion[i]); //accessible here one at a time Eg.,[{slippers},{shoes}]
}
})
})
);
}
Promise.all([Promise.all(menPromises), Promise.all(womenPromises)]).then(([menResults, womenResults]) => {
console.log(menFashion[0]); // not accessible here, so I can't render my page
res.render('index.pug', {
allCategories,
menFashion,
womenFashion
}); //won't work as menFashion and womenFashion aren't available
});
}
});
});

Resources