createPopulateQueryFilter.js 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. 'use strict';
  2. const SkipPopulateValue = require('./SkipPopulateValue');
  3. const parentPaths = require('../path/parentPaths');
  4. const { trusted } = require('../query/trusted');
  5. module.exports = function createPopulateQueryFilter(ids, _match, _foreignField, model, skipInvalidIds) {
  6. const match = _formatMatch(_match);
  7. if (_foreignField.size === 1) {
  8. const foreignField = Array.from(_foreignField)[0];
  9. const foreignSchemaType = model.schema.path(foreignField);
  10. if (foreignField !== '_id' || !match['_id']) {
  11. ids = _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds);
  12. match[foreignField] = trusted({ $in: ids });
  13. }
  14. const _parentPaths = parentPaths(foreignField);
  15. for (let i = 0; i < _parentPaths.length - 1; ++i) {
  16. const cur = _parentPaths[i];
  17. if (match[cur] != null && match[cur].$elemMatch != null) {
  18. match[cur].$elemMatch[foreignField.slice(cur.length + 1)] = trusted({ $in: ids });
  19. delete match[foreignField];
  20. break;
  21. }
  22. }
  23. } else {
  24. const $or = [];
  25. if (Array.isArray(match.$or)) {
  26. match.$and = [{ $or: match.$or }, { $or: $or }];
  27. delete match.$or;
  28. } else {
  29. match.$or = $or;
  30. }
  31. for (const foreignField of _foreignField) {
  32. if (foreignField !== '_id' || !match['_id']) {
  33. const foreignSchemaType = model.schema.path(foreignField);
  34. ids = _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds);
  35. $or.push({ [foreignField]: { $in: ids } });
  36. }
  37. }
  38. }
  39. return match;
  40. };
  41. /*!
  42. * Optionally filter out invalid ids that don't conform to foreign field's schema
  43. * to avoid cast errors (gh-7706)
  44. */
  45. function _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds) {
  46. ids = ids.filter(v => !(v instanceof SkipPopulateValue));
  47. if (!skipInvalidIds) {
  48. return ids;
  49. }
  50. return ids.filter(id => {
  51. try {
  52. foreignSchemaType.cast(id);
  53. return true;
  54. } catch (err) {
  55. return false;
  56. }
  57. });
  58. }
  59. /*!
  60. * Format `mod.match` given that it may be an array that we need to $or if
  61. * the client has multiple docs with match functions
  62. */
  63. function _formatMatch(match) {
  64. if (Array.isArray(match)) {
  65. if (match.length > 1) {
  66. return { $or: [].concat(match.map(m => Object.assign({}, m))) };
  67. }
  68. return Object.assign({}, match[0]);
  69. }
  70. return Object.assign({}, match);
  71. }