| 4712 | */ |
| 4713 | |
| 4714 | function _execPopulateQuery(mod, match, select) { |
| 4715 | // `null` match means `mod`'s documents have no ids to query, possible if a large populate |
| 4716 | // was split into a query per document (gh-5890). Skip executing a query: `_assign()` still |
| 4717 | // sets the documents' populated paths to the appropriate default value. |
| 4718 | if (match == null) { |
| 4719 | return Promise.resolve({ docs: [], deferredPopulates: [] }); |
| 4720 | } |
| 4721 | let subPopulate = clone(mod.options.populate); |
| 4722 | const queryOptions = {}; |
| 4723 | if (mod.options.skip !== undefined) { |
| 4724 | queryOptions.skip = mod.options.skip; |
| 4725 | } |
| 4726 | if (mod.options.limit !== undefined) { |
| 4727 | queryOptions.limit = mod.options.limit; |
| 4728 | } |
| 4729 | if (mod.options.perDocumentLimit !== undefined) { |
| 4730 | queryOptions.perDocumentLimit = mod.options.perDocumentLimit; |
| 4731 | } |
| 4732 | Object.assign(queryOptions, mod.options.options); |
| 4733 | |
| 4734 | if (mod.count) { |
| 4735 | delete queryOptions.skip; |
| 4736 | } |
| 4737 | |
| 4738 | if (queryOptions.perDocumentLimit != null) { |
| 4739 | queryOptions.limit = queryOptions.perDocumentLimit; |
| 4740 | delete queryOptions.perDocumentLimit; |
| 4741 | } else if (queryOptions.limit != null) { |
| 4742 | queryOptions.limit = queryOptions.limit * mod.ids.length; |
| 4743 | } |
| 4744 | |
| 4745 | // When there's a per-document match function, defer any populate (including from hooks) |
| 4746 | // until after sift filtering, otherwise sift compares ObjectIds against populated docs. |
| 4747 | if (Array.isArray(mod.match)) { |
| 4748 | queryOptions._deferPopulate = true; |
| 4749 | } |
| 4750 | |
| 4751 | const query = mod.model.find(match, select, queryOptions); |
| 4752 | // If we're doing virtual populate and projection is inclusive and foreign |
| 4753 | // field is not selected, automatically select it because mongoose needs it. |
| 4754 | // If projection is exclusive and client explicitly unselected the foreign |
| 4755 | // field, that's the client's fault. |
| 4756 | for (const foreignField of mod.foreignField) { |
| 4757 | if (foreignField !== '_id' && |
| 4758 | query.selectedInclusively() && |
| 4759 | !isPathSelectedInclusive(query._fields, foreignField)) { |
| 4760 | query.select(foreignField); |
| 4761 | } |
| 4762 | } |
| 4763 | |
| 4764 | // If using count, still need the `foreignField` so we can match counts |
| 4765 | // to documents, otherwise we would need a separate `count()` for every doc. |
| 4766 | if (mod.count) { |
| 4767 | for (const foreignField of mod.foreignField) { |
| 4768 | query.select(foreignField); |
| 4769 | } |
| 4770 | } |
| 4771 | |