From 14032ffd50f395c7586b1934f78ee121f4740f3c Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 13 Feb 2025 09:04:33 -0500 Subject: [PATCH] perf(schema): clear childSchemas when overwriting existing path to avoid performance degradations Fix #15253 --- lib/schema.js | 3 +++ test/schema.test.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/lib/schema.js b/lib/schema.js index 0204c6cc9c4..f7528f6b4b4 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -1118,6 +1118,9 @@ Schema.prototype.path = function(path, obj) { this.paths[path] = this.interpretAsType(path, obj, this.options); const schemaType = this.paths[path]; + // If overwriting an existing path, make sure to clear the childSchemas + this.childSchemas = this.childSchemas.filter(childSchema => childSchema.path !== path); + if (schemaType.$isSchemaMap) { // Maps can have arbitrary keys, so `$*` is internal shorthand for "any key" // The '$' is to imply this path should never be stored in MongoDB so we diff --git a/test/schema.test.js b/test/schema.test.js index 416502b03a5..52a1b265781 100644 --- a/test/schema.test.js +++ b/test/schema.test.js @@ -3889,4 +3889,34 @@ describe('schema', function() { assert.throws(() => schema.toJSONSchema(), /unsupported SchemaType to JSON Schema: Mixed/); }); }); + + it('path() clears existing child schemas (gh-15253)', async function() { + const RecursiveSchema = new mongoose.Schema({ + data: String + }); + + const s = [RecursiveSchema]; + RecursiveSchema.path('nested', s); + assert.strictEqual(RecursiveSchema.childSchemas.length, 1); + RecursiveSchema.path('nested', s); + assert.strictEqual(RecursiveSchema.childSchemas.length, 1); + RecursiveSchema.path('nested', s); + assert.strictEqual(RecursiveSchema.childSchemas.length, 1); + RecursiveSchema.path('nested', s); + assert.strictEqual(RecursiveSchema.childSchemas.length, 1); + + const generateRecursiveDocument = (depth, curr = 0) => { + return { + name: `Document of depth ${curr}`, + nested: depth > 0 ? new Array(3).fill().map(() => generateRecursiveDocument(depth - 1, curr + 1)) : [], + data: Math.random() + }; + }; + + const TestModel = db.model('Test', RecursiveSchema); + const data = generateRecursiveDocument(6); + const doc = new TestModel(data); + await doc.save(); + + }); });