Skip to content

Commit

Permalink
feat(model): add mergeHooks option to Model.discriminator() to av…
Browse files Browse the repository at this point in the history
…oid duplicate hooks

Fix #12472
  • Loading branch information
vkarpov15 committed Oct 8, 2022
1 parent b8f4cb4 commit 3f2c3cb
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 4 deletions.
10 changes: 7 additions & 3 deletions lib/helpers/model/discriminator.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
* ignore
*/

module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins) {
module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins, mergeHooks) {
if (!(schema && schema.instanceOfSchema)) {
throw new Error('You must pass a valid discriminator Schema');
}

mergeHooks = mergeHooks == null ? true : mergeHooks;

if (model.schema.discriminatorMapping &&
!model.schema.discriminatorMapping.isRoot) {
throw new Error('Discriminator "' + name +
Expand All @@ -32,7 +34,7 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu

if (applyPlugins) {
const applyPluginsToDiscriminators = get(model.base,
'options.applyPluginsToDiscriminators', false);
'options.applyPluginsToDiscriminators', false) || !mergeHooks;
// Even if `applyPluginsToDiscriminators` isn't set, we should still apply
// global plugins to schemas embedded in the discriminator schema (gh-7370)
model.base._applyPlugins(schema, {
Expand Down Expand Up @@ -179,7 +181,9 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
schema.options._id = _id;
}
schema.options.id = id;
schema.s.hooks = model.schema.s.hooks.merge(schema.s.hooks);
if (mergeHooks) {
schema.s.hooks = model.schema.s.hooks.merge(schema.s.hooks);
}

schema.plugins = Array.prototype.slice.call(baseSchema.plugins);
schema.callQueue = baseSchema.callQueue.concat(schema.callQueue);
Expand Down
3 changes: 2 additions & 1 deletion lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,7 @@ Model.exists = function exists(filter, options, callback) {
* @param {String} [options.value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter.
* @param {Boolean} [options.clone=true] By default, `discriminator()` clones the given `schema`. Set to `false` to skip cloning.
* @param {Boolean} [options.overwriteModels=false] by default, Mongoose does not allow you to define a discriminator with the same name as another discriminator. Set this to allow overwriting discriminators with the same name.
* @param {Boolean} [options.mergeHooks=true] By default, Mongoose merges the base schema's hooks with the discriminator schema's hooks. Set this option to `false` to make Mongoose use the discriminator schema's hooks instead.
* @return {Model} The newly created discriminator model
* @api public
*/
Expand Down Expand Up @@ -1238,7 +1239,7 @@ Model.discriminator = function(name, schema, options) {
schema = schema.clone();
}

schema = discriminator(this, name, schema, value, true);
schema = discriminator(this, name, schema, value, true, options.mergeHooks);
if (this.db.models[name] && !schema.options.overwriteModels) {
throw new OverwriteModelError(name);
}
Expand Down
28 changes: 28 additions & 0 deletions test/model.discriminator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2011,4 +2011,32 @@ describe('model', function() {
assert.strictEqual(doc.shape.get('a').radius, '5');
assert.strictEqual(doc.shape.get('b').side, 10);
});

it('supports `mergeHooks` option to use the discriminator schema\'s hooks over the base schema\'s (gh-12472)', function() {
const shapeSchema = Schema({ name: String }, { discriminatorKey: 'kind' });
shapeSchema.plugin(myPlugin);

const Shape = db.model('Test', shapeSchema);

const triangleSchema = Schema({ sides: { type: Number, enum: [3] } });
triangleSchema.plugin(myPlugin);
const Triangle = Shape.discriminator(
'Triangle',
triangleSchema
);
const squareSchema = Schema({ sides: { type: Number, enum: [4] } });
squareSchema.plugin(myPlugin);
const Square = Shape.discriminator(
'Square',
squareSchema,
{ mergeHooks: false }
);

assert.equal(Triangle.schema.s.hooks._pres.get('save').filter(hook => hook.fn.name === 'testHook12472').length, 2);
assert.equal(Square.schema.s.hooks._pres.get('save').filter(hook => hook.fn.name === 'testHook12472').length, 1);

function myPlugin(schema) {
schema.pre('save', function testHook12472() {});
}
});
});

0 comments on commit 3f2c3cb

Please sign in to comment.