Mongoose: Modify this._update in pre-update middleware does not work

Created on 24 Aug 2015  路  2Comments  路  Source: Automattic/mongoose

[using mongoose v4.0.7]

Is it possible to modify the this._update object in pre-middleware? I have some middleware set up to transform a certain field if present in the update and i've found that if the field is in the top-level of the update object, the transformed value is stored, but if the value is in a $set command, the untransformed value is stored. To illustrate:

UserSchema.pre('update', function(next) {
  var update = this._update;
  if (update.$set && update.$set.name) {
    // this does not work...whatever was originally in update.$set.name is saved
    update.$set.name = transform(update.$set.name);
  } else if (update.name) {
    // this works...the transformed name is saved
    update.name = transform(update.name);
  }
  next();
})

Is this expected? I've noticed that I can do this to make it work:

if (update.$set && update.$set.name) {
  update.name = transform(update.$set.name);
}

but it seems that this would limit my ability to make adjustments in middleware for $push, $inc, etc operations.

Possibly related to issue #3024

Most helpful comment

The below script works in 4.1.5, looks like #3024 may have fixed this. The update op should always be under $set in pre-update middleware, mongoose handles that casting when you call .update() but before the hooks happen.

var assert = require('assert');
var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost/gh3303');
mongoose.set('debug', true);

var UserSchema = new mongoose.Schema({
  name: String,
  test: String
});

UserSchema.pre('update', function(next) {
  var update = this._update;
  if (update.$set && update.$set.name) {
    this.update({}, { name: transform(update.$set.name) });
  }

  next();
});

var User = mongoose.model('gh3303', UserSchema);

User.update({}, { name: 'abc', test: 'abc2' }, function(error) {
  assert.ifError(error);
  User.update({}, { $set: { name: 'abc', test: 'abc2' } }, function(error) {
    User.update({}, { $set: { test: 'abc2' } }, function(error) {
      process.exit(0);
    });
  });
});

function transform(str) {
  return str.toUpperCase();
}

I get the expected queries:

$ node gh-3303.js 
Mongoose: gh3303.update({}) { '$set': { test: 'abc2', name: 'ABC' } } {} 
Mongoose: gh3303.update({}) { '$set': { name: 'ABC', test: 'abc2' } } {} 
Mongoose: gh3303.update({}) { '$set': { test: 'abc2' } } {}

There should be no need to update the _update object directly (and its very much recommended that you do that), just use this.update().

Looks like this works, so I'm gonna close it. Re-open if this is still an issue in 4.1.x

All 2 comments

The below script works in 4.1.5, looks like #3024 may have fixed this. The update op should always be under $set in pre-update middleware, mongoose handles that casting when you call .update() but before the hooks happen.

var assert = require('assert');
var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost/gh3303');
mongoose.set('debug', true);

var UserSchema = new mongoose.Schema({
  name: String,
  test: String
});

UserSchema.pre('update', function(next) {
  var update = this._update;
  if (update.$set && update.$set.name) {
    this.update({}, { name: transform(update.$set.name) });
  }

  next();
});

var User = mongoose.model('gh3303', UserSchema);

User.update({}, { name: 'abc', test: 'abc2' }, function(error) {
  assert.ifError(error);
  User.update({}, { $set: { name: 'abc', test: 'abc2' } }, function(error) {
    User.update({}, { $set: { test: 'abc2' } }, function(error) {
      process.exit(0);
    });
  });
});

function transform(str) {
  return str.toUpperCase();
}

I get the expected queries:

$ node gh-3303.js 
Mongoose: gh3303.update({}) { '$set': { test: 'abc2', name: 'ABC' } } {} 
Mongoose: gh3303.update({}) { '$set': { name: 'ABC', test: 'abc2' } } {} 
Mongoose: gh3303.update({}) { '$set': { test: 'abc2' } } {}

There should be no need to update the _update object directly (and its very much recommended that you do that), just use this.update().

Looks like this works, so I'm gonna close it. Re-open if this is still an issue in 4.1.x

@vkarpov15 I did the same thing you did but this results in two update queries for same document. One is the normal update and other one is in pre. This is how i did it #4389

Was this page helpful?
0 / 5 - 0 ratings