From 25b679851663af7bd70a5a3b4cd4f08f7085380f Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 20 Oct 2019 10:55:35 -0400 Subject: [PATCH] fix(populate): make `ArraySubdocument#populated()` return a value when the path is populated Re: #8247 --- lib/document.js | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/lib/document.js b/lib/document.js index b816b8bde97..c725b2083ab 100644 --- a/lib/document.js +++ b/lib/document.js @@ -486,8 +486,7 @@ Document.prototype.$__init = function(doc, opts) { // handle docs with populated paths // If doc._id is not null or undefined - if (doc._id !== null && doc._id !== undefined && - opts.populated && opts.populated.length) { + if (doc._id != null && opts.populated && opts.populated.length) { const id = String(doc._id); for (let i = 0; i < opts.populated.length; ++i) { const item = opts.populated[i]; @@ -501,6 +500,8 @@ Document.prototype.$__init = function(doc, opts) { init(this, doc, this._doc, opts); + markArraySubdocsPopulated(this, opts.populated); + this.emit('init', this); this.constructor.emit('init', this); @@ -509,6 +510,44 @@ Document.prototype.$__init = function(doc, opts) { return this; }; +/*! + * If populating a path within a document array, make sure each + * subdoc within the array knows its subpaths are populated. + * + * ####Example: + * const doc = await Article.findOne().populate('comments.author'); + * doc.comments[0].populated('author'); // Should be set + */ + +function markArraySubdocsPopulated(doc, populated) { + if (doc._id == null || populated == null || populated.length === 0) { + return; + } + + const id = String(doc._id); + for (const item of populated) { + if (item.isVirtual) { + continue; + } + const path = item.path; + const pieces = path.split('.'); + for (let i = 0; i < pieces.length - 1; ++i) { + const subpath = pieces.slice(0, i + 1).join('.'); + const rest = pieces.slice(i + 1).join('.'); + const val = doc.get(subpath); + if (val == null) { + continue; + } + if (val.isMongooseDocumentArray) { + for (let j = 0; j < val.length; ++j) { + val[j].populated(rest, item._docs[id][j], item); + } + break; + } + } + } +} + /*! * Init helper. *