@@ -4245,67 +4245,34 @@ Model.populate = async function populate(docs, paths) {
42454245 if ( typeof paths === 'function' || typeof arguments [ 2 ] === 'function' ) {
42464246 throw new MongooseError ( 'Model.populate() no longer accepts a callback' ) ;
42474247 }
4248- const _this = this ;
42494248 // normalized paths
42504249 paths = utils . populate ( paths ) ;
4251- // data that should persist across subPopulate calls
4252- const cache = { } ;
4253-
4254- return new Promise ( ( resolve , reject ) => {
4255- _populate ( _this , docs , paths , cache , ( err , res ) => {
4256- if ( err ) {
4257- return reject ( err ) ;
4258- }
4259- resolve ( res ) ;
4260- } ) ;
4261- } ) ;
4262- } ;
4263-
4264- /**
4265- * Populate helper
4266- *
4267- * @param {Model } model the model to use
4268- * @param {Document|Array } docs Either a single document or array of documents to populate.
4269- * @param {Object } paths
4270- * @param {never } cache Unused
4271- * @param {Function } [callback] Optional callback, executed upon completion. Receives `err` and the `doc(s)`.
4272- * @return {Function }
4273- * @api private
4274- */
42754250
4276- function _populate ( model , docs , paths , cache , callback ) {
4277- let pending = paths . length ;
42784251 if ( paths . length === 0 ) {
4279- return callback ( null , docs ) ;
4252+ return docs ;
42804253 }
4254+
42814255 // each path has its own query options and must be executed separately
4256+ const promises = [ ] ;
42824257 for ( const path of paths ) {
4283- populate ( model , docs , path , next ) ;
4258+ promises . push ( _populatePath ( this , docs , path ) ) ;
42844259 }
4260+ await Promise . all ( promises ) ;
42854261
4286- function next ( err ) {
4287- if ( err ) {
4288- return callback ( err , null ) ;
4289- }
4290- if ( -- pending ) {
4291- return ;
4292- }
4293- callback ( null , docs ) ;
4294- }
4295- }
4262+ return docs ;
4263+ } ;
42964264
42974265/*!
4298- * Populates `docs`
4266+ * Populates `docs` for a single `populateOptions` instance.
42994267 */
43004268const excludeIdReg = / \s ? - _ i d \s ? / ;
43014269const excludeIdRegGlobal = / \s ? - _ i d \s ? / g;
43024270
4303- function populate ( model , docs , options , callback ) {
4304- const populateOptions = options ;
4305- if ( options . strictPopulate == null ) {
4306- if ( options . _localModel != null && options . _localModel . schema . _userProvidedOptions . strictPopulate != null ) {
4307- populateOptions . strictPopulate = options . _localModel . schema . _userProvidedOptions . strictPopulate ;
4308- } else if ( options . _localModel != null && model . base . options . strictPopulate != null ) {
4271+ async function _populatePath ( model , docs , populateOptions ) {
4272+ if ( populateOptions . strictPopulate == null ) {
4273+ if ( populateOptions . _localModel != null && populateOptions . _localModel . schema . _userProvidedOptions . strictPopulate != null ) {
4274+ populateOptions . strictPopulate = populateOptions . _localModel . schema . _userProvidedOptions . strictPopulate ;
4275+ } else if ( populateOptions . _localModel != null && model . base . options . strictPopulate != null ) {
43094276 populateOptions . strictPopulate = model . base . options . strictPopulate ;
43104277 } else if ( model . base . options . strictPopulate != null ) {
43114278 populateOptions . strictPopulate = model . base . options . strictPopulate ;
@@ -4317,15 +4284,12 @@ function populate(model, docs, options, callback) {
43174284 docs = [ docs ] ;
43184285 }
43194286 if ( docs . length === 0 || docs . every ( utils . isNullOrUndefined ) ) {
4320- return callback ( ) ;
4287+ return ;
43214288 }
43224289
43234290 const modelsMap = getModelsMapForPopulate ( model , docs , populateOptions ) ;
4324-
43254291 if ( modelsMap instanceof MongooseError ) {
4326- return immediate ( function ( ) {
4327- callback ( modelsMap ) ;
4328- } ) ;
4292+ throw modelsMap ;
43294293 }
43304294 const len = modelsMap . length ;
43314295 let vals = [ ] ;
@@ -4335,7 +4299,6 @@ function populate(model, docs, options, callback) {
43354299 return undefined !== item ;
43364300 }
43374301
4338- let _remaining = len ;
43394302 let hasOne = false ;
43404303 const params = [ ] ;
43414304 for ( let i = 0 ; i < len ; ++ i ) {
@@ -4366,7 +4329,6 @@ function populate(model, docs, options, callback) {
43664329 // Ensure that we set to 0 or empty array even
43674330 // if we don't actually execute a query to make sure there's a value
43684331 // and we know this path was populated for future sets. See gh-7731, gh-8230
4369- -- _remaining ;
43704332 _assign ( model , [ ] , mod , assignmentOpts ) ;
43714333 continue ;
43724334 }
@@ -4397,72 +4359,58 @@ function populate(model, docs, options, callback) {
43974359 } else if ( mod . options . limit != null ) {
43984360 assignmentOpts . originalLimit = mod . options . limit ;
43994361 }
4400- params . push ( [ mod , match , select , assignmentOpts , _next ] ) ;
4362+ params . push ( [ mod , match , select , assignmentOpts ] ) ;
44014363 }
44024364 if ( ! hasOne ) {
44034365 // If models but no docs, skip further deep populate.
44044366 if ( modelsMap . length !== 0 ) {
4405- return callback ( ) ;
4367+ return ;
44064368 }
4407- // If no models to populate but we have a nested populate,
4408- // keep trying, re: gh-8946
4369+ // If no models and no docs to populate but we have a nested populate,
4370+ // probably a case of unnecessarily populating a non-ref path re: gh-8946
44094371 if ( populateOptions . populate != null ) {
44104372 const opts = utils . populate ( populateOptions . populate ) . map ( pop => Object . assign ( { } , pop , {
44114373 path : populateOptions . path + '.' + pop . path
44124374 } ) ) ;
4413- model . populate ( docs , opts ) . then ( res => { callback ( null , res ) ; } , err => { callback ( err ) ; } ) ;
4414- return ;
4375+ return model . populate ( docs , opts ) ;
44154376 }
4416- return callback ( ) ;
4377+ return ;
44174378 }
44184379
4380+ const promises = [ ] ;
44194381 for ( const arr of params ) {
4420- _execPopulateQuery . apply ( null , arr ) ;
4421- }
4422- function _next ( err , valsFromDb ) {
4423- if ( err != null ) {
4424- return callback ( err , null ) ;
4425- }
4426- vals = vals . concat ( valsFromDb ) ;
4427- if ( -- _remaining === 0 ) {
4428- _done ( ) ;
4429- }
4382+ promises . push ( _execPopulateQuery . apply ( null , arr ) . then ( valsFromDb => { vals = vals . concat ( valsFromDb ) ; } ) ) ;
44304383 }
44314384
4432- function _done ( ) {
4433- for ( const arr of params ) {
4434- const mod = arr [ 0 ] ;
4435- const assignmentOpts = arr [ 3 ] ;
4436- for ( const val of vals ) {
4437- mod . options . _childDocs . push ( val ) ;
4438- }
4439- try {
4440- _assign ( model , vals , mod , assignmentOpts ) ;
4441- } catch ( err ) {
4442- return callback ( err ) ;
4443- }
4444- }
4385+ await Promise . all ( promises ) ;
44454386
4446- for ( const arr of params ) {
4447- removeDeselectedForeignField ( arr [ 0 ] . foreignField , arr [ 0 ] . options , vals ) ;
4387+ for ( const arr of params ) {
4388+ const mod = arr [ 0 ] ;
4389+ const assignmentOpts = arr [ 3 ] ;
4390+ for ( const val of vals ) {
4391+ mod . options . _childDocs . push ( val ) ;
44484392 }
4449- for ( const arr of params ) {
4450- const mod = arr [ 0 ] ;
4451- if ( mod . options && mod . options . options && mod . options . options . _leanTransform ) {
4452- for ( const doc of vals ) {
4453- mod . options . options . _leanTransform ( doc ) ;
4454- }
4393+ _assign ( model , vals , mod , assignmentOpts ) ;
4394+ }
4395+
4396+ for ( const arr of params ) {
4397+ removeDeselectedForeignField ( arr [ 0 ] . foreignField , arr [ 0 ] . options , vals ) ;
4398+ }
4399+ for ( const arr of params ) {
4400+ const mod = arr [ 0 ] ;
4401+ if ( mod . options && mod . options . options && mod . options . options . _leanTransform ) {
4402+ for ( const doc of vals ) {
4403+ mod . options . options . _leanTransform ( doc ) ;
44554404 }
44564405 }
4457- callback ( ) ;
44584406 }
44594407}
44604408
44614409/*!
44624410 * ignore
44634411 */
44644412
4465- function _execPopulateQuery ( mod , match , select , assignmentOpts , callback ) {
4413+ function _execPopulateQuery ( mod , match , select ) {
44664414 let subPopulate = clone ( mod . options . populate ) ;
44674415 const queryOptions = Object . assign ( {
44684416 skip : mod . options . skip ,
@@ -4528,15 +4476,12 @@ function _execPopulateQuery(mod, match, select, assignmentOpts, callback) {
45284476 query . populate ( subPopulate ) ;
45294477 }
45304478
4531- query . exec ( ) . then (
4479+ return query . exec ( ) . then (
45324480 docs => {
45334481 for ( const val of docs ) {
45344482 leanPopulateMap . set ( val , mod . model ) ;
45354483 }
4536- callback ( null , docs ) ;
4537- } ,
4538- err => {
4539- callback ( err ) ;
4484+ return docs ;
45404485 }
45414486 ) ;
45424487}
0 commit comments