From a81211de24c045a29b1df9ffba288608ee57f32b Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 21 Oct 2019 12:20:43 -0400 Subject: [PATCH] fix(populate): add document array subpaths to parent doc `populated()` when calling `DocumentArray#push()` Fix #8247 --- lib/document.js | 7 +------ lib/types/core_array.js | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/document.js b/lib/document.js index e108d55679c..23c30db1ce0 100644 --- a/lib/document.js +++ b/lib/document.js @@ -1130,12 +1130,7 @@ Document.prototype.$set = function $set(path, val, type, options) { let didPopulate = false; if (refMatches && val instanceof Document) { - if (this.ownerDocument) { - this.ownerDocument().populated(this.$__fullPath(path), - val._id, { [populateModelSymbol]: val.constructor }); - } else { - this.populated(path, val._id, { [populateModelSymbol]: val.constructor }); - } + this.populated(path, val._id, { [populateModelSymbol]: val.constructor }); didPopulate = true; } diff --git a/lib/types/core_array.js b/lib/types/core_array.js index 9f0aa9f5f25..92121d9a523 100644 --- a/lib/types/core_array.js +++ b/lib/types/core_array.js @@ -628,11 +628,30 @@ class CoreMongooseArray extends Array { _checkManualPopulation(this, arguments); + const parent = this[arrayParentSymbol]; let values = [].map.call(arguments, this._mapCast, this); - values = this[arraySchemaSymbol].applySetters(values, this[arrayParentSymbol], undefined, + values = this[arraySchemaSymbol].applySetters(values, parent, undefined, undefined, { skipDocumentArrayCast: true }); const ret = [].push.apply(this, values); + // If this is a document array, each element may contain single + // populated paths, so we need to modify the top-level document's + // populated cache. See gh-8247. + if (this.isMongooseDocumentArray && parent.$__.populated != null) { + const populatedPaths = Object.keys(parent.$__.populated). + filter(p => p.startsWith(this[arrayPathSymbol] + '.')); + + for (const path of populatedPaths) { + const remnant = path.slice((this[arrayPathSymbol] + '.').length); + if (!Array.isArray(parent.$__.populated[path].value)) { + continue; + } + for (const val of values) { + parent.$__.populated[path].value.push(val.populated(remnant)); + } + } + } + this._registerAtomic('$push', values); this._markModified(); return ret;