Update Mongoose: Help on how to update document in mongoose - node.js

I am newbie and working for my thesis.
I was able to create insert and delete function but getting issues updating the data.
The below is the code under the controllers folder:
Insert Document:
module.exports.register = (params) => {
let user = new User({
firstName: params.firstName,
lastName: params.lastName,
department: params.department,
position: params.position,
email: params.email,
mobileNo: params.mobileNo,
password: bcrypt.hashSync(params.password, 10),
isAdmin: params.isAdmin,
departments: {
departmentId: params.departmentId
}
})
return user.save().then((user, err) => {
return (err) ? false : true
})
}
Delete Document:
module.exports.deleteUser = (params) => {
return User.findByIdAndRemove(params.userId).then((user, err) => {
return (err) ? false : true
})
}
While the below is the code for the routers:
Insert Document:
router.post('/register', (req, res) => {
UserController.register(req.body).then(result => res.send(result))
});
Delete Document:
router.delete('/delete/:id', (req, res) => {
let userId = req.params.id
UserController.deleteUser({userId}).then(user => res.send(user))
});
What I've tried to far is to use the delete logic but instead of findOneAndRemove, I used findOneAndUpdate but it's not updating the data. It just sending true value but not updating the document.
I've also tried some of the logic in YouTube and some here but it doesn't matched the way we construct the data so I'm having difficulties understanding them.
The target is to update the same values in the register using the id of the user.
I'm trying to update using this code:
Controllers:
module.exports.updateUser = (params) => {
return User.findOneAndUpdate(params.userId, (user, err) => {
return (err) ? false : true
})
}
Routes:
router.post('/update/:id', (req, res) => {
UserController.updateUser(req.params.id).then(user => res.send(user))
});
I've also tried to add the parameters but it's not working. What I want to update is the whole details example:
firstName: params.firstName,
lastName: params.lastName,
department: params.department,
position: params.position,
email: params.email,
mobileNo: params.mobileNo,
password: bcrypt.hashSync(params.password, 10),
isAdmin: params.isAdmin,
departments: {
departmentId: params.departmentId
}
Sample User:
{
"userId": "60f649bd8896c80004b3ffbe",
"firstName": "Jane",
"lastName": "Joe",
"department": "Accounting",
"position": "head",
"email": "janedoe#mail.com",
"mobileNo": "0",
"password": "pass123",
"isAdmin": "yes",
"departments": {
"departmentId": "60efcbec769cf60004b85319"
}
}
Here's my update code:
Controller:
module.exports.updateUserData = (params) => {
const dataToUpdate = {
firstName: params.firstName,
lastName: params.lastName,
department: params.department,
position: params.position,
email: params.email,
mobileNo: params.mobileNo,
password: bcrypt.hash(params.password, 10),
isAdmin: params.isAdmin,
departments: {
departmentId: params.departmentId
}
}
User.findOneAndUpdate({userId:params.userId}, {$set:dataToUpdate}, {new: true}, (err, doc) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
});
}
Routers:
router.post('/update-user/:id', (req, res) => {
let userId = req.params.id;
UserController.updateUserData({userId}).then(doc => res.send(doc))
})
Using this code I am getting error message: TypeError: Cannot read property 'then' of undefined under routers folder.

You should try following. It might meet your requirement
const dataToUpdate = {
firstName: params.firstName,
lastName: params.lastName,
department: params.department,
position: params.position,
email: params.email,
mobileNo: params.mobileNo,
password: bcrypt.hashSync(params.password, 10),
isAdmin: params.isAdmin,
departments: {
departmentId: params.departmentId
}
}
User.findOneAndUpdate({userId:params.userId}, {$set:dataToUpdate}, {new: true}, (err, doc) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
});

Related

Keep track of last login date with NodeJS and MongoDB

I'm working on adding a last login functionality to my Node app and can't seem to get it to work. Here's what I've got for a mongoose user schema:
userSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
password: String,
email: {
type: String,
unique: true
},
avatar: String,
firstName: String,
lastName: String,
lastLogin: {
type: Date,
default: Date.now
},
resetPasswordToken: String,
resetPasswordExpires: Date,
isAdmin: {
type: Boolean,
default: false
}
});
userSchema.plugin(passportLocalMongoose);
userSchema.statics.newLogin = function login(id, callback) {
return this.findByIdAndUpdate(id,{'$set' : { 'lastLogin' : Date.now()} }, { new : true }, callback);
};
module.exports = mongoose.model("User", userSchema);
Here's my login route:
router.post("/login", passport.authenticate("local",
{
failureRedirect: "/login"
}), function(req, res) {
User.findOneAndUpdate(req.username, {lastLogin: Date.now()}, (err, data) => {
if(err) console.log(err);
else console.log("Successfully updated the lastLogin", data);
res.redirect("/players");
});
});
I've been able to get the initial date to stick when the account is created; however, when I login with that same account the date remains unchanged. What am I missing here?
There are a few other questions with similar topics, but none seem to resolve my issue. Specifically, this question where I've implemented part of the solution to no avail. Thanks in advance for the help!
Here's the code currently being tested, example req.body:
{ username: 'anyUserHere', password: 'anyPasswordHere' }
req.user:
{
isAdmin: true,
_id: 5e9b301a6bb78973c9ec8fae,
username: 'anyUserHere',
salt: 'saltValue',
hash: 'hashValue',
__v: 0,
avatar: '../images/admin.jpg',
email: 'example#example.com',
firstName: 'first',
lastName: 'last',
password: 'anyPasswordHere',
lastLogin: 2020-05-22T18:35:50.941Z
}
So in this case, the 'anyUserHere' example should be the one being updated, but the update occurs to the first user in Mongo. Console output:
Successfully updated the lastLogin {
isAdmin: false,
_id: 5e939f988ced3e0428c8b521,
username: 'test',
__v: 0,
lastLogin: 2020-05-22T18:38:59.836Z
}
Can you update the User.newLogin(); with the below code and try
User.newLogin(id, (err, data) => {
if(err) console.log(err);
else console.log("Successfully updated the lastLogin", data);
});
router.post("/login", passport.authenticate("local",
{
failureRedirect: "/login"
}), function(req, res) {
User.newLogin(id, (err, data) => {
if(err) console.log(err);
else console.log("Successfully updated the lastLogin", data);
res.redirect("/players");
});
});
Edit
According to the current approach with findOneAndUpdate you need to make the following updated to the filter
User.findOneAndUpdate({username: req.username}, {lastLogin: Date.now()}, (err, data) => {
if(err) console.log(err);
else console.log("Successfully updated the lastLogin", data);
res.redirect("/players");
});

Response is not defined for a mongoose function findOneAndUpdate?

I am updating and returning(new Object) a existing object in the database with mongoose findOneAndUpdate but getting an error
Error
response is not defined
at Function.module.exports.updateProfile ........
In router File
router.post('/edit_profile', (req, res) => {
let updateProfile = new Profile({
name: req.body.name,
email: req.body.email,
username: req.body.username,
gender: req.body.gender,
bio: req.body.bio,
user_id: req.body.user_id
});
console.log(updateProfile); //consoling data Place(1)
Profile.updateProfile(updateProfile.user_id, (err, user) => {
if (err) throw err;
else {
console.log("Update User");
console.log(user);
res.json({
user: user
})
}
})
})
consoled data at Place(1)
{ _id: 5c9cd517b3b7db248c6d7981,
name: 'Shivva',
email: 'ritinbhardwaj933#gmail.com',
username: 'zzz',
gender: 'Male',
bio: 'I am HOwdy Member',
user_id: '5c9cd47bf3d9bb1ea8cbfcbe' }
In profile.js
module.exports.updateProfile = (id, callback) => {
let query = { user_id: id };
console.log(query); //consoling data Place(2)
Profile.findOneAndUpdate(query, { $set: response }, { new: true }, (err, user) => {
if (err) throw err;
else {
callback(null, user);
}
});
}
consoled data at Place(2)
{ user_id: '5c9cd47bf3d9bb1ea8cbfcbe' }
Error
The error i am getting is response is not defined a the Function.module.exports.updateProfile
Error
the accepted solution worked but now it is returning the error
collection.findAndModify is deprecated. Use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead
If you look closely, in this line you have used variable response but never have you initialised it.
Profile.findOneAndUpdate(query, { $set: response }, { new: true }, (err, user) => {
That response word should be replaced with an object with whatever changes you want eg.{ name: 'jason bourne' }
And honestly you don't need to create an instance like what you have done below because you aren't using that anywhere.
let updateProfile = new Profile({
name: req.body.name,
email: req.body.email,
username: req.body.username,
gender: req.body.gender,
bio: req.body.bio,
user_id: req.body.user_id
});

Many to one relationships in mongoose. How to access and output the referred to object ID's?

I'm trying to create a "wishlist" feature for users on my node / mongo application. I've assumed the best way to model the scheme would be to reference the items they like. So far my reading has brought me to this point (I'm not very familiar with the Types.ObjectID):
Schema Model
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
password: {
type: String,
required: true
},
wishlist: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Wishlist",
required: true
}]
});
I've managed to write some code which pushes the relevant _id into the "Likes" array:
Product.findById(productID).exec(function (err, user) {
User.updateOne({ _id: req.session.userId }, { "$push": { "wishlist": productID } }, function (err, user) {
if (err) {
console.log("Failed to add")
} else {
console.log(productID + " has been added")
}
});
});
This outputs in the database like so:
{
"_id" : ObjectId("5c3f7e1f1268203b1f31cb17"),
"email" : "email",
"password" : "password",
"__v" : 0,
"wishlist" : [
ObjectId("5c41f4b42f82b14798d5c7fc"),
ObjectId("5c41f4b42f82b14798d5c7ff")
]
}
I'm stuck on how I'd output these wishlist items in my template. My assumption was to get the data like this:
router.get('/wishlist', middleware.requiresLogin, function(req, res, next) {
User.findOne({ _id: req.session.userId }, function(err, user) {
res.render('wishlist', {
title: 'Wishlist',
template: 'wishlist',
saved: user.wishlist,
header: true,
footer: true
});
});
});
And the loop through the items like this:
{{#each saved }} Code goes here {{/each }}
Am I approaching this correctly?
you'll need to populate the wishlist field, try this,
User.findOne({ _id: req.session.userId }).
populate('wishlist').
exec(function (err, user) {
res.render('wishlist', {
title: 'Wishlist',
template: 'wishlist',
saved: user.wishlist,
header: true,
footer: true
});
});
You can refer to the Populate (mongoose documentation).
//User_controller.js
exports.getUser = (req, res) => {
User.findOne({ _id: req.session.userId })
.populate('wishlist')
.then((user) => { res.json(user) })
.catch((error) => { res.status(500).json({ error })
});
};
// UserRoute.js
const express = require("express");
const router = express.Router();
const userCtrl = require('./user_controller');
router.get('/:id', userCtrl.getUser);
module.exports = router;
//server.js
//...
const userRoute = require("./UserRoute");
app.use("/user", userRoute);
//...

Cannot access Sequelize instance methods

I get the following error when I attempt to call the generateHash instance method I've defined on my User model:
User.generateHash(...).then is not a function
Here's the model definition itself:
const User = sequelize.define('User',
{
firstName: {
type: Sequelize.TEXT,
field: 'first_name'
},
lastName: {
type: Sequelize.TEXT,
allowNull: false,
field: 'last_name'
},
userName: {
type: Sequelize.TEXT,
field: 'user_name',
allowNull: false
},
password: {
type: Sequelize.TEXT,
allowNull: false
}
}, {
tableName: 'users',
underscored: true,
classMethods: {
associate: function(models) {
User.hasMany(
models.Trip,
{
as: 'trips',
foreignKey: {
name: 'userId',
field: 'user_id',
allowNull: false
},
onDelete: 'CASCADE'
});
},
},
instanceMethods: {
generateHash: function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
validatePassword: function(password) {
return bcrypt.compareSync(password, this.password);
},
apiRepr: function() {
return {
id: this.id,
firstName: this.firstName,
lastName: this.lastName,
userName: this.userName
};
}
}
});
Here's the endpoint where I attempt to call the method:
router.post('/', (req, res) => {
let {userName, password, firstName, lastName} = req.body;
// if no existing user, hash password
return User.generateHash(password)
.then(hash => {
// create new user
return User.create({
firstName: firstName,
lastName: lastName,
userName: userName,
password: hash
});
})
.then(user => {
// send back apirRepr data
return res.status(201).json(user.apiRepr());
})
// error handling
.catch(err => {
if (err.name === 'AuthenticationError') {
return res.status(422).json({message: err.message});
}
res.status(500).json({message: 'Internal server error'});
});});
I'm totally stuck. Any ideas?
In sequelize V4 class and instance methods are removed.
Now you have to make it this way:
const Model = sequelize.define('Model', {
...
});
// Class Method
Model.associate = function (models) {
...associate the models
};
// Instance Method
Model.prototype.someMethod = function () {..}
More information here Sequelize v4 breaking changes
You are calling .then() on something that does not return a promise. Try this:
router.post('/', (req, res) => {
let {userName, password, firstName, lastName} = req.body;
let hash = User.generateHash(password);
// if no existing user, hash password
return User.create({
firstName: firstName,
lastName: lastName,
userName: userName,
password: hash
})
.then(user => {
// send back apirRepr data
return res.status(201).json(user.apiRepr());
})
// error handling
.catch(err => {
if (err.name === 'AuthenticationError') {
return res.status(422).json({message: err.message});
}
return res.status(500).json({message: 'Internal server error'});
});

Mongoose API get nested items in array by _id

I am building an api with Express and Mongoose (Backbone on the front). I have a Mongoose User model that contains an array called "orders". I need to set up a create method that will READ a Single Order by ID.
When I navigate to:
http://localhost:3000/test/
I get the following for a logged in user:
{
"__v": 0,
"_id": "537d09a1fe47a00000c54514",
"kittenType": "Grumpy",
"local": {
"petname": "Smeagol",
"password": "$2a$08$X4sF5UmYZ3/2cxfRzpPcq.pphYFRKcb.6xBGupdUyUMgWJlFSr/uq",
"email": "julie#gmail.com"
},
"orders": [
{
"title": "Big Max Fountain",
"description": "Large capacity drinking fountain",
"quantity": "2",
"price": 500,
"_id": "53837e9e681808e6ea9f9ca4",
"modified": "2014-05-28T23:49:10.232Z"
},
{
"title": "Lotus Fountain",
"description": "Tranquil pools of water",
"quantity": "1",
"price": 1000,
"_id": "53867762ff514df026b608fa",
"modified": "2014-05-28T23:55:16.263Z"
}
]
}
When I navigate to:
http://localhost:3000/test/orders
I send the list of orders for the logged in user (this gives me the array of orders:
app.get('/test/orders', function(req, res) {
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
res.send(user.orders);
}
});
});
How do I then send each order by id?
app.get('/test/orders/:id', function(req, res) {
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
//send the order by id here thru the url
}
});
});
ADDED AFTER RESPONSE:
var userSchema = mongoose.Schema({
user : {
type: mongoose.Schema.ObjectId,
ref: 'User'
},
orders: [{
title: String,
description: String,
quantity : String,
price : Number,
modified: { type: Date, default: Date.now }
}],
signup: [{
name: String,
courseDay: String,
time: String,
location: String,
modified: { type: Date, default: Date.now }
}],
kittenType : String,
profilePhoto : String,
profilePage : String,
local : {
email : String,
password : String,
petname : String,
path : String,
}
routes:
app.get('/test', function(req,res) {
res.send(res.locals.user);
});
app.get('/test/orders', function(req, res) {
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
res.send(user.orders);
}
});
});
app.post('/api/orders', isLoggedIn, function (req, res){
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
user.orders.quantity = req.body.quantity;
user.orders.description = req.body.description;
user.orders.title = req.body.title;
user.orders.price = req.body.price;
user.orders.modified = req.body.modified;
user.update({$push: { "orders" :
{ title: user.orders.title,
description: user.orders.description,
quantity: user.orders.quantity,
price: user.orders.price,
modified: user.orders.modified
}
}},{safe:true, upsert:true},function(err){
if(err){
console.log(err);
} else{
console.log("Successfully added" + user.orders);
}
});
console.log('located a user');
}
});
});
WORKING GET METHOD:
app.get('/test/orders/:id', function(req, res) {
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
console.log(user.orders);
var order = user.orders.filter(function(e){ return e._id == req.params.id })[0]
console.log(order);
res.send(order);
}
});
});
I think you don't need to find a user in this case. Enough to find Order with proper condition:
app.get('/test/orders/:id', function(req, res) {
Order.findOne({'_id': req.params.id, 'user_id': req.user.id }, function(err, order) {
if (err)
return done(err);
if (order) {
res.send(order);
}
});
});
But you should log req to be sure that you use proper ids. It depends also on your routes, that you didn't public.
Or if you need to find User model, you can simply use filter method. Code will be almost the same as in first method:
app.get('/test/orders/:id', function(req, res) {
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
console.log(user.orders); // returns an array
// console.log(req.id); // to be sure that it returns proper order id
// perhaps it could be next
console.log(req.params.id);
var order_id = user.orders.filter(function(e){ return e == req.params.id })[0]
// then find this order
Order.findOne({'_id': order_id }, function(err1, order) {
if (err1)
return done(err1);
if (order) {
res.send(order);
}
});
}
});
});

Resources