From 4ee3d277013642af9d4e4026eff709ff6f961d17 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 29 Feb 2024 15:01:22 -0500 Subject: [PATCH] fix(document): make `$clone` avoid converting subdocs into POJOs Re: #14353 --- lib/document.js | 2 +- lib/helpers/clone.js | 11 +++++++++++ test/document.test.js | 21 +++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/document.js b/lib/document.js index 4127102f0c9..78b6f58eff2 100644 --- a/lib/document.js +++ b/lib/document.js @@ -4735,7 +4735,7 @@ Document.prototype.$clone = function() { const clonedDoc = new Model(); clonedDoc.$isNew = this.$isNew; if (this._doc) { - clonedDoc._doc = clone(this._doc); + clonedDoc._doc = clone(this._doc, { retainDocuments: true }); } if (this.$__) { const Cache = this.$__.constructor; diff --git a/lib/helpers/clone.js b/lib/helpers/clone.js index b83858acdf7..b4469110717 100644 --- a/lib/helpers/clone.js +++ b/lib/helpers/clone.js @@ -41,6 +41,17 @@ function clone(obj, options, isArrayChild) { if (options && options._skipSingleNestedGetters && obj.$isSingleNested) { options = Object.assign({}, options, { getters: false }); } + if (options != null && options.retainDocuments != null && obj.$__ != null) { + const clonedDoc = obj.$clone(); + if (obj.__index != null) { + clonedDoc.__index = obj.__index; + } + if (obj.__parentArray != null) { + clonedDoc.__parentArray = obj.__parentArray; + } + clonedDoc.$__parent = obj.$__parent; + return clonedDoc; + } const isSingleNested = obj.$isSingleNested; if (isPOJO(obj) && obj.$__ != null && obj._doc != null) { diff --git a/test/document.test.js b/test/document.test.js index 9999d18564b..f6f1d0797b3 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -12070,6 +12070,27 @@ describe('document', function() { assert.strictEqual(clonedDoc.$session(), session); }); + it('$clone() with single nested and doc array (gh-14353) (gh-11849)', async function() { + const schema = new mongoose.Schema({ + subdocArray: [{ + name: String + }], + subdoc: new mongoose.Schema({ name: String }) + }); + const Test = db.model('Test', schema); + + const item = await Test.create({ subdocArray: [{ name: 'test 1' }], subdoc: { name: 'test 2' } }); + + const doc = await Test.findById(item._id); + const clonedDoc = doc.$clone(); + + assert.ok(clonedDoc.subdocArray[0].$__); + assert.ok(clonedDoc.subdoc.$__); + + assert.deepEqual(doc.subdocArray[0], clonedDoc.subdocArray[0]); + assert.deepEqual(doc.subdoc, clonedDoc.subdoc); + }); + it('can create document with document array and top-level key named `schema` (gh-12480)', async function() { const AuthorSchema = new Schema({ fullName: { type: 'String', required: true }