4

EDIT: This question was asked earlier, but I didn't do a good job of asking it. I've rewritten the question. Thanks in advance for your help!

I'm in the process of writing a simple messaging server for a school project. Among its other functionalities, the server allows the user to update the information stored in their account. When the user does update their account, an authentication token is generated for them. Here's the schema that defines all of that. Note, header and body are parts of the user input:

UserSchema = new Schema({ _id: {type: ObjectId, select: false}, username: {type: String, required: true, index: {unique: true} }, password: {type: String, required: true, select: false}, email: {type: String}, token: {type: String, select: false} }, { autoIndex: false }); UserSchema.pre("save", function(next) { // Create a new token for the user var self = this; bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) { if (err) { next(err); } else { crypto.randomBytes(256, function(err, bytes) { if (err) { next(err); } else { bytes = bytes.toString("hex"); bcrypt.hash((new Date() + bytes), salt, function(err, tokenHash) { if (err) { next(err); } else { self.token = tokenHash; next(); } }); } }); } }); }); UserSchema.pre("save", function(next) { // Hash the password before saving var self = this; if (!self.isModified("password")) { next(); } else { bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) { if (err) { next(err); } else { bcrypt.hash(self.password, salt, function(err, passwordHash) { if (err) { next(err); } else { self.password = passwordHash; next(); } }); } }); } }); 

I'm running into an issue when updating a particular user. Because I want to use the Model middleware, the way I'm updating a user is by using Model#findOne() followed by Model#save(). Here's the code I have to do that:

// Make sure user provided all necessary information. if (!header.token) { return callback(new errors.MissingHeaderDataError("Missing 'token' parameter in the header.")); } else { // Update the user account based on what's in the envelope's body. User.findOne({"token": header.token}, "+token +password", function (err, user) { if (err) { return callback(err); } else { // Get a list of all parameters the user wants to change. var paramsToChange = Object.keys(body); // Now update the parameters paramsToChange.forEach(function(param) { user[param] = body[param]; }); console.log("Updated user:"); console.dir(user); user.save(function(err, user) { if (err) { return callback(err); } else { console.log("Returned user:"); console.dir(user); User.find({}, "+token +password", function(err, foundUser) { if (err) { throw err; } else { console.log(JSON.stringify(foundUser)); } }); callback(null, new SuccessEnvelope(user)); } }); } }); } 

When I run my tests and come to the last bit of code (after save() is returned), I get this output:

Updated user: { token: '$2a$10$5VWWqjJ52aGbS4xc6NDKjuGPv8brX7pRmwiKyYjP8VHoTKCtYZiTu', username: 'jim_bob', password: '$2a$10$ue08HUsunzzzcbZURzXF7uaH1dZxF3SwkwadC6D1JsIC9xAUhTbCC', email: '[email protected]', __v: 0 } Returned user: { token: '$2a$10$fRwED..7fFFhN46Vn.ZJW..xYql5t5P39LHddjFS4kl/pmhwfT.tO', username: 'jim_bob', password: '$2a$10$ue08HUsunzzzcbZURzXF7uaH1dZxF3SwkwadC6D1JsIC9xAUhTbCC', email: '[email protected]', __v: 0 } [{"token":"$2a$10$5VWWqjJ52aGbS4xc6NDKjuGPv8brX7pRmwiKyYjP8VHoTKCtYZiTu","username":"joe_bob","password":"$2a$10$ue08HUsunzzzcbZURzXF7uaH1dZ xF3SwkwadC6D1JsIC9xAUhTbCC","email":"[email protected]","__v":0}] 

As you can see, the document is not properly saved to the database, as the previous data is still there. My question is: why? Why is the user not being updated when calling save? I think I'm doing everything properly, but obviously I'm not. Any help with this would be great since I'm going mad!

2
  • This is a lot of code. Can you simplify your question? Commented Oct 5, 2013 at 21:59
  • There we go, I simplified everything! Commented Oct 9, 2013 at 17:49

1 Answer 1

3

Apparently, in order to save a document to the database, it needs an _id. Kinda silly that Mongoose doesn't give an error when it doesn't find a document. Alas...

I updated my code to reflect the change:

 // Make sure user provided all necessary information. if (!header.token) { return callback(new errors.MissingHeaderDataError("Missing 'token' parameter in the header.")); } else { // Update the user account based on what's in the envelope's body. User.findOne({"token": header.token}, "+_id +token +password", function (err, user) { if (err) { return callback(err); } else { console.log("Found user:"); console.dir(user); // Get a list of all parameters the user wants to change. var paramsToChange = Object.keys(body); // Now update the parameters paramsToChange.forEach(function(param) { user[param] = body[param]; }); console.log("Updated user:"); console.dir(user); user.save(function(err, user, numberTouched) { if (err) { return callback(err); } else { console.log("Returned user:"); console.dir(user); console.log(numberTouched); User.find({}, "+token +password", function(err, foundUser) { if (err) { throw err; } else { console.dir(foundUser); } }); callback(null, new SuccessEnvelope(user)); } }); } }); } 
Sign up to request clarification or add additional context in comments.

3 Comments

If you leave out the + characters in your fields argument, it might work better.
Nope, they're used correctly. I want to select those fields, in addition to the ones automatically being selected
Sorry, didn't notice the select:false in your schema :) Having an _id in a document tells Mongoose that the document already exists in the database (and the save is really an update); otherwise, it assumes that you're trying to save a new document. As you found out :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.