Updated certain field of a document and do not effect the rest - node.js

I have below code.
User.findOneAndUpdate({ _id: request.body.id }, record, {new:true})
.exec((error, doc) => {
if (error) return response.json(error);
return response.json(doc);
});
Above code when I enter all the fields such as name, email and password then it correctly update the User Schema however, when I provide name and email but not password then it again updates all the fields including the password to null value. But in second scenario I do not want to update the password because I am not providing the password so it should not change. How can I make it work as I intend?
UPDATE
I ended up using below method but for save password is required and it has a pre hook that which encrypts password before saving. That means password is necessary for this method.
User.findOne({ _id: request.body.id })
.exec((error, user) => {
if (error) return response.json(error);
user.name = request.body.name;
user.email = request.body.email;
user.password = request.body.password;
user.save();
return response.json(user);
});

Use $set for partial update as following:
Users.findOneAndUpdate(
{ _id: request.body.id }, // query
{
"$set": { // doc
"name": req.body.name,
"email": req.body.password
}
},
{ upsert: true, new: true }, // options
function(err, doc){ // callback
if (err){
throw err;
} else if(doc){
console.log('Existing document updated successfully');
console.log(doc);
}
}
);

Have you try with new updateOne(). You can do like this:
const updateObj = {
"$set": {
"name": req.body.name,
"email": req.body.password
}
};
const updateQuery = User.updateOne({
_id: request.body.id
}, updateObj, {
new: true
}).exec();
updateQuery
.then((updateResponse) => {
console.log("Record update successfully");
response.status(200).json(updateResponse);
})
.catch((err) => {
console.log("Error while updating record: ", err);
return response.status(500).json({
error: err
});
});
You can read more on: https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/
Hope this helps :)

Related

How to update user lastlogin time in react

i want to display last login time of user in table column after they logged in. i tried with some code in backend with nodejs and mongoose but sure it is not the right way. Can anyone help me how to handle it from backend and FE as well?
Here is Schema of user:
const userSchema = new Schema(
{
name: { type: String },
email: { type: String, unique: true},
password: { type: String },
status: {type: String, enum:["Active", "Blocked"], default:"Active"},
token: { type: String },
lastLogin: { type: Date, default: Date.now()},
},
{ timestamps: true }
);
Route:
userRouter.post("/login", async (req, res, next) => {
try {
const { email, password } = req.body;
if (!(email && password)) res.status(204).send({ msg: "All fields are required!" });
const user = await UsersModel.findOne({ email });
console.log(user);
if (user && (await bcrypt.compare(password, user.password))) {
const accessToken = await JWTAuthenticate(user);
user.token = accessToken;
user.lastLogin = Date.now()
res.status(200).send(user);
} else {
res.status(404).send({ msg: "User with this email not found!" });
}
UsersModel.findOneAndUpdate({lastLogin: Date.now()}, (err, data) => {
if(err) console.log(err);
else console.log("Successfully updated the lastLogin", data);
})
} catch (error) {
next(error);
}
});
You just have to call await user.save() after setting lastLogin (and remove findOneAndUpdate call)
or
actually query for the user in your findOneAndUpdate call. Currently you are querying for a 'lastLogin' field with the value now() which will never match. Adjust this call as following:
UsersModel.findByIdAndUpdate(user._id, {lastLogin: Date.now()}, (err, data) => {
if(err) console.log(err);
else console.log("Successfully updated the lastLogin", data);
})
Just a hint: JWTs are used for storing the session state on client side. You don't need to actually store these Tokens in your database. You just have to validate it on incoming request.

Mongoose How to await updating documents?

im trying to figure out why my code isnt awaiting the update method in mongoose.
First I have tried to find the user
//Find user
const user = await User.findOne({
username,
active: { $ne: false },
accessBanExpires: { $lt: Date.now() },
}).select('+password');
//Update the user property
let newuser;
if (remember) {
if (!user.rememberAcct) {
newuser = await User.findOneAndUpdate(user._id, {
rememberAcct: true,
});
console.log('check: ', user.rememberAcct);
}
} else if (user.rememberAcct) {
await user.updateOne({
rememberAcct: false,
});
}
console.log(user.rememberAcct, newuser); //Here document always updating asyncrounously, but await seems not applying to update. newUser returns the user with old property
document always updating asyncrounously, but await seems not applying to update. newuser returns the user with old property
Update:
with options {returnDocument: 'after'} or {new: true} is still returning old document.
Refers: https://mongoosejs.com/docs/api.html#model_Model-findOneAndUpdate
What Im missing here?
newuser = await User.findOneAndUpdate(user._id, {
rememberAcct: true,
}, {returnDocument: 'after'});
In your snippet, there are 3 mistakes
1st
const user = await User.findOne({
username,
active: { $ne: false },
accessBanExpires: { $lt: Date.now() },
}).select('+password') // <-- missing `rememberAcct` to select, without it `user.rememberAcct` will always be `undefined`
2nd
await user.updateOne({ // <-- this should be `User` not `user`
rememberAcct: false, // <-- this filter will select a random user & update query is missing
})
3rd :- Not using user.save() to update user.
here is final solution.
const user = await User.findOne(
{
username,
active: { $ne: false },
accessBanExpires: { $lt: Date.now() },
},
{ password: 1, rememberAcct: 1 }
)
if (!user) throw new Error('user not found')
console.log('user.rememberAcct (before)', user.rememberAcct)
if (remember) {
if (!user.rememberAcct) {
user.rememberAcct = true
await user.save()
}
} else if (user.rememberAcct) {
user.rememberAcct = false
await user.save()
}
// NOTE: above `if/else` statements can be boiled down into single `if/else` statement, no need to check more than once.
// I leave it you figure it out.
console.log('user.rememberAcct (after)', user.rememberAcct)
Your should either use findByIdAndUpdate, or pass a proper query:
// Either:
await User.findByIdAndUpdate(
user._id,
{
rememberAcct: true
},
{
returnDocument: 'after'
}
);
// Or:
await User.findOneAndUpdate(
{
_id : user._id,
},
{
rememberAcct: true
},
{
returnDocument: 'after'
}
);
I believe you need to place them into a try catches, let me know if the below works
this ads some error handling as well so you know why it may have failed.
async function yourfunctionorrequest() {
let password
try {
const user = await User.findOne({
username,
active: {
$ne: false
},
accessBanExpires: {
$lt: Date.now()
},
}, function (err, docs) {
// set the specific item you were after to the variable
password = docs.password
})
} catch (e) {
console.log(e)
}
let newuser;
if (remember) {
if (!user.rememberAcct) {
try {
newuser = await User.findOneAndUpdate(user._id, {
rememberAcct: true,
});
console.log('check: ', user.rememberAcct);
} catch (e) {
console.log(e)
}
} else if (user.rememberAcct) {
try {
await user.updateOne({
rememberAcct: false,
});
} catch (e) {
console.log(e)
}
}
console.log(user.rememberAcct, newuser); //Here document always updating asyncrounously, but await seems not applying to update. newUser returns the user with old property
console.log("error message", e)
}
}

Why booleans always update false?? (Moongose)

I would like to knwo why using the Query findOneAndUpdate, overwrite some data and donĀ“t update booleans, always are false.
exports.userUpdateInterest = (req, res) => {
let keys = Object.keys(req.body);
if (keys.indexOf('email') > -1) {
User.findOne({
email: req.body.email
}).exec(async (err, user) => {
console.log("execuser",user)
const update = {
onboarding: req.body.onboarding,
oneToOne: req.body.oneToOne,
nps: req.body.nps,
questions: req.body.questions,
comunication: req.body.comunication
};
try {
let document = await User.findOneAndUpdate(
{ email: user.email },
update,
{ returnOriginal: false }
);
console.log("res", document)
res
.status(200)
.send({ message: 'User update success!', user: document });
console.log("res", document)
} catch (error) {
res.status(500).send({ message: 'User update fail!' });
}
});
}else{
res.status(400).send({ message: 'Missing email field!'})
}
};
I have try to change de query and nothing is working, at least, on my unless knowment. Any help please?

Mongoose update found document [duplicate]

I have a Mongoose User model:
var User = mongoose.model('Users',
mongoose.Schema({
username: 'string',
password: 'string',
rights: 'string'
})
);
I want to find one instance of the User model, modify it's properties, and save the changes. This is what I have tried (it's wrong!):
User.find({username: oldUsername}, function (err, user) {
user.username = newUser.username;
user.password = newUser.password;
user.rights = newUser.rights;
user.save(function (err) {
if(err) {
console.error('ERROR!');
}
});
});
What is the syntax to find, modify and save an instance of the User model?
The user parameter of your callback is an array with find. Use findOne instead of find when querying for a single instance.
User.findOne({username: oldUsername}, function (err, user) {
user.username = newUser.username;
user.password = newUser.password;
user.rights = newUser.rights;
user.save(function (err) {
if(err) {
console.error('ERROR!');
}
});
});
Why not use Model.update? After all you're not using the found user for anything else than to update it's properties:
User.update({username: oldUsername}, {
username: newUser.username,
password: newUser.password,
rights: newUser.rights
}, function(err, numberAffected, rawResponse) {
//handle it
})
findOne, modify fields & save
User.findOne({username: oldUsername})
.then(user => {
user.username = newUser.username;
user.password = newUser.password;
user.rights = newUser.rights;
user.markModified('username');
user.markModified('password');
user.markModified('rights');
user.save(err => console.log(err));
});
OR findOneAndUpdate
User.findOneAndUpdate({username: oldUsername}, {$set: { username: newUser.username, user: newUser.password, user:newUser.rights;}}, {new: true}, (err, doc) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
});
Also see updateOne
I wanted to add something very important. I use JohnnyHK method a lot but I noticed sometimes the changes didn't persist to the database. When I used .markModified it worked.
User.findOne({username: oldUsername}, function (err, user) {
user.username = newUser.username;
user.password = newUser.password;
user.rights = newUser.rights;
user.markModified(username)
user.markModified(password)
user.markModified(rights)
user.save(function (err) {
if(err) {
console.error('ERROR!');
}
});
});
tell mongoose about the change with doc.markModified('pathToYourDate') before saving.
If you want to use find, like I would for any validation you want to do on the client side.
find returns an ARRAY of objects
findOne returns only an object
Adding user = user[0] made the save method work for me.
Here is where you put it.
User.find({username: oldUsername}, function (err, user) {
user = user[0];
user.username = newUser.username;
user.password = newUser.password;
user.rights = newUser.rights;
user.save(function (err) {
if(err) {
console.error('ERROR!');
}
});
});
You could also write it a little more cleaner using updateOne & $set, plus async/await.
const updateUser = async (newUser) => {
try {
await User.updateOne({ username: oldUsername }, {
$set: {
username: newUser.username,
password: newUser.password,
rights: newUser.rights
}
})
} catch (err) {
console.log(err)
}
}
Since you don't need the resulting document, you can just use updateOne instead of findOneAndUpdate.
Here's a good discussion about the difference: MongoDB 3.2 - Use cases for updateOne over findOneAndUpdate

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