| 4745 | */ |
| 4746 | |
| 4747 | function _execPopulateQuery(mod, match, select) { |
| 4748 | // `null` match means `mod`'s documents have no ids to query, possible if a large populate |
| 4749 | // was split into a query per document (gh-5890). Skip executing a query: `_assign()` still |
| 4750 | // sets the documents' populated paths to the appropriate default value. |
| 4751 | if (match == null) { |
| 4752 | return Promise.resolve({ docs: [], deferredPopulates: [] }); |
| 4753 | } |
| 4754 | let subPopulate = clone(mod.options.populate); |
| 4755 | const queryOptions = {}; |
| 4756 | if (mod.options.skip !== undefined) { |
| 4757 | queryOptions.skip = mod.options.skip; |
| 4758 | } |
| 4759 | if (mod.options.limit !== undefined) { |
| 4760 | queryOptions.limit = mod.options.limit; |
| 4761 | } |
| 4762 | if (mod.options.perDocumentLimit !== undefined) { |
| 4763 | queryOptions.perDocumentLimit = mod.options.perDocumentLimit; |
| 4764 | } |
| 4765 | Object.assign(queryOptions, mod.options.options); |
| 4766 | |
| 4767 | if (mod.count) { |
| 4768 | delete queryOptions.skip; |
| 4769 | } |
| 4770 | |
| 4771 | if (queryOptions.perDocumentLimit != null) { |
| 4772 | queryOptions.limit = queryOptions.perDocumentLimit; |
| 4773 | delete queryOptions.perDocumentLimit; |
| 4774 | } else if (queryOptions.limit != null) { |
| 4775 | queryOptions.limit = queryOptions.limit * mod.ids.length; |
| 4776 | } |
| 4777 | |
| 4778 | // When there's a per-document match function, defer any populate (including from hooks) |
| 4779 | // until after sift filtering, otherwise sift compares ObjectIds against populated docs. |
| 4780 | if (Array.isArray(mod.match)) { |
| 4781 | queryOptions._deferPopulate = true; |
| 4782 | } |
| 4783 | |
| 4784 | const query = mod.model.find(match, select, queryOptions); |
| 4785 | // If we're doing virtual populate and projection is inclusive and foreign |
| 4786 | // field is not selected, automatically select it because mongoose needs it. |
| 4787 | // If projection is exclusive and client explicitly unselected the foreign |
| 4788 | // field, that's the client's fault. |
| 4789 | for (const foreignField of mod.foreignField) { |
| 4790 | if (foreignField !== '_id' && |
| 4791 | query.selectedInclusively() && |
| 4792 | !isPathSelectedInclusive(query._fields, foreignField)) { |
| 4793 | query.select(foreignField); |
| 4794 | } |
| 4795 | } |
| 4796 | |
| 4797 | // If using count, still need the `foreignField` so we can match counts |
| 4798 | // to documents, otherwise we would need a separate `count()` for every doc. |
| 4799 | if (mod.count) { |
| 4800 | for (const foreignField of mod.foreignField) { |
| 4801 | query.select(foreignField); |
| 4802 | } |
| 4803 | } |
| 4804 | |