53

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?

Randomblue
  • 112,777
  • 145
  • 353
  • 547

6 Answers6

124

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!');
        }
    });
});
JohnnyHK
  • 305,182
  • 66
  • 621
  • 471
  • 5
    @DeLac No, the `save` performs an update operation in this case. – JohnnyHK Apr 14 '16 at 01:22
  • @DeLac, you may see the duplication key error from saving the User `_id`. In this case, try to update the properties separately or remove `_id` first prior to `save`. – wag0325 Apr 14 '16 at 01:39
  • I can't remove the _id, since other objects refer to it. If I remove it, I guess mongo will choose another _id for it. the update doesn't execute the pre-save functions. I didn't know that save(), if applied to an existing object, update it. I am not sure how mongo can know when it's an update, and when instead it's an illegal attempt to save a new object with an existing _id – DeLac Apr 21 '16 at 14:58
  • 1
    @DeLac Mongoose keeps track of that via the [`isNew`](http://mongoosejs.com/docs/api.html#document_Document-isNew) property on Mongoose document instances. – JohnnyHK Apr 21 '16 at 15:25
  • 1
    VERY IMPORTANT TIP. Sometimes I have to use `.markModified` on the doc before saving. If I didn't, the changes wouldn't be saved to the DB. http://mongoosejs.com/docs/schematypes.html#usage-notes – jack blank Jul 22 '18 at 01:27
  • @jackblank that only applies when modifying Date's with built JavaScript functions. From the doc "Built-in Date methods are not hooked into the mongoose change tracking logic which in English means that if you use a Date in your document and modify it with a method like setMonth(), mongoose will be unaware of this change and doc.save() will not persist this modification." Doesn't apply in this particular example – AshotN Aug 24 '18 at 01:46
57

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
})
soulcheck
  • 36,297
  • 6
  • 91
  • 90
  • 12
    Yes, but be awarded of this "Model.update is sending the command directly to MongoDB - documents are not returned - so there's nothing to run validation on" - Aaron Heckmann https://github.com/LearnBoost/mongoose/issues/635 – jackdbernier Mar 09 '13 at 21:41
  • 7
    Please note that the Model.update does not pass the validation defined in the Schema! See first paragraph on: http://mongoosejs.com/docs/validation.html – Bas van Ommen Sep 10 '13 at 06:38
8

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

Anthony Awuley
  • 3,455
  • 30
  • 20
5

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.

jack blank
  • 5,073
  • 7
  • 41
  • 73
4

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!');
        }
    });
});
Gulfaraz Rahman
  • 417
  • 4
  • 13
  • 17
    Suggesting not to do validation on the server is horrible advice. – VtoCorleone Jun 29 '16 at 14:45
  • 3
    @VtoCorleone the OP's problem is not validation, it's access.Once you have access to the object, you are free to write any logic around it be it validation or modification. – Gulfaraz Rahman Jun 30 '16 at 09:30
1

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