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