'findOneAndUpdate()' does not update the existing data in mongo db - node.js

I am developing a web application using MEAN Stack with Angular 6. There if the user previously has added data into the db that data should be updated. For that I used findOneAndUpdate() method. But without updating the existing data it posts another data set into the db.
This is my post route. This 'userName' comes from a different schema.
router.post('/save', function(req, res) {
var mod = new rdaColor(req.body);
rdaColor.findOneAndUpdate(
{
userName: req.body.userName,
colorMovementBox: req.body.colorMovementBox
},
req.body,
{ upsert: true, new: true },
function(err, data) {
if (err) {
console.log(err);
res.send(err);
} else {
res.send(mod);
}
}
);
});
This is schema.
var mongoose = require('mongoose');
// Schema for rda color panel
var rdaColorSchema = new mongoose.Schema({
userName: {
type: String
},
colorMovementBox: {
type: String,
},
});
module.exports = mongoose.model('rdaColor', rdaColorSchema);
This is the output for the following console.log.
console.log("mod"+mod+" "+(req.body));
output
mod{ _id: 5bbd68344619a612b07a688e,
userName: 'abc#yahoo.com',
colorMovementBox: 'rgb(49,64,116)',
} [object Object]
How can I make it only to update the data.

Please check this query.
This will update if data exists with Id and create new if not exists.
If you remove upsert: true then it does not create a new record if not exists.
rdaColor.findOneAndUpdate(
{ _id: "yourId" },
{
$set: {
userName: req.body.userName,
colorMovementBox: req.body.colorMovementBox
}
},
{ upsert: true, new: true },
function(err, doc) {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
}
);

Related

I'm unable to push my array to mongodb document using node.js

In my previous html code when I submit it sends a post to /comment/:id then the website crashes and outputs MongoError: Unsupported projection option: $push: { comment: { content: "gfdghd" } } in my console. I don't know how to solve it and I hope I can get some help on the issue as I'm a starter with web development.
I want this to work by pushing the array which includes the req.body into a certain mongodb array default collection where it finds the parent post _id. If you need me to elaborate please ask, thanks.
This is my code:
app.js
const Post = require("./models/Post");
mongoose
.connect("secret", {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: true,
})
.then(() => {
console.log("connected to mongodb cloud! :)");
})
.catch((err) => {
console.log(err);
});
app
.post("/comment/:id", authenticateUser, async (req, res) => {
const content = req.body;
// checks for missing fields
if (!content){
return res.send("Please enter all the required credentials!");
}
//This is where I tried to match and then push it to mongodb
Post.update({"_id": ObjectId(req.params.id) }, {
$push: {
comment: content,
}
}, function (error, success) {
if (error) {
console.log(error);
} else {
console.log(success);
}
});
})
Post Mongoose Schema
Post.js
const mongoose = require("mongoose");
const PostSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
postedAt: {
type: String,
default: new Date().toString()
},
postedBy: {
type: String,
},
warned: {
type: String,
},
comment: [String]
});
module.exports = new mongoose.model("Post", PostSchema);
Everything else works but the array functionality.
I think there are a few mistakes, you didn't await the request and you put "_id" when querying instead of _id.
Another way you could do it too would be using findByIdAndUpdate method.
await Post.findByIdAndUpdate(req.params.id, {
$push: {
comment: content,
},
function(error, success) {
if (error) {
console.log(error);
} else {
console.log(success);
}
},
});

find an id in embedded document node mongoose

I have a model of courses with the following structure:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const user_shortid = require('shortid');
// Create Course schema
const CourseSchema = new Schema({
courseDetail: {
type: String
},
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
enrolledUsers: [{
type: Schema.Types.ObjectId,
ref: 'users'
}],
currentStatus: {
type: String,
default: 'Planned'
}
});
mongoose.model('courses', CourseSchema);
I have created a post request for adding a signed in user to the array of enrolledUsers, the problem is, I want to check first if the req.user.id exists in the enrolledUsers array. Following is my post request:
router.post('/joincourse', [ensureAuthenticated], (req, res) => {
Course.findByIdAndUpdate({ _id: req.body.coursecode },
{ $push: { enrolledUsers: req.user.id } },
{ safe: true, upsert: true },
function (err, doc) {
if (err) {
req.flash('error_msg', 'Could not enroll in the course');
res.redirect('/dashboard');
} else {
req.flash('success_msg', 'You are now enrolled in the course');
res.redirect('/dashboard');
}
}
);
});
Right now the behavior is that a user can enroll again and again in the same course.
Is there some way I can check for the req.user.id in the enrolledUsers array before it is added?
you can do find the user first by using find() and then if a user exists, update it , else
give an error like this
router.post('/joincourse', [ensureAuthenticated], (req, res) => {
Course.findById({ _id: req.body.coursecode },
function (err, doc) {
if (err) {
req.flash('error_msg', 'Could not enroll in the course');
res.redirect('/dashboard');
} else {
if(doc){
if(!doc.enrolledUsers.includes(req.user.id)){ // here is the checking
doc.enrolledUsers.push(req.user.id);
doc.save();
req.flash('success_msg', 'You are now enrolled in the course');
res.redirect('/dashboard');
}
}else{
// show error msg
}
}
}
);
});

FindOneAndUpdate with the model in mongoose

I tried two ways to overwrite an existing document if its not exist.
1st way
This code didn't work and throws this error on every update:
error: *After applying the update to the document {_id: ObjectId('***') , ...}, the (immutable) field '_id' was found to have been altered to _id: ObjectId('***78')"*
exports.loginUser = function (req, res) {
var newUser = new userModel(req.body);
userModel.findOneAndUpdate({email: req.body.email}, newUser ,{ new: true,
upsert:true, setDefaultsOnInsert: true }, function (err, userUpdate) {
return res.json(userUpdate);
}
2nd Way
Directly inserting the request body which work well. But I dont want to do that, because body can have junk:
exports.loginUser = function (req, res) {
var newUser = new userModel(req.body);
userModel.findOneAndUpdate({email: req.body.email}, req.body ,{ new: true,
upsert:true, setDefaultsOnInsert: true }, function (err, userUpdate) {
return res.json(userUpdate);
}
So can anybody suggest me how to do findOneAndUpdate with the model instead of request body.
Use .update() method:
var newUser = new userModel(req.body);
newUser.update(newUser, { upsert:true, setDefaultsOnInsert: true }, function (err) {
if (err) {
console.log(err);
return res.json('Cannot save user');
}
res.json(newUser);
});
There is no need to use the new:true option because newUser will always contain the updated or inserted document
You shouldn't use new because that will create a new _id, so instead you should do:
exports.loginUser = function(req, res) {
var newUser = req.body; // here remove the new
userModel.findOneAndUpdate(
{ email: req.body.email },
newUser,
{ new: true, upsert: true, setDefaultsOnInsert: true },
function(err, userUpdate) {
return res.json(userUpdate);
}
);
}

Mongoose reference overwritten

I am in a bit of a pickle. Whenever I create a new resume as a logged in user it doesn't add the resume id as an array. I.e, ["20293", "2932392", "32903239"]
Instead, it overwrites the current resume id in the users schema. Here is the code
UserSchema
const UserSchema = new Schema({
_vId: {
type: String,
default: id.generate()
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
accountType: {
type: String,
enum: ['Alphaneer', 'Administrator', 'Support', 'PRO'],
default: 'Alphaneer'
},
email: {
type: String,
required: true,
trim: true
},
username: {
type: String,
required: true,
trim: true,
unique: true
},
bio: {
type: String,
default: "No bio provided."
},
// TODO: Hash the password before inserting as a document :)
password: {
type: String,
required: true
},
createdAt: {
type: String,
default: moment(new Date()).format("MMM DD, YYYY") // "Sun, 3PM 17"
},
resume: [ { type: mongoose.Schema.ObjectId, ref: "Resume" } ]
});
Where I post my resume
// POST /dashboard/resume/create
router.post('/resume/create', (req, res, next) => {
Resume.create(req.body, (err, resume) => {
if (err) {
var err = new Error("Error:" + err);
err.status = 404;
next(err);
} else {
req.user = jwtDecode.decode(req.session.tokenID, 'secret');
//I am assuming that you have saved your resume and getting the saved object in `resume`, now update the logged in user in req.user
var user = req.user.sessionId;
var updateData = {
resume: resume._id
}
//save the updated user
User.findByIdAndUpdate(user, updateData, function(err, user) {
console.log(user);
if (err) {
res.json(err);
} else {
res.json(user);
}
})
}
})
});
gif of submitting new resumes
UPDATE:
error picture
UPDATED CODE:
// POST /dashboard/resume/create
router.post('/resume/create', (req, res, next) => {
Resume.create(req.body, (err, resume) => {
if (err) {
var err = new Error("Error:" + err);
err.status = 404;
next(err);
} else {
req.user = jwtDecode.decode(req.session.tokenID, 'secret');
//I am assuming that you have saved your resume and getting the saved object in `resume`, now update the logged in user in req.user
var user = req.user.sessionId;
var updateData = {
resume: resume._id
}
//save the updated user
User.findById(user, function(err, user) {
console.log(user);
if (err) {
res.json(err);
} else {
user.resume.push(resume.id)
user.save(function(user) {
return res.json(user);
});
}
})
}
})
});
This is wrong:
var user = req.user.sessionId;
var updateData = {
resume: resume._id
}
//save the updated user
User.findByIdAndUpdate(user, updateData, function(err, user) {
console.log(user);
if (err) {
res.json(err);
} else {
res.json(user);
}
});
The resume field is an array and you are manipulating it as a string field. The method findOneAndUpdate do two things:
Find the document by it's id
Update it with the new data
The second argument is the new data to set. So, the second step is translated to:
User.upate({ _id: user }, { resume: resume._id });
Can you see what's wrong? resume must store an array of resume's id and your are setting a id as value. Obviously this will throw an MongooseError.
Your second shot is correct but has a typo error:
User.findById(user, function(err, user) {
console.log(user);
if (err) {
res.json(err);
} else {
user.resume.push(resume.id)
user.save(function(user) {
return res.json(user);
});
}
});
You must add the _id field since this is the ObjectID of the new created document (resume). So, you need to do user.resume.push(resume._id) instead.
Update
According with your last comment, you want to populate your User model, that is, through association id's retrieve all model data. In this case, is recommended that the resumes array change like this:
...
resumes: [
{
resume: {
type: Schema.Types.ObjectId,
ref: 'Resume'
}
}
]
To populate the User document with all Resume data you just need to reference the resume key in resumes field array.
User.findById(user, function(err, user) {
if (err) {
return res.json({ success: false, message: err.message });
}
user.resume.push(resume.id)
user.save(function(err, user) {
if (err) {
return res.json({ success: false, message: err.message });
}
// save was fine, finally return the user document populated
User.findById(user).populate('resumes.resume').exec(function(err, u) {
return res.json(u);
});
});
}
});
The populate method accepts a string with the fields that we want fill with it model data. In your case is an only field (resume). After run the query, you will get something like this:
{
_id: a939v0240mf0205jf48ut84sdfdjg4,
...,
resumes: [
resume: {
_id: f940tndfq4ut84jofgh03ut85dg9454g,
title: 'Some title'
},
...
]
}
Just to follow up on my comment regarding how I suggest you solve the issue:
router.post('/resume/create', (req, res, next) => {
Resume.create(req.body, (err, resume) => {
if (err) {
var err = new Error("Error:" + err);
err.status = 404;
next(err);
} else {
req.user = jwtDecode.decode(req.session.tokenID, 'secret');
//Here, instead of creating a new key entry for resume, you rather push new resume-id into the resume property of the "found user".
//find, update and save the user
User.findOne({_id: req.user.sessionId}, function (err, userToUpdate) {
userToUpdate.toJSON().resume.push(resume.id);
userToUpdate.save(function (err) {
if(err) {
console.error('ERROR!');
}
});
});
}
})
});
I left the rest of your code (saving new resume) untouched - I assume that part works. Give this a try and let me know if you encounter some problems.

Cannot update MongoDB using mongoose

I am trying to update a collection from my database using de node module mongoose. The problem is with $set updates. Here is my code:
// Update a user
app.patch('/user/:user_id', passport.authenticate('bearer', { session: false }),
function (req, res) {
var conditions = { _id: new ObjectId(req.params.user_id)},
updateObj = { $set: req.body }; // {email : "bob#example.com", username: "bob"}
User.update(conditions, updateObj, function callback (err, numAffected, rawResponse) {
if (err) {
res.send(err);
return;
}
// numAffected is the number of updated documents
if (numAffected == 0) {
res.json({ message: 'No user affected'});
return;
}
res.json({ message: 'User updated'});
});
});
If I update an existing key like email, it is updated. But if I want to add a new key, numAffected is always 0 and the rawResponse is undefined.
Any idea of what happens?
Edit
Here is my Schema:
var userSchema = mongoose.Schema({
email : String,
username : String,
password : String
});
In order to set multiple fields in a document, you must set the Multi option in your config, otherwise Mongoose will ignore the continuation, and only update the first doc.
From the docs:
var conditions = { name: 'borne' }
, update = { $inc: { visits: 1 }}
, options = { multi: true };
Model.update(conditions, update, options, callback);
function callback (err, numAffected) {
// numAffected is the number of updated documents
});
Another note here: The numAffected should return as expected, but I can't find any documentation on their site about the raw response, but it should return as expected as well. Do you know of any documentation for this?
I think this is what you really want to do with mongoose to update email and username of a user.
app.patch('/user/:user_id', passport.authenticate('bearer', { session: false }),
function (req, res) {
User.findOneAndUpdate({_id: req.params.user_id},
{
$set: {
username: req.body.username,
email: req.body.email
}
}, function(err, user) {
if (err)
res.send(err);
if (user) {
res.json({message: 'User updated'});
} else {
res.json({message: 'User does not exist'});
}
});
});

Resources