leonsPAPA
leonsPAPA

Reputation: 797

How to update some but not all fields in Mongoose

here is the UserSchema:

var UserSchema = new Schema({
    username: { type: String, required: true, index:{unique: true} },
    firstName: { type: String, required: true },
    lastName: { type: String, required: true },
    email: { type: String, required: true, index:{unique: true} }, 
    password: { type: String, required: true, select: false }
});

Here is the http PUT request:

  // update user information
  api.put('/users/:username', function(req, res) {

    User.findOne({username: req.params.username}, function(err, user) {

      if (err){
        res.send(err);
        return;
      }

      if (!user){
        res.status(404).send({
          success: false,
          message: "user not found"
        });
      } else {
        user.username = req.body.username;
        user.email = req.body.email;
        user.password = req.body.password;
        user.firstName = req.body.firstName;
        user.lastName = req.body.lastName;

        user.save(function(err) {
          if (err){
            res.send(err);
            return;
          }

          res.json({
            success: true,
            message: "user information updated."
          });
        });
      }
    });
  });

The question is, if the user only want to update limited fields, for example, only update username, then the above code does not work, the error looks like this:

{
  "message": "User validation failed",
  "name": "ValidationError",
  "errors": {
    "lastName": {
      "properties": {
        "type": "required",
        "message": "Path `{PATH}` is required.",
        "path": "lastName"
      },
      "message": "Path `lastName` is required.",
      "name": "ValidatorError",
      "kind": "required",
      "path": "lastName"
    },
    "firstName": {
      "properties": {
        "type": "required",
        "message": "Path `{PATH}` is required.",
        "path": "firstName"
      },
.........

so how can I implemement to allow user updates some but not all fields?

Any comments and suggestions are appreciated!

Upvotes: 4

Views: 17233

Answers (5)

Maycon Mesquita
Maycon Mesquita

Reputation: 4590

I ended up using this solution:

const dot = require('dot-object'); // this package works like magic

const updateData = { some: true, fields: true };

User.updateOne(
  { _id: req.user._id },
  { $set: dot.dot(updateData) },
  (err, results) => {
    if (err) res.json({ err: true });
    else res.json({ success: true });
  }
);

I found this idea here: https://github.com/Automattic/mongoose/issues/5285#issuecomment-439378282

Upvotes: 5

Omar De Angelis
Omar De Angelis

Reputation: 41

This is a good compromise:

Specify the fields that the user can update

let fieldToUpdate = {
    name: req.body.name,
    email: req.body.email,
  };

Then delete all the keys that contains falsy value

  for (const [key, value] of Object.entries(fieldToUpdate)) {
    if (!value) {
      delete fieldToUpdate[key];
    }
  }

Then Update the value using the $set operator

const user = await User.findByIdAndUpdate(
    req.user.id,
    { $set: { ...fieldToUpdate } },
    {
      runValidators: true,
      new: true,
    }
  );

Upvotes: 4

anthony
anthony

Reputation: 104

From what I understand is that you want to be able to update any amount of fields. The code below is from a past project.

Model

const ingredientSchema = mongoose.Schema({
    _id: mongoose.Schema.Types.ObjectId,
    name: { type:String, required: true },   
    quantity: { type: Number, default: 0}
});

HTTP PUT

router.put('/:ingredientId', (req, res, next) => {
    // extracting ingredient id from url parameters
    const id = req.params.ingredientId;

    //creating a map from the passed array
    const updateOps = {};
    for(const ops of req.body){
        updateOps[ops.propName] = ops.value;
    }

    //updating the found ingredient with the new map
    Ingredient.update({_id: id}, { $set: updateOps})
        .exec()
        .then(result =>{
            console.log(result);
            //returning successful operation information
            res.status(200).json(result);
        })
        //catching any errors that might have occured from above operation
        .catch(err => {
        console.log(err);
            //returning server error
            res.status(500).json({
                error: err
            });
        });
});

PUT Request (json)

[
    {"propName": "name", "value": "Some other name"},
    {"propName": "quantity", "value": "15"},
]

or if you one want to update one field

[
    {"propName": "name", "value": "Some other name"}
]

basically you have an array of these property/field names and their new values. you can update just one or all of them this way if you would like. Or none of them I believe.

Hopefully, this helps! if you have any questions just ask!

Upvotes: 2

Moad Ennagi
Moad Ennagi

Reputation: 1073

Using findOneAndUpdate with the operator $set in the update object:

User.findOneAndUpdate({username: req.params.username}, { $set: req.body }, { new: true }, callback);

$set will allow you to modify only the supplied fields in the req.body object.

Upvotes: 9

Jason Cust
Jason Cust

Reputation: 10899

You can use the 'findOneAndUpdate' method.

User.findOneAndUpdate({username: req.params.username}, {username: req.body.username}, function(err, user) {
  //...
});

Upvotes: 2

Related Questions