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..a7b5f2f2fe5 100644 --- a/lib/helpers/clone.js +++ b/lib/helpers/clone.js @@ -36,10 +36,23 @@ function clone(obj, options, isArrayChild) { } if (isMongooseObject(obj)) { - // Single nested subdocs should apply getters later in `applyGetters()` - // when calling `toObject()`. See gh-7442, gh-8295 - if (options && options._skipSingleNestedGetters && obj.$isSingleNested) { - options = Object.assign({}, options, { getters: false }); + if (options) { + // Single nested subdocs should apply getters later in `applyGetters()` + // when calling `toObject()`. See gh-7442, gh-8295 + if (options._skipSingleNestedGetters && obj.$isSingleNested) { + options = Object.assign({}, options, { getters: false }); + } + if (options.retainDocuments && 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; 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 }