Skip to content

Commit c8d6acf

Browse files
committed
fix(model): make Model.recompileSchema() also re-apply discriminators
Fix #14444
1 parent 131d13c commit c8d6acf

File tree

5 files changed

+61
-6
lines changed

5 files changed

+61
-6
lines changed

lib/helpers/discriminator/applyEmbeddedDiscriminators.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
module.exports = applyEmbeddedDiscriminators;
44

5-
function applyEmbeddedDiscriminators(schema, seen = new WeakSet()) {
5+
function applyEmbeddedDiscriminators(schema, seen = new WeakSet(), overwriteExisting = false) {
66
if (seen.has(schema)) {
77
return;
88
}
@@ -16,13 +16,17 @@ function applyEmbeddedDiscriminators(schema, seen = new WeakSet()) {
1616
if (!schemaType.schema._applyDiscriminators) {
1717
continue;
1818
}
19-
if (schemaType._appliedDiscriminators) {
19+
if (schemaType._appliedDiscriminators && !overwriteExisting) {
2020
continue;
2121
}
2222
for (const discriminatorKey of schemaType.schema._applyDiscriminators.keys()) {
2323
const discriminatorSchema = schemaType.schema._applyDiscriminators.get(discriminatorKey);
2424
applyEmbeddedDiscriminators(discriminatorSchema, seen);
25-
schemaType.discriminator(discriminatorKey, discriminatorSchema);
25+
schemaType.discriminator(
26+
discriminatorKey,
27+
discriminatorSchema,
28+
overwriteExisting ? { overwriteExisting: true } : null
29+
);
2630
}
2731
schemaType._appliedDiscriminators = true;
2832
}

lib/helpers/model/discriminator.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
2121
* ignore
2222
*/
2323

24-
module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins, mergeHooks) {
24+
module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins, mergeHooks, overwriteExisting) {
2525
if (!(schema && schema.instanceOfSchema)) {
2626
throw new Error('You must pass a valid discriminator Schema');
2727
}
@@ -205,7 +205,7 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
205205

206206
model.schema.discriminators[name] = schema;
207207

208-
if (model.discriminators[name] && !schema.options.overwriteModels) {
208+
if (model.discriminators[name] && !schema.options.overwriteModels && !overwriteExisting) {
209209
throw new Error('Discriminator with name "' + name + '" already exists');
210210
}
211211

lib/model.js

+10
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const VersionError = require('./error/version');
2323
const ParallelSaveError = require('./error/parallelSave');
2424
const applyDefaultsHelper = require('./helpers/document/applyDefaults');
2525
const applyDefaultsToPOJO = require('./helpers/model/applyDefaultsToPOJO');
26+
const applyEmbeddedDiscriminators = require('./helpers/discriminator/applyEmbeddedDiscriminators');
2627
const applyHooks = require('./helpers/model/applyHooks');
2728
const applyMethods = require('./helpers/model/applyMethods');
2829
const applyProjection = require('./helpers/projection/applyProjection');
@@ -5001,6 +5002,15 @@ Model.__subclass = function subclass(conn, schema, collection) {
50015002

50025003
Model.recompileSchema = function recompileSchema() {
50035004
this.prototype.$__setSchema(this.schema);
5005+
5006+
if (this.schema._applyDiscriminators != null) {
5007+
for (const disc of this.schema._applyDiscriminators.keys()) {
5008+
this.discriminator(disc, this.schema._applyDiscriminators.get(disc));
5009+
}
5010+
}
5011+
5012+
const overwriteExisting = true;
5013+
applyEmbeddedDiscriminators(this.schema, new WeakSet(), overwriteExisting);
50045014
};
50055015

50065016
/**

lib/schema/subdocument.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ SchemaSubdocument.prototype.discriminator = function(name, schema, options) {
325325
schema = schema.clone();
326326
}
327327

328-
schema = discriminator(this.caster, name, schema, value);
328+
schema = discriminator(this.caster, name, schema, value, null, null, options.overwriteExisting);
329329

330330
this.caster.discriminators[name] = _createConstructor(schema, this.caster);
331331

test/model.test.js

+41
Original file line numberDiff line numberDiff line change
@@ -7419,6 +7419,47 @@ describe('Model', function() {
74197419
assert.equal(doc.myVirtual, 'Hello from myVirtual');
74207420
});
74217421

7422+
it('supports recompiling model with new discriminators (gh-14444) (gh-14296)', function() {
7423+
// Define discriminated schema
7424+
const decoratorSchema = new Schema({
7425+
type: { type: String, required: true }
7426+
}, { discriminatorKey: 'type' });
7427+
7428+
class Decorator {
7429+
whoAmI() { return 'I am BaseDeco'; }
7430+
}
7431+
decoratorSchema.loadClass(Decorator);
7432+
7433+
// Define discriminated class before model is compiled
7434+
class Deco1 extends Decorator { whoAmI() { return 'I am Test1'; }}
7435+
const deco1Schema = new Schema({}).loadClass(Deco1);
7436+
deco1Schema.loadClass(Deco1);
7437+
decoratorSchema.discriminator('Test1', deco1Schema);
7438+
7439+
// Define model that uses discriminated schema
7440+
const shopSchema = new Schema({
7441+
item: { type: decoratorSchema, required: true }
7442+
});
7443+
7444+
class Shop {}
7445+
shopSchema.loadClass(Shop);
7446+
const shopModel = db.model('Test', shopSchema);
7447+
7448+
// Define another discriminated class after the model is compiled
7449+
class Deco2 extends Decorator { whoAmI() { return 'I am Test2'; }}
7450+
const deco2Schema = new Schema({}).loadClass(Deco2);
7451+
deco2Schema.loadClass(Deco2);
7452+
decoratorSchema.discriminator('Test2', deco2Schema);
7453+
7454+
shopModel.recompileSchema();
7455+
7456+
let instance = new shopModel({ item: { type: 'Test1' } });
7457+
assert.equal(instance.item.whoAmI(), 'I am Test1');
7458+
7459+
instance = new shopModel({ item: { type: 'Test2' } });
7460+
assert.equal(instance.item.whoAmI(), 'I am Test2');
7461+
});
7462+
74227463
it('inserts versionKey even if schema has `toObject.versionKey` set to false (gh-14344)', async function() {
74237464
const schema = new mongoose.Schema(
74247465
{ name: String },

0 commit comments

Comments
 (0)