Skip to content

Commit

Permalink
feat(query): add overwriteDiscriminatorKey option that allows changin…
Browse files Browse the repository at this point in the history
…g the discriminator key in `findOneAndUpdate()`, `updateOne()`, etc.

Fix #6087
  • Loading branch information
vkarpov15 committed Jun 30, 2020
1 parent 9cf75ff commit eeb9c9e
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 2 deletions.
21 changes: 19 additions & 2 deletions lib/query.js
Expand Up @@ -1293,7 +1293,8 @@ Query.prototype.getOptions = function() {
* - [writeConcern](https://docs.mongodb.com/manual/reference/method/db.collection.update/)
* - [timestamps](https://mongoosejs.com/docs/guide.html#timestamps): If `timestamps` is set in the schema, set this option to `false` to skip timestamps for that particular update. Has no effect if `timestamps` is not enabled in the schema options.
* - omitUndefined: delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
*
* - overwriteDiscriminatorKey: allow setting the discriminator key in the update. Will use the correct discriminator schema if the update changes the discriminator key.
*
* The following options are only for `find()`, `findOne()`, `findById()`, `findOneAndUpdate()`, and `findByIdAndUpdate()`:
*
* - [lean](./api.html#query_Query-lean)
Expand Down Expand Up @@ -1361,6 +1362,10 @@ Query.prototype.setOptions = function(options, overwrite) {
this._mongooseOptions.setDefaultsOnInsert = options.setDefaultsOnInsert;
delete options.setDefaultsOnInsert;
}
if ('overwriteDiscriminatorKey' in options) {
this._mongooseOptions.overwriteDiscriminatorKey = options.overwriteDiscriminatorKey;
delete options.overwriteDiscriminatorKey;
}

return Query.base.setOptions.call(this, options);
};
Expand Down Expand Up @@ -4485,6 +4490,19 @@ Query.prototype._post = function(fn) {

Query.prototype._castUpdate = function _castUpdate(obj, overwrite) {
let strict;
let schema = this.schema;

const discriminatorKey = schema.options.discriminatorKey;
const baseSchema = schema._baseSchema ? schema._baseSchema : schema;
if (this._mongooseOptions.overwriteDiscriminatorKey &&
obj[discriminatorKey] != null &&
baseSchema.discriminators) {
const _schema = baseSchema.discriminators[obj[discriminatorKey]];
if (_schema != null) {
schema = _schema;
}
}

if ('strict' in this._mongooseOptions) {
strict = this._mongooseOptions.strict;
} else if (this.schema && this.schema.options) {
Expand All @@ -4508,7 +4526,6 @@ Query.prototype._castUpdate = function _castUpdate(obj, overwrite) {
upsert = this.options.upsert;
}

let schema = this.schema;
const filter = this._conditions;
if (schema != null &&
utils.hasUserDefinedProperty(filter, schema.options.discriminatorKey) &&
Expand Down
29 changes: 29 additions & 0 deletions test/model.update.test.js
Expand Up @@ -3478,4 +3478,33 @@ describe('model: updateOne: ', function() {
});
});
});

describe('overwriteDiscriminatorKey', function() {
it('allows changing discriminator key in update (gh-6087)', function() {
const baseSchema = new Schema({}, { discriminatorKey: 'type' });
const baseModel = db.model('Test', baseSchema);

const aSchema = Schema({ aThing: Number }, { _id: false, id: false });
const aModel = baseModel.discriminator('A', aSchema);

const bSchema = new Schema({ bThing: String }, { _id: false, id: false });
const bModel = baseModel.discriminator('B', bSchema);

return co(function*() {
// Model is created as a type A
let doc = yield baseModel.create({ type: 'A', aThing: 1 });

yield aModel.updateOne(
{ _id: doc._id },
{ type: 'B', bThing: 'two' },
{ runValidators: true, overwriteDiscriminatorKey: true }
);

doc = yield baseModel.findById(doc);
assert.equal(doc.type, 'B');
assert.ok(doc instanceof bModel);
assert.equal(doc.bThing, 'two');
});
});
});
});

0 comments on commit eeb9c9e

Please sign in to comment.