Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

assignVals.js 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. 'use strict';
  2. const assignRawDocsToIdStructure = require('./assignRawDocsToIdStructure');
  3. const get = require('../get');
  4. const getVirtual = require('./getVirtual');
  5. const leanPopulateMap = require('./leanPopulateMap');
  6. const mpath = require('mpath');
  7. const sift = require('sift').default;
  8. const utils = require('../../utils');
  9. module.exports = function assignVals(o) {
  10. // Options that aren't explicitly listed in `populateOptions`
  11. const userOptions = get(o, 'allOptions.options.options');
  12. // `o.options` contains options explicitly listed in `populateOptions`, like
  13. // `match` and `limit`.
  14. const populateOptions = Object.assign({}, o.options, userOptions, {
  15. justOne: o.justOne
  16. });
  17. const originalIds = [].concat(o.rawIds);
  18. // replace the original ids in our intermediate _ids structure
  19. // with the documents found by query
  20. assignRawDocsToIdStructure(o.rawIds, o.rawDocs, o.rawOrder, populateOptions);
  21. // now update the original documents being populated using the
  22. // result structure that contains real documents.
  23. const docs = o.docs;
  24. const rawIds = o.rawIds;
  25. const options = o.options;
  26. const count = o.count && o.isVirtual;
  27. function setValue(val) {
  28. if (count) {
  29. return val;
  30. }
  31. if (o.justOne === true && Array.isArray(val)) {
  32. return valueFilter(val[0], options, populateOptions);
  33. } else if (o.justOne === false && !Array.isArray(val)) {
  34. return valueFilter([val], options, populateOptions);
  35. }
  36. return valueFilter(val, options, populateOptions);
  37. }
  38. for (let i = 0; i < docs.length; ++i) {
  39. const existingVal = utils.getValue(o.path, docs[i]);
  40. if (existingVal == null && !getVirtual(o.originalModel.schema, o.path)) {
  41. continue;
  42. }
  43. let valueToSet;
  44. if (count) {
  45. valueToSet = numDocs(rawIds[i]);
  46. } else if (Array.isArray(o.match)) {
  47. valueToSet = Array.isArray(rawIds[i]) ?
  48. sift(o.match[i], rawIds[i]) :
  49. sift(o.match[i], [rawIds[i]])[0];
  50. } else {
  51. valueToSet = rawIds[i];
  52. }
  53. // If we're populating a map, the existing value will be an object, so
  54. // we need to transform again
  55. const originalSchema = o.originalModel.schema;
  56. const isDoc = get(docs[i], '$__', null) != null;
  57. let isMap = isDoc ?
  58. existingVal instanceof Map :
  59. utils.isPOJO(existingVal);
  60. // If we pass the first check, also make sure the local field's schematype
  61. // is map (re: gh-6460)
  62. isMap = isMap && get(originalSchema._getSchema(o.path), '$isSchemaMap');
  63. if (!o.isVirtual && isMap) {
  64. const _keys = existingVal instanceof Map ?
  65. Array.from(existingVal.keys()) :
  66. Object.keys(existingVal);
  67. valueToSet = valueToSet.reduce((cur, v, i) => {
  68. cur.set(_keys[i], v);
  69. return cur;
  70. }, new Map());
  71. }
  72. if (o.isVirtual && isDoc) {
  73. docs[i].populated(o.path, o.justOne ? originalIds[0] : originalIds, o.allOptions);
  74. // If virtual populate and doc is already init-ed, need to walk through
  75. // the actual doc to set rather than setting `_doc` directly
  76. mpath.set(o.path, valueToSet, docs[i], setValue);
  77. continue;
  78. }
  79. const parts = o.path.split('.');
  80. let cur = docs[i];
  81. for (let j = 0; j < parts.length - 1; ++j) {
  82. // If we get to an array with a dotted path, like `arr.foo`, don't set
  83. // `foo` on the array.
  84. if (Array.isArray(cur) && !utils.isArrayIndex(parts[j])) {
  85. break;
  86. }
  87. if (cur[parts[j]] == null) {
  88. cur[parts[j]] = {};
  89. }
  90. cur = cur[parts[j]];
  91. // If the property in MongoDB is a primitive, we won't be able to populate
  92. // the nested path, so skip it. See gh-7545
  93. if (typeof cur !== 'object') {
  94. return;
  95. }
  96. }
  97. if (docs[i].$__) {
  98. docs[i].populated(o.path, o.allIds[i], o.allOptions);
  99. }
  100. // If lean, need to check that each individual virtual respects
  101. // `justOne`, because you may have a populated virtual with `justOne`
  102. // underneath an array. See gh-6867
  103. utils.setValue(o.path, valueToSet, docs[i], setValue, false);
  104. }
  105. };
  106. function numDocs(v) {
  107. if (Array.isArray(v)) {
  108. return v.length;
  109. }
  110. return v == null ? 0 : 1;
  111. }
  112. /*!
  113. * 1) Apply backwards compatible find/findOne behavior to sub documents
  114. *
  115. * find logic:
  116. * a) filter out non-documents
  117. * b) remove _id from sub docs when user specified
  118. *
  119. * findOne
  120. * a) if no doc found, set to null
  121. * b) remove _id from sub docs when user specified
  122. *
  123. * 2) Remove _ids when specified by users query.
  124. *
  125. * background:
  126. * _ids are left in the query even when user excludes them so
  127. * that population mapping can occur.
  128. */
  129. function valueFilter(val, assignmentOpts, populateOptions) {
  130. if (Array.isArray(val)) {
  131. // find logic
  132. const ret = [];
  133. const numValues = val.length;
  134. for (let i = 0; i < numValues; ++i) {
  135. const subdoc = val[i];
  136. if (!isPopulatedObject(subdoc) && (!populateOptions.retainNullValues || subdoc != null)) {
  137. continue;
  138. }
  139. maybeRemoveId(subdoc, assignmentOpts);
  140. ret.push(subdoc);
  141. if (assignmentOpts.originalLimit &&
  142. ret.length >= assignmentOpts.originalLimit) {
  143. break;
  144. }
  145. }
  146. // Since we don't want to have to create a new mongoosearray, make sure to
  147. // modify the array in place
  148. while (val.length > ret.length) {
  149. Array.prototype.pop.apply(val, []);
  150. }
  151. for (let i = 0; i < ret.length; ++i) {
  152. val[i] = ret[i];
  153. }
  154. return val;
  155. }
  156. // findOne
  157. if (isPopulatedObject(val)) {
  158. maybeRemoveId(val, assignmentOpts);
  159. return val;
  160. }
  161. if (val instanceof Map) {
  162. return val;
  163. }
  164. if (populateOptions.justOne === true) {
  165. return (val == null ? val : null);
  166. }
  167. if (populateOptions.justOne === false) {
  168. return [];
  169. }
  170. return val;
  171. }
  172. /*!
  173. * Remove _id from `subdoc` if user specified "lean" query option
  174. */
  175. function maybeRemoveId(subdoc, assignmentOpts) {
  176. if (assignmentOpts.excludeId) {
  177. if (typeof subdoc.$__setValue === 'function') {
  178. delete subdoc._doc._id;
  179. } else {
  180. delete subdoc._id;
  181. }
  182. }
  183. }
  184. /*!
  185. * Determine if `obj` is something we can set a populated path to. Can be a
  186. * document, a lean document, or an array/map that contains docs.
  187. */
  188. function isPopulatedObject(obj) {
  189. if (obj == null) {
  190. return false;
  191. }
  192. return Array.isArray(obj) ||
  193. obj.$isMongooseMap ||
  194. obj.$__ != null ||
  195. leanPopulateMap.has(obj);
  196. }