update document using a unique filed with findOneAndUpdate is thorwing exception - node.js

I am new to node.js and mean.
I have nearly 20 fields to update in a document.
var UserSchema = mongoose.model('UserSchema');
I am passing these 20 fields in the request body and updating the UserSchema model object.
var newUser =new UserSchema(req.body);
Actually i am not passing _id from my request body but when i saw my newUser._id it is filled up with some id, so it resulting in an error called
exception: After applying the update to the document {_id: ObjectId('560e506ad4369d2a02a6ad6d') , ...}, the (immutable) field '_id' was found to have been altered to _id: ObjectId('560e65f395857e210313d3a7')
UserSchema.findOneAndUpdate({ userName: newUser.userName }, newUser, { new: true }, function (err, newuser) {
if (err) {
console.log(err);
return res.status(400).json({ message: err });
}
return res.status(200).json({ result: newUser });
});

Because you are creating new record set by following statement,
var newUser =new UserSchema(req.body);
Above statement will match all provided fields by req.body with registered schema for UserSchema Model and will generate new ObjectId for it. So newUser variable have _id field and when you try update existing record with newUser, it causes error as you are trying to change immutable _id field.
Instead you can re-write whole as following,
var UserSchema = mongoose.model('UserSchema');
var newUser = req.body;
UserSchema.findOneAndUpdate({ userName: newUser.userName }, newUser, { new: true }, function (err, newuser) {
if (err) {
console.log(err);
return res.status(400).json({ message: err });
}
return res.status(200).json({ result: newUser });
});
And if provided record does not exists then findOneAndUpdate will insert new as you provided new: true flag.

Related

How to update object in array of object of a document in mongoose?

My User Schema is like this
{
_id:ObjectId("6e9465528a15ba6")
name: 'XYZ',
email: 'abc#gmail.com',
transactions: [
{
_id:ObjectId("5e946557a5128a15ba6"),
date: 2020-04-09T06:00:30.000Z,
type: 'type1',
category: 'category1',
description: 'some desc',
}
]
}
I want to update some fields of transaction with specific id. But not happening.
I tried the solution answered to
Mongoose, update values in array of objects this question.
May be my _id is of type ObjectId and id coming from my request is String?
So how can I solve this problem?
My code is like this but still getiing error user.transactions._id is not function
app.post('/api/update', function (req, res) {
const {
id,
email,
date,
type,
category,
description
} = req.body;
User.findOne({email}, function (err, user) {
if (err) {
console.error(err);
res.status(500)
.json({
error: 'Internal error please try again'
});
} else if (!user) {
res.status(401)
.json({
error: 'Incorrect email or password'
});
} else {
const objectId = mongoose.Types.ObjectId(id);
let transaction = user.transactions._id(objectId);
transaction.date = date;
transaction.type = type;
transaction.category = category;
transaction.description = description;
user.save((err, data) => {
if (err) return res.send(err);
return res.sendStatus(200);
});
}
});
});
fortunately I had to do something similar recently, so I suggest you to have a look at this page from Mongoose docs in the case I miss something, but basically you have to find the document first and then update its array of objects.
I'd try something like this:
User.findOne({ _id: your_user_id }, (err, data) => {
if (err) return console.log(err);
if (data) {
//Mongoose has a special method (id) for subdocuments
var transaction = data.transactions.id(your_transaction_id);
date: new Date(),
type: 'type2',
category: 'category2',
description: 'whatever',
//data.save() saves everything, document and subdocument
data.save((err, data) => {
if (err) return console.log(err);
done(null, data);
});
}

How this pre hook save works?

I have been trying to use populate in mongoose, and my current idea is to check if a particular document is present in MongoDB, if it is then, the pre-save hook should check and rather then creating a new Document, it should just push it to the refs, so that I can populate later.
Now, the ref is being saved in MongoDB, however, how to check if the document which I am trying to save is there in mongo in pre save(or any other more suitable method)
This is the schema.
var Userschmea = new mongoose.Schema({
user:String,
posts:[{
type:mongoose.Schema.Types.ObjectId,
ref:'Post'
}]
})
var PostSchema = new mongoose.Schema({
content:String,
author:[{
type:mongoose.Schema.Types.ObjectId,
ref:'Author'
}]
})
Userschmea.pre('save',(next)=>{
//what to do here
next()
})
var Post = mongoose.model('Post',PostSchema);
var User = mongoose.model('User',Userschmea);
This is the endPoint by which I am trying to save:
app.post('/save/user',(req,res)=>{
console.log(req.body);
//Can i access this in my pre-save
const newUser = new User({
user: req.body.user
})
newUser.save((err)=>{
const newPost = new Post({
content:req.body.content,
author: newUser._id
})
newPost.save((err)=> {
if(err) {
return res.send(err);
}
})
if(err){
console.log(err);
return res.send(err);
}
})
return res.send(req.body.user);
})
You can use findOne method with some unique key to check whether user is exists or not. I have used here email.
Userschmea.pre('save',(next)=>{
var self = this;
Userschmea.findOne({email : this.email}, 'email', function(err, results) {
if(err) {
next(err);
} else if(results) {
self.invalidate("user", "user is exists");
next(new Error("User is already exists"));
} else {
next();
}
});
})
Hope this help!
====== UPDATE =====
Q: this is coming empty.
Ans:
Function expressions are best for object methods while Arrow functions are best for callbacks or methods like map, reduce, or forEach.
So, Don't use arrow function in this case to access global scope.

unique schema not working mongoose

I am having a situation in which I have defined username and email as unique in my model but whenever I try to insert the duplicate username/email in the database it gets inserted.
here is my model
const userSchema = new Schema({
email: {
type: String,
required: true,
unique:true
},
username: {
type: String,
required: true,
unique:true
}})
here is my code in which I insert the data
let user = new User(req.body);
user.save((err, result) => {
if (err) {
res.json({ success: false });
} else {
res.json({ result, success: true });
sendmail(req.body.email,req.body.username,verificationcode);
}
});
Your code is fine. So Try using mongoose unique validator in your model
var uniqueValidator = require('mongoose-unique-validator');
userSchema.plugin(uniqueValidator);
Or try to delete the table so that mongo recreates them
I would query for the user existence then use an if statement as follow:
User.findOne({email:req.body.email,username:req.body.username},(err, user)=>{
if(!user){
let newUser = new User bla bla bla
}else{
res.json({message:"User already exists."})
}

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

node.js email verification token

I'm trying to set up a verification step in Mongoose, Express Node application based on this blog post ... http://danielstudds.com/adding-verify-urls-to-an-express-js-app-to-confirm-user-emails-secure-spa-part-6/ that post is over a year old so it kind of surprises me that its the first google result for 'node email verification'. I'm very new to node so I'm reliant on examples. Based on that post I did not see a download so I pieced it together to suit my scenario and here is what my code looks like.
Verification Model
var mongoose = require('mongoose'),
uuid = require('node-uuid'),
User = require('mongoose').model('User');
var verificationTokenSchema = new mongoose.Schema({
_userid : { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
token: {type: String, required: true},
createdAt: {type: Date, required: true, default: Date.now, expires: '4h'}
});
verificationTokenSchema.methods = {
createVerificationToken : function (done) {
var verificationToken = this;
var token = uuid.v4();
verificationToken.set('token', token);
verificationToken.save( function (err) {
if (err) return done(err);
return done(null, token);
});
}
};
exports.verifyUser = function(token, done) {
verificationTokenModel.findOne({token: token}, function (err, doc){
if (err) return done(err);
User.findOne({_id: doc._userId}, function (err, user) {
if (err) return done(err);
user["verified"] = true;
user.save(function(err) {
done(err);
});
});
});
};
var verificationTokenModel = mongoose.model('VerificationToken', verificationTokenSchema);
exports.verificationTokenModel = verificationTokenModel;
Then in my User model I call create like so..
User Model
exports.createUser = function(req, res, next) {
// Do all the stuff that creates the user save it and get the id back
var verificationToken = new verificationTokenModel({_userId: user._id});
verificationToken.createVerificationToken(function (err, token) {
if (err){
err = new Error("Couldn't create verification token");
res.status(400);
return res.send({reason:err.toString()});
}
// Do stuff with the token here and email
This works 'partially' in my db 'verificationtokens' collection the objects don't contain a _userid they contain the _userid (user._id) stored in _id
My first issue is I don't really understand how this works when there isn't a 'constructor'
var verificationToken = new verificationTokenModel({_userId: user._id});
and how do I get this to store the user._id as _userid in the verification collection
I don't use Mongoose, but here is what it happened:
For your first question:
when you run:
var verificationTokenModel = mongoose.model('VerificationToken', verificationTokenSchema);
it creates the constructor.
For the second question:
MongoDB always create documents with a field called "_id", so, the "_id" field in your verification collection is not the "_id" field from your User collection. The reason that _userid is not inserted is because you have a typo:
var verificationToken = new verificationTokenModel({_userId: user._id});
where "_userId" should be "userid"

Resources