diff --git a/lib/helpers/model/discriminator.js b/lib/helpers/model/discriminator.js index a178093a6dc..96443002ab1 100644 --- a/lib/helpers/model/discriminator.js +++ b/lib/helpers/model/discriminator.js @@ -184,8 +184,9 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu if (mergeHooks) { schema.s.hooks = model.schema.s.hooks.merge(schema.s.hooks); } - - schema.plugins = Array.prototype.slice.call(baseSchema.plugins); + if (applyPlugins) { + schema.plugins = Array.prototype.slice.call(baseSchema.plugins); + } schema.callQueue = baseSchema.callQueue.concat(schema.callQueue); delete schema._requiredpaths; // reset just in case Schema#requiredPaths() was called on either schema } diff --git a/lib/model.js b/lib/model.js index 27dd89ee5a7..8f876bc3321 100644 --- a/lib/model.js +++ b/lib/model.js @@ -1215,6 +1215,7 @@ Model.exists = function exists(filter, options, callback) { * @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. + * @param {Boolean} [options.mergePlugins=true] By default, Mongoose merges the base schema's plugins with the discriminator schema's plugins. Set this option to `false` to make Mongoose use the discriminator schema's plugins instead. * @return {Model} The newly created discriminator model * @api public */ @@ -1232,6 +1233,7 @@ Model.discriminator = function(name, schema, options) { options = options || {}; const value = utils.isPOJO(options) ? options.value : options; const clone = typeof options.clone === 'boolean' ? options.clone : true; + const mergePlugins = typeof options.mergePlugins === 'boolean' ? options.mergePlugins : true; _checkContext(this, 'discriminator'); @@ -1242,7 +1244,7 @@ Model.discriminator = function(name, schema, options) { schema = schema.clone(); } - schema = discriminator(this, name, schema, value, true, options.mergeHooks); + schema = discriminator(this, name, schema, value, mergePlugins, options.mergeHooks); if (this.db.models[name] && !schema.options.overwriteModels) { throw new OverwriteModelError(name); } diff --git a/test/model.discriminator.test.js b/test/model.discriminator.test.js index 919101de4bf..3e34bfa7b62 100644 --- a/test/model.discriminator.test.js +++ b/test/model.discriminator.test.js @@ -2039,4 +2039,42 @@ describe('model', function() { schema.pre('save', function testHook12472() {}); } }); + + it('supports `mergePlugins` option to use the discriminator schema\'s plugins over the base schema\'s (gh-12604)', function() { + let pluginTimes = 0; + const shapeDef = { name: String }; + const shapeSchema = Schema(shapeDef, { discriminatorKey: 'kind' }); + shapeSchema.plugin(myPlugin, { opts1: true }); + + const Shape = db.model('Test', shapeSchema); + + const triangleSchema = Schema({ ...shapeDef, sides: { type: Number, enum: [3] } }); + triangleSchema.plugin(myPlugin, { opts2: true }); + const Triangle = Shape.discriminator( + 'Triangle', + triangleSchema + ); + + const squareSchema = Schema({ ...shapeDef, sides: { type: Number, enum: [4] } }); + squareSchema.plugin(myPlugin, { opts3: true }); + const Square = Shape.discriminator( + 'Square', + squareSchema, + { mergeHooks: false, mergePlugins: false } + ); + + assert.equal(Triangle.schema.s.hooks._pres.get('save').filter(hook => hook.fn.name === 'testHook12604').length, 2); + assert.equal(Square.schema.s.hooks._pres.get('save').filter(hook => hook.fn.name === 'testHook12604').length, 1); + + const squareFilteredPlugins = Square.schema.plugins.filter((obj) => obj.fn.name === 'myPlugin'); + assert.equal(squareFilteredPlugins.length, 1); + assert.equal(squareFilteredPlugins[0].opts['opts3'], true); + + assert.equal(pluginTimes, 3); + + function myPlugin(schema) { + pluginTimes += 1; + schema.pre('save', function testHook12604() {}); + } + }); }); diff --git a/test/types/discriminator.test.ts b/test/types/discriminator.test.ts index 1f32fd1149b..0519345322c 100644 --- a/test/types/discriminator.test.ts +++ b/test/types/discriminator.test.ts @@ -21,7 +21,7 @@ doc.email = 'hello'; const Disc2 = Base.discriminator( 'Disc2', new Schema({ email: { type: String } }), - { value: 'test', mergeHooks: false } + { value: 'test', mergeHooks: false, mergePlugins: false } ); function test(): void { diff --git a/types/models.d.ts b/types/models.d.ts index 8c479d2a1ca..de5dad33b91 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -6,6 +6,7 @@ declare module 'mongoose' { clone?: boolean; overwriteModels?: boolean; mergeHooks?: boolean; + mergePlugins?: boolean; } export interface AcceptsDiscriminator {