How to Loop Data and Validate in Mongodb - node.js

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;

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);
});
}

How to achieve a one to many relationship with GraphQL & DataLoader

I'm having a hard time figuring out why my graphQL & DataLoader setup isn't working and could use some help.
I have a User and a Orchestra type, and I would like to transform a User to populate its createdOrchestras field and do the same thing with Orchestra and an owner field.
EDITED.
The following code causes an infinite loop:
Here are the DataLoaders, which are passed to the resolvers via context:
const DataLoader = require('dataloader');
const Orchestra = require('../models/orchestras');
const User = require('../models/users');
const batchOrchestras = async ids => {
const orchestras = await Orchestra.find({ _id: { $in: ids } });
const orchestraMap = {};
orchestras.forEach(o => {
orchestraMap[o.id] = o;
});
return ids.map(id => orchestraMap[id] || new Error(`No result for ${id}`));
}
const batchUsers = async ids => {
const users = await User.find({ _id: { $in: ids } });
const userMap = {};
users.forEach(u => {
userMap[u.id] = u;
});
return ids.map(id => userMap[id] || new Error(`No result for ${id}`));
};
module.exports = () => new DataLoader(batchUsers)
module.exports = () => new DataLoader(batchOrchestras);
Here are the transform functions which should be capable of fetching data for nested fields via data loaders and modify sensitive fields like the user password.
async function transformUser(userId, loaders) {
const user = await loaders.userLoader.load(userId.toString());
return {
...user._doc,
createdOrchestras: await Promise.all(
user._doc.createdOrchestras.map(orchestra =>
transformOrchestra(orchestra, loaders)
)
)
}
}
async function transformOrchestra(orchestraId, loaders) {
const orchestra = await loaders.orchestraLoader.load(orchestraId.toString());
return {
...orchestra._doc,
owner: transformUser(orchestra._doc.owner, loaders)
}
}
module.exports = {
transformUser,
transformOrchestra
}
How should I restructure the code to prevent an infinite loop but keeping the transform functions as the final providers of data for a particular field ?

get all passing student and count

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 :)

Business logic works but Mongoose save doesn't

Hi I'm trying to save data from my shopping cart app to MongoDB database. In my cart controller folder, there are getAllProducts, addOneProduct, removeOneProduct and updateOneProducts methods. All of them run perfectly fine except for updateOneProducts.
The console.log shows:
GET all product { id: [1,2,4], qty: [1,1,1] }
UPDATE one product { id: [1,2,4], qty: [1,1,2] }
which indicate that the business logic works. However, the document is not updated on mLab.
User model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
username: String,
userID: String,
picture: String,
products: {
id: [Number],
qty: [Number],
}
});
const Users = mongoose.model('User', userSchema);
module.exports = Users;
updateOneProduct
const extractProduct = (user) => {
const product = {};
product.id = user.products.id;
product.qty = user.products.qty;
return product;
}
// if user is logged in n changes the qty of a product, update the qty in database
const updateOneProduct = (req, res) => {
// if (!req.user) return res.status(401).json({ error: 'Unauthorized' });
const userId = req.user._id;
const event = req.body.event;
const productId = req.body.id;
return Users.findById(userId)
.then(user => {
const qty = user.products.qty;
const index = user.products.id.indexOf(productId);
if (index === -1) {
throw new Error(`Product doesn't exist!`);
}
const regex = /^[0-9\b]+$/;
if (event === 'plus') {
qty[index] += 1;
} else if (event === 'minus') {
qty[index] -= 1;
} else if (regex.test(event) && event !== '0') {
qty[index] = Number(event);
}
if (qty[index] === 0) {
user.products.id.splice(index, 1);
qty.splice(index, 1);
}
return user.save();
})
.then(user => {
const product = extractProduct(user);
console.log('UPDATE one product', product);
return res.status(200).json(product);
})
.catch(error => res.status(400).json({ error }));
}
So I basically cloned user.products.qty array with const qty = [...user.products.qty], do operations on it and replace user.products.qty with it using user.products.qty = qty. Lastly, I save it with return user.save().
For some reason, I can't mutate subdocument project and save it. I have to replace the whole subdocument user.products then save for it to work. I think it might also have something to do with this being a PUT request.
// if user is logged in n changes the qty of a product, update the qty in database
const updateOneProduct = (req, res) => {
// if (!req.user) return res.status(401).json({ error: 'Unauthorized' });
const userId = req.user._id;
const event = req.body.event;
const productId = req.body.id;
return Users.findById(userId)
.then(user => {
const qty = [...user.products.qty];
const index = user.products.id.indexOf(productId);
if (index === -1) {
throw new Error(`Product doesn't exist!`);
}
const regex = /^[0-9\b]+$/;
if (event === 'plus') {
qty[index] += 1;
} else if (event === 'minus') {
qty[index] -= 1;
} else if (regex.test(event) && event !== '0') {
qty[index] = Number(event);
}
if (qty[index] === 0) {
user.products.id.splice(index, 1);
qty.splice(index, 1);
}
user.products.qty = qty;
return user.save();
})
.then(user => {
console.log('UPDATE - After save promise', user.products);
return res.status(200).json(user.products);
})
.catch(error => res.status(400).json({ error }));
}

Getting empty array in nodejs

I am posting value But I am getting empty array . I know its node asynchronous problem . But I don't know how do i solve this. I have refer this following link:
How do I return the response from an asynchronous call?
But I could not able to understand . Kindly help me to understand promises and how do i use that in my code.
router.post('/inspection_list', function (req, res) {
var id = req.body.project_id;
console.log(id)
// res.send("ok")
db.inspection.findOne({'_id':id},(err,response)=>{
if(err){
console.log("error");
}
else{
console.log("Data")
var inspection = [];
var data = response.inspection_data;
var f = data.map(function (item) {
var fielduser = item.fielduser_id
db.fielduser.findOne({'_id': mongoose.Types.ObjectId(fielduser)},(err,user)=>{
console.log(user.owner_name);
console.log(item.inspection_name)
inspection.push({inspection_name:item.inspection_name,field_user_name : user.owner_name})
})
});
console.log(inspection) // Here am getting empty value
// setTimeout(function(){ console.log(inspection) }, 5000); my timeout code
}
})
});
router.post('/inspection_list', async function (req, res) {
var id = req.body.project_id;
try{
var response = await db.inspection.findOne({'_id':id})
var inspection = [];
var data = response.inspection_data;
for ( var i = 0; i<data.length; i++){
var item = data[i]
var fielduser = item.fielduser_id
var user = await db.fielduser.findOne({'_id': mongoose.Types.ObjectId(fielduser)})
inspection.push({inspection_name:item.inspection_name,field_user_name : user.owner_name})
}
}
catch(err){
throw err
}
})
This uses async and await, you can use it if you are using node version >=7.6
Also note the following:
router.post('/inspection_list', async function (req, res)
Handling each error seperately
router.post('/inspection_list', async function (req, res) {
var id = req.body.project_id;
try{
var response = await db.inspection.findOne({'_id':id})
}
catch(err){
// handle error here
throw err
}
var inspection = [];
var data = response.inspection_data;
for ( var i = 0; i<data.length; var item = data[i]
var fielduser = item.fielduser_id
try{
var user = await db.fielduser.findOne({'_id': mongoose.Types.ObjectId(fielduser)})
}
catch(err){
// handle error
}
inspection.push({inspection_name:item.inspection_name,field_user_name : user.owner_name})
}
})
Using mongoose would be the easy way out, it returns Promises for all query and save functions, so you'd simply do:
YourModel.findOne({params}).then(() => {...})
If you're unable to do that, in your case, a 'promisified' example would be:
var findAndFillArray = (project_id) => new Promise((resolve) => {
.... your previous code here ....
inspection.push({inspection_name:item.inspection_name,field_user_name :
user.owner_name})
if (data.length === inspection.length){ // Or some other preferred condition
resolve(inspection);
}
})
Then you'd call this function after you get the id, like any other function:
var id = req.body.project_id;
findAndFillArray(id).then((inspection_array) => {
res.send(inspection_array) // Or whatever
})
Now, map and all list functions are synchronous in JS, are you sure the error is due to that?

Resources