Node Js: Remove string array element from mongoDB - javascript

I have a user schema as follows:
const UserSchema = new mongoose.Schema({
skills: [String]
});
module.exports = mongoose.model("User", UserSchema);
And a Fetch request to delete a skill as follows:
const deleteItem = async (id) => {
try {
await fetch(`http://localhost:5000/api/user/deleteskill`, {
method: "DELETE",
headers: { "Content-Type": "application/JSON", token: accessToken },
body: JSON.stringify({ userid: userid , skill:id}),
})
.then((res) => res.json())
.then((data) => {
console.log("USER SKILLS:", data.userskills);
});
} catch (err) {
console.log(err);
}
};
Server
const deleteSkill = async (req, res) => {
try {
const user = await User.findById(req.body.userid)
//user.skills.pull(req.body.skill);
// removeskill = user.skills.filter(function(item) {
// return item !== req.body.skill
// })
if (user.skills.includes(req.body.skill)) {
res.status(400).json("Item Still Exists");
} else {
res.status(200).json("Item Deleted");
}
} catch (error) {
res.status(500).send({ error: error.message });
}
};
the array is in the following structure
[
'skill1', 'java', 'skill5'
]
I have tried to remove the user skill from the array in several ways but I still get res.status(400).json("Item Still Exists");. What I'm doing wrong?

Use the findOneAndUpdate method to find a document with the user id and update it in one atomic operation:
const deleteSkill = async (req, res) => {
try {
let message = "Item Deleted";
let status = 200;
const user = await User.findOneAndUpdate(
{ _id: req.body.userid },
{ $pull: { skills: req.body.skill } },
{ new: true }
)
if (user && user.skills.includes(req.body.skill)) {
message = "Item Still Exists";
status = 400;
} else if (!user) {
message = "User Not Found";
status = 404;
}
res.status(status).send({ message });
} catch (error) {
res.status(500).send({ error: error.message });
}
};

I believe you want to remove skills from the database then the following function could help you out.
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("mydb");
var myquery = { userid: userid, skillid: skillid};
dbo.collection("skills").deleteOne(myquery, function(err, obj) {
if (err) throw err;
console.log("1 document deleted");
db.close();
});
});

You have a method of removing elements from arrays, if you want to remove the first one you could use array.shift (more on it here), but if you want to delete it completely from your database you could always, find it and then update it.

User.update({ _id: userid }, { $pull: { "skills": "[skill]" }})

Related

Error in updating profile with image using mongoose and cloudinary

updateProfile: async function(req, res) {
try {
const update = req.body;
const id = req.params.id;
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
const image = req.files.profileImage;
const cloudFile = await upload(image.tempFilePath);
const profileImage = cloudFile.url
console.log('Loging cloudfile', profileImage)
await User.updateOne(id, { update }, { profileImage }, { new: true },
function(err, doc) {
if (err) {
console.log(err)
}
if (doc) {
return res.status(200).send({ sucess: true, msg: 'Profile updated successful' })
}
});
} catch (error) {
res.status(500).json({ msg: error.message });
}
}
But I'm getting an error of "Callback must be a function, got [object Object]"
I have tried to $set: update and $set: profileImage but still not working.
So the image successful upload into the cloudinary but the update for mongoose is not working.
Upon brief research into the issue, I think you are feeding the arguments in wrong. Objects can be confusing but not to worry.
Your code is:
await User.updateOne(id, { update }, { profileImage }, { new: true }
However, I believe it should be something more like:
await User.updateOne({id: id}, { profileImagine: profileImage, new: true },
The API reference annotates use of the function as:
const filter = { name: 'John Doe' };
const update = { age: 30 };
const oldDocument = await User.updateOne(filter, update);
oldDocument.n; // Number of documents matched
oldDocument.nModified; // Number of documents modified

Make query for every object in json using for or forEach

My problem is, I want to make INSERT query for every object from JSON using some loop, but I almost always got an error "Cannot set headers after they are sent to the client".Can someone help?Tnx
const connection = require('./config');
module.exports.excel = function (req, res) {
var _query = 'INSERT INTO excel (id, first_name, last_name) values ?';
var jsonData = req.body;
var values = [];
function database() {
return new Promise((resolve, reject) => {
jsonData.forEach((value) => {
values.push([value.id, value.first_name, value.last_name]);
connection.query(_query, [values], (error, results) => {
if (error) {
reject(
res.json({
status: false,
message: error.message
}))
} else {
resolve(
res.json({
status: true,
data: results,
message: 'Excel file successfully created in database'
}))
}
});
});
})
}
async function write() {
await database();
}
write();
}
After I got JSON from my Angular 6 front I put req.body into jsonData and try with forEach to put every object("value" in this case) into query and write that into Excel file.
You will have to wrap each query in a Promise and wait for all to complete before sending the response using Promise.all
Not that database() is going to throw when one of the queries fail and you won't have any access to the resolved promises.
const connection = require('./config');
module.exports.excel = function(req, res) {
const _query = 'INSERT INTO excel (id, first_name, last_name) values ?';
const jsonData = req.body;
function database() {
return Promise.all(
jsonData.map(
value =>
new Promise((resolve, reject) => {
const values = [value.id, value.first_name, value.last_name]
connection.query(_query, [values], (error, results) => {
if (error) {
reject(error.message);
return;
}
resolve(results);
});
})
)
);
}
async function write() {
try {
const results = await database();
res.json({
status: true,
data: results,
message: 'Excel file successfully created in database'
});
} catch (e) {
res.json({
status: false,
message: e.message
});
}
}
write();
};

How to migrate my mongoose PROMISE chain transactions to ASYNC / AWAIT flow?

I created an API that integrate database responses in a promise flow, but I think the interpretation of the code is complex and I believe that async / await approach could improve both understanding and the code itself.
The API is built in NodeJS using mongoose 5.6.1 and express 4.17.1.
Can you help me in improve this?
Below is the API that I want to improve:
/** New employee */
router.post('/', (req, res) => {
let { idCompany, name, departament } = req.body;
let _id = mongoose.Types.ObjectId(); // Generating new MongoDB _ID
let employeeCreated;
const promise1 = new Promise((resolve, reject) => {
// Querying by document '$oid'
Companies.findOne({ _id: idCompany }, (err, company) => {
// Error returned
if (err) reject({ error: "Invalid request, something went wrong!" });
// Invalid data received
if (!company) reject({ error: "Unauthorized action!" });
// Everything OK
resolve(company);
});
})
.then(company => {
if(company) {
const promise2 = new Promise((resolve, reject) => {
Employees.create({ _id, idCompany, name, departament }, (err, employee) => {
// Error returned
if (err) reject({ error: "Invalid request, something went wrong!", err });
// Everything OK
employeeCreated = employee;
resolve(company);
});
})
return promise2;
}else reject({ error: "Company not found!" });
})
.then(company => {
let { name: companyName, address, email, tel, employees } = company;
employees.push(_id);
const promise3 = new Promise((resolve, reject) => {
Companies.findByIdAndUpdate(
{ _id: idCompany },
{ $set: { _id: idCompany, name: companyName, address, email, tel, employees } }, // spotlight
{ new: true },
(err, company) => {
// Something wrong happens
if (err) reject({ success: false, error: "Can't update company!" });
// Everything OK
resolve(company);
}
);
});
return promise3;
});
promise1
.then(() => res.json({ success: true, employeeCreated }))
.catch(err => res.status(400).json({ error: "Invalid request, something went wrong!", err }));
});
Regards.
One key to using promises with mongoose, is using the exec method:
Your code could then look something like this (not tested):
router.post('/', async (req, res) => {
try {
const { idCompany, name, departament } = req.body;
const _id = mongoose.Types.ObjectId();
const company = await Companies.findOne({ _id: idCompany }).exec();
const employeeCreated = await Employees.create({ _id, idCompany, name, departament });
const { name: companyName, address, email, tel, employees } = company;
employees.push(_id);
await Companies.findByIdAndUpdate(
{ _id: idCompany },
{ $set: { _id: idCompany, name: companyName, address, email, tel, employees } }, // spotlight
{ new: true }).exec();
res.json({ success: true, employeeCreated });
} catch(err) {
res.status(400).json({ error: "Invalid request, something went wrong!", err });
}
});
You could throw some specific custom errors in the try block if you find that necessary.
You could simply make the functions where your promises are running async and so, you could await for the promises to resolve.
For example, in your route use this:
router.post('/', async (req, res) => {
and then when performing an async operation, use this:
const company = await Companies.findOne({ _id: idCompany }).exec();
Also, I would suggest you to wrap this with try and catch statments
Hope it helps!

Using Multiple FindOne in Mongodb

I am trying to extend the amount of fields that our API is returning. Right now the API is returning the student info by using find, as well as adding some information of the projects by getting the student info and using findOne to get the info about the project that the student is currently registered to.
I am trying to add some information about the course by using the same logic that I used to get the project information.
So I used the same findOne function that I was using for Projects and my logic is the following.
I created a variable where I can save the courseID and then I will put the contents of that variable in the temp object that sending in a json file.
If I comment out the what I added, the code works perfectly and it returns all the students that I require. However, when I make the additional findOne to get information about the course, it stops returning anything but "{}"
I am going to put a comment on the lines of code that I added, to make it easier to find.
Any sort of help will be highly appreciated!
User.find({
isEnrolled: true,
course: {
$ne: null
}
},
'email pantherID firstName lastName project course',
function(err, users) {
console.log("err, users", err, users);
if (err) {
return res.send(err);
} else if (users) {
var userPromises = [];
users.map(function(user) {
userPromises.push(new Promise(function(resolve, reject) {
///////// Added Code START///////
var courseID;
Course.findOne({
fullName: user.course
}, function(err, course) {
console.log("err, course", err, course);
if (err) {
reject('')
}
courseID = course ? course._id : null
//console.log(tempObj)
resolve(tempObj)
}),
///// ADDED CODE END //////
Project.findOne({
title: user.project
}, function(err, proj) {
console.log("err, proj", err, proj);
if (err) {
reject('')
}
//Course ID, Semester, Semester ID
//map to custom object for MJ
var tempObj = {
email: user.email,
id: user.pantherID,
firstName: user.firstName,
lastName: user.lastName,
middle: null,
valid: true,
projectTitle: user.project,
projectId: proj ? proj._id : null,
course: user.course,
courseId: courseID
}
//console.log(tempObj)
resolve(tempObj)
})
}))
})
//async wait and set
Promise.all(userPromises).then(function(results) {
res.json(results)
}).catch(function(err) {
res.send(err)
})
}
})
using promise could be bit tedious, try using async, this is how i would have done it.
// Make sure User, Course & Project models are required.
const async = require('async');
let getUsers = (cb) => {
Users.find({
isEnrolled: true,
course: {
$ne: null
}
}, 'email pantherID firstName lastName project course', (err, users) => {
if (!err) {
cb(null, users);
} else {
cb(err);
}
});
};
let findCourse = (users, cb) => {
async.each(users, (user, ecb) => {
Project.findOne({title: user.project})
.exec((err, project) => {
if (!err) {
users[users.indexOf(user)].projectId = project._id;
ecb();
} else {
ecb(err);
}
});
}, (err) => {
if (!err) {
cb(null, users);
} else {
cb(err);
}
});
};
let findProject = (users, cb) => {
async.each(users, (user, ecb) => {
Course.findOne({fullName: user.course})
.exec((err, course) => {
if (!err) {
users[users.indexOf(user)].courseId = course._id;
ecb();
} else {
ecb(err);
}
});
}, (err) => {
if (!err) {
cb(null, users);
} else {
cb(err);
}
});
};
// This part of the code belongs at the route scope
async.waterfall([
getUsers,
findCourse,
findProject
], (err, result) => {
if (!err) {
res.send(result);
} else {
return res.send(err);
}
});
Hope this gives better insight on how you could go about with multiple IO transactions on the same request.

Getting a variable to outer scope

I'm confused by this one. First of all this is my code:
router.post('/update', (req, res, next) => {
// Todo legit credit card holding
Account.findOneAndUpdate(
{ _id: req.user._id },
{
$set: {
// username: req.body.username,
creditCardNo: req.body.cardNo,
isPremium: true,
},
},
{ upsert: true },
(err, doc) => {
if (err) {
console.log(err);
}
}
);
var newUser;
Account.findById(req.user._id,(err, doc)=>{
if(err){
console.log(err);
}
else{
newUser = doc;
}
});
console.log(newUser);
res.render('user-pannel/pannel', {
title: 'User pannel',
user: newUser,
});
});
What it does is: It gets the POST call and updates a record in the db. Now I want to basically reload the the page (res.render part) and send the new user object.
I need to send the new one, because the one in req.user is now outdated (was updated before and I'm just printing the old version).
I tried getting around the problem by doing this newUser = doc;, but for some reason the newUservariable is undefined when logged outside of the findById method. Why? If I console log the doc inside of the findById method, it returns the changed object.
I turned it into an async function and awaited its resolution with the desired value.
router.post('/update', async (req, res, next) => {
// Todo legit credit card holding
Account.findOneAndUpdate(
{ _id: req.user._id },
{
$set: {
// username: req.body.username,
creditCardNo: req.body.cardNo,
isPremium: true,
},
},
{ upsert: true },
(err, doc) => {
if (err) {
console.log(err);
}
}
);
const newUser = await new Promise((resolve, reject) => {
Account.findById(req.user._id,(err, doc) => {
if(err) reject(err);
else resolve(doc);
});
});
console.log(newUser);
res.render('user-pannel/pannel', {
title: 'User pannel',
user: newUser,
});
});

Categories

Resources