applyVirtuals.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. 'use strict';
  2. const mpath = require('mpath');
  3. const utils = require('../../utils');
  4. module.exports = applyVirtuals;
  5. /**
  6. * Apply a given schema's virtuals to a given POJO
  7. *
  8. * @param {Schema} schema
  9. * @param {Object} obj
  10. * @param {Array<string>} [virtuals] optional whitelist of virtuals to apply
  11. * @returns
  12. */
  13. function applyVirtuals(schema, obj, virtuals) {
  14. if (obj == null) {
  15. return obj;
  16. }
  17. let virtualsForChildren = virtuals;
  18. let toApply = null;
  19. if (Array.isArray(virtuals)) {
  20. virtualsForChildren = [];
  21. toApply = [];
  22. for (const virtual of virtuals) {
  23. if (virtual.length === 1) {
  24. toApply.push(virtual[0]);
  25. } else {
  26. virtualsForChildren.push(virtual);
  27. }
  28. }
  29. }
  30. applyVirtualsToChildren(schema, obj, virtualsForChildren);
  31. return applyVirtualsToDoc(schema, obj, toApply);
  32. }
  33. /**
  34. * Apply virtuals to any subdocuments
  35. *
  36. * @param {Schema} schema subdocument schema
  37. * @param {Object} res subdocument
  38. * @param {Array<String>} [virtuals] optional whitelist of virtuals to apply
  39. */
  40. function applyVirtualsToChildren(schema, res, virtuals) {
  41. let attachedVirtuals = false;
  42. for (const childSchema of schema.childSchemas) {
  43. const _path = childSchema.model.path;
  44. const _schema = childSchema.schema;
  45. if (!_path) {
  46. continue;
  47. }
  48. const _obj = mpath.get(_path, res);
  49. if (_obj == null || (Array.isArray(_obj) && _obj.flat(Infinity).length === 0)) {
  50. continue;
  51. }
  52. let virtualsForChild = null;
  53. if (Array.isArray(virtuals)) {
  54. virtualsForChild = [];
  55. for (const virtual of virtuals) {
  56. if (virtual[0] == _path) {
  57. virtualsForChild.push(virtual.slice(1));
  58. }
  59. }
  60. if (virtualsForChild.length === 0) {
  61. continue;
  62. }
  63. }
  64. applyVirtuals(_schema, _obj, virtualsForChild);
  65. attachedVirtuals = true;
  66. }
  67. if (virtuals?.length && !attachedVirtuals) {
  68. applyVirtualsToDoc(schema, res, virtuals);
  69. }
  70. }
  71. /**
  72. * Apply virtuals to a given document. Does not apply virtuals to subdocuments: use `applyVirtualsToChildren` instead
  73. *
  74. * @param {Schema} schema
  75. * @param {Object} doc
  76. * @param {Array<String>} [virtuals] optional whitelist of virtuals to apply
  77. * @returns
  78. */
  79. function applyVirtualsToDoc(schema, obj, virtuals) {
  80. if (obj == null || typeof obj !== 'object') {
  81. return;
  82. }
  83. if (Array.isArray(obj)) {
  84. for (const el of obj) {
  85. applyVirtualsToDoc(schema, el, virtuals);
  86. }
  87. return;
  88. }
  89. if (schema.discriminators && utils.hasOwnKeys(schema.discriminators)) {
  90. for (const discriminatorKey of Object.keys(schema.discriminators)) {
  91. const discriminator = schema.discriminators[discriminatorKey];
  92. const key = discriminator.discriminatorMapping.key;
  93. const value = discriminator.discriminatorMapping.value;
  94. if (obj[key] == value) {
  95. schema = discriminator;
  96. break;
  97. }
  98. }
  99. }
  100. if (virtuals == null) {
  101. virtuals = Object.keys(schema.virtuals);
  102. }
  103. for (const virtual of virtuals) {
  104. if (schema.virtuals[virtual] == null) {
  105. continue;
  106. }
  107. const virtualType = schema.virtuals[virtual];
  108. const sp = Array.isArray(virtual)
  109. ? virtual
  110. : virtual.indexOf('.') === -1
  111. ? [virtual]
  112. : virtual.split('.');
  113. let cur = obj;
  114. for (let i = 0; i < sp.length - 1; ++i) {
  115. cur[sp[i]] = sp[i] in cur ? cur[sp[i]] : {};
  116. cur = cur[sp[i]];
  117. }
  118. let val = virtualType.applyGetters(cur[sp[sp.length - 1]], obj);
  119. const isPopulateVirtual =
  120. virtualType.options?.ref || virtualType.options?.refPath;
  121. if (isPopulateVirtual && val === undefined) {
  122. if (virtualType.options.justOne) {
  123. val = null;
  124. } else {
  125. val = [];
  126. }
  127. }
  128. cur[sp[sp.length - 1]] = val;
  129. }
  130. }