| 4529 | */ |
| 4530 | |
| 4531 | function _execPopulateQuery(mod, match, select, assignmentOpts, callback) { |
| 4532 | let subPopulate = clone(mod.options.populate); |
| 4533 | const queryOptions = Object.assign({ |
| 4534 | skip: mod.options.skip, |
| 4535 | limit: mod.options.limit, |
| 4536 | perDocumentLimit: mod.options.perDocumentLimit |
| 4537 | }, mod.options.options); |
| 4538 | |
| 4539 | if (mod.count) { |
| 4540 | delete queryOptions.skip; |
| 4541 | } |
| 4542 | |
| 4543 | if (queryOptions.perDocumentLimit != null) { |
| 4544 | queryOptions.limit = queryOptions.perDocumentLimit; |
| 4545 | delete queryOptions.perDocumentLimit; |
| 4546 | } else if (queryOptions.limit != null) { |
| 4547 | queryOptions.limit = queryOptions.limit * mod.ids.length; |
| 4548 | } |
| 4549 | |
| 4550 | const query = mod.model.find(match, select, queryOptions); |
| 4551 | // If we're doing virtual populate and projection is inclusive and foreign |
| 4552 | // field is not selected, automatically select it because mongoose needs it. |
| 4553 | // If projection is exclusive and client explicitly unselected the foreign |
| 4554 | // field, that's the client's fault. |
| 4555 | for (const foreignField of mod.foreignField) { |
| 4556 | if (foreignField !== '_id' && |
| 4557 | query.selectedInclusively() && |
| 4558 | !isPathSelectedInclusive(query._fields, foreignField)) { |
| 4559 | query.select(foreignField); |
| 4560 | } |
| 4561 | } |
| 4562 | |
| 4563 | // If using count, still need the `foreignField` so we can match counts |
| 4564 | // to documents, otherwise we would need a separate `count()` for every doc. |
| 4565 | if (mod.count) { |
| 4566 | for (const foreignField of mod.foreignField) { |
| 4567 | query.select(foreignField); |
| 4568 | } |
| 4569 | } |
| 4570 | |
| 4571 | // If we need to sub-populate, call populate recursively |
| 4572 | if (subPopulate) { |
| 4573 | // If subpopulating on a discriminator, skip check for non-existent |
| 4574 | // paths. Because the discriminator may not have the path defined. |
| 4575 | if (mod.model.baseModelName != null) { |
| 4576 | if (Array.isArray(subPopulate)) { |
| 4577 | subPopulate.forEach(pop => { pop.strictPopulate = false; }); |
| 4578 | } else if (typeof subPopulate === 'string') { |
| 4579 | subPopulate = { path: subPopulate, strictPopulate: false }; |
| 4580 | } else { |
| 4581 | subPopulate.strictPopulate = false; |
| 4582 | } |
| 4583 | } |
| 4584 | const basePath = mod.options._fullPath || mod.options.path; |
| 4585 | |
| 4586 | if (Array.isArray(subPopulate)) { |
| 4587 | for (const pop of subPopulate) { |
| 4588 | pop._fullPath = basePath + '.' + pop.path; |