Mongoose: findOneAndUpdate() doesn't apply setters

Created on 8 May 2016  路  4Comments  路  Source: Automattic/mongoose

Hi there, just reporting a bug.

When defining a simple schema like this

 var UserSchema = new Schema({
     name: {
         type: String,
         trim: true,
         validate: validations().name
     },
     email: {
         type: String,
         trim: true,
         unique: true,
         lowercase: true,
         validate: validations().email
     }
});

Creating a record with captial letters in email field will successfully convert email to lowercase. However, when use Model.findByIdAndUpdate, it does not respect this setting.

new feature

Most helpful comment

4.10.x has a runSettersOnQuery option that allows you to opt in to this, follow #5300 for updates on docs. Or take a look at the test case in https://github.com/Automattic/mongoose/commit/173d72efba76b5881eb604f0cbfc4ff3cf849231

All 4 comments

Currently not supported by design - findOneAndUpdate() currently doesn't apply setters, and trim is a custom setter.

4.10.x has a runSettersOnQuery option that allows you to opt in to this, follow #5300 for updates on docs. Or take a look at the test case in https://github.com/Automattic/mongoose/commit/173d72efba76b5881eb604f0cbfc4ff3cf849231

I'm also experiencing the problem of Model.findOneAndUpdate to not be calling custom setters.

const schema = mongoose.Schema({
  firstName: {
    type: String,
    required: true,
  },
  lastName: {
    type: String,
    required: true,
  },
  email: {
    type: String,
    required: true,
  },
  password: {
    type: String,
    required: true,
  },
  salt: {
    type: String,
    required: true,
  },
});

schema.path('password', {
  set(nextPassword) {
    const salt = rand();
    const password = pwd
      .hashSync(getPasswordBuffer(nextPassword, salt))
      .toString(ENCODING);

    // set salt too as they are bound together
    this.salt = salt;

    return password;
  },
});

The following doesn't call the custom setter.

const handleUserUpdate = async (updates) => {
  return User.findOneAndUpdate({ id }, updates, { new: true });
}

Everything works as expected if I run the following instead:

const handleUserUpdate = async (updates) => {
  const user = await User.findById(id);

  Object.entries(updates).forEach(([key, value]) => {
    user[key] = value;
  });

  await user.save();

  return user;
}

The update notes for v5 state that the runSettersOnQuery option was removed as setters are run by default on Queries. I'm not sure what I'm missing to get the first example to work as expected.

@SavePointSam what does your updates object look like in the handleUserUpdate() function?

Was this page helpful?
0 / 5 - 0 ratings