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.

embedded.js 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. /* eslint no-func-assign: 1 */
  2. /*!
  3. * Module dependencies.
  4. */
  5. 'use strict';
  6. const Document = require('../document_provider')();
  7. const EventEmitter = require('events').EventEmitter;
  8. const immediate = require('../helpers/immediate');
  9. const internalToObjectOptions = require('../options').internalToObjectOptions;
  10. const get = require('../helpers/get');
  11. const utils = require('../utils');
  12. const util = require('util');
  13. const documentArrayParent = require('../helpers/symbols').documentArrayParent;
  14. const validatorErrorSymbol = require('../helpers/symbols').validatorErrorSymbol;
  15. /**
  16. * EmbeddedDocument constructor.
  17. *
  18. * @param {Object} obj js object returned from the db
  19. * @param {MongooseDocumentArray} parentArr the parent array of this document
  20. * @param {Boolean} skipId
  21. * @inherits Document
  22. * @api private
  23. */
  24. function EmbeddedDocument(obj, parentArr, skipId, fields, index) {
  25. if (parentArr) {
  26. this.__parentArray = parentArr;
  27. this[documentArrayParent] = parentArr.$parent();
  28. } else {
  29. this.__parentArray = undefined;
  30. this[documentArrayParent] = undefined;
  31. }
  32. this.$setIndex(index);
  33. this.$isDocumentArrayElement = true;
  34. Document.call(this, obj, fields, skipId);
  35. const _this = this;
  36. this.on('isNew', function(val) {
  37. _this.isNew = val;
  38. });
  39. _this.on('save', function() {
  40. _this.constructor.emit('save', _this);
  41. });
  42. }
  43. /*!
  44. * Inherit from Document
  45. */
  46. EmbeddedDocument.prototype = Object.create(Document.prototype);
  47. EmbeddedDocument.prototype.constructor = EmbeddedDocument;
  48. for (const i in EventEmitter.prototype) {
  49. EmbeddedDocument[i] = EventEmitter.prototype[i];
  50. }
  51. EmbeddedDocument.prototype.toBSON = function() {
  52. return this.toObject(internalToObjectOptions);
  53. };
  54. /*!
  55. * ignore
  56. */
  57. EmbeddedDocument.prototype.$setIndex = function(index) {
  58. this.__index = index;
  59. if (get(this, '$__.validationError', null) != null) {
  60. const keys = Object.keys(this.$__.validationError.errors);
  61. for (const key of keys) {
  62. this.invalidate(key, this.$__.validationError.errors[key]);
  63. }
  64. }
  65. };
  66. /**
  67. * Marks the embedded doc modified.
  68. *
  69. * ####Example:
  70. *
  71. * var doc = blogpost.comments.id(hexstring);
  72. * doc.mixed.type = 'changed';
  73. * doc.markModified('mixed.type');
  74. *
  75. * @param {String} path the path which changed
  76. * @api public
  77. * @receiver EmbeddedDocument
  78. */
  79. EmbeddedDocument.prototype.markModified = function(path) {
  80. this.$__.activePaths.modify(path);
  81. if (!this.__parentArray) {
  82. return;
  83. }
  84. if (this.isNew) {
  85. // Mark the WHOLE parent array as modified
  86. // if this is a new document (i.e., we are initializing
  87. // a document),
  88. this.__parentArray._markModified();
  89. } else {
  90. this.__parentArray._markModified(this, path);
  91. }
  92. };
  93. /*!
  94. * ignore
  95. */
  96. EmbeddedDocument.prototype.populate = function() {
  97. throw new Error('Mongoose does not support calling populate() on nested ' +
  98. 'docs. Instead of `doc.arr[0].populate("path")`, use ' +
  99. '`doc.populate("arr.0.path")`');
  100. };
  101. /**
  102. * Used as a stub for [hooks.js](https://github.com/bnoguchi/hooks-js/tree/31ec571cef0332e21121ee7157e0cf9728572cc3)
  103. *
  104. * ####NOTE:
  105. *
  106. * _This is a no-op. Does not actually save the doc to the db._
  107. *
  108. * @param {Function} [fn]
  109. * @return {Promise} resolved Promise
  110. * @api private
  111. */
  112. EmbeddedDocument.prototype.save = function(options, fn) {
  113. if (typeof options === 'function') {
  114. fn = options;
  115. options = {};
  116. }
  117. options = options || {};
  118. if (!options.suppressWarning) {
  119. console.warn('mongoose: calling `save()` on a subdoc does **not** save ' +
  120. 'the document to MongoDB, it only runs save middleware. ' +
  121. 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' +
  122. 'if you\'re sure this behavior is right for your app.');
  123. }
  124. return utils.promiseOrCallback(fn, cb => {
  125. this.$__save(cb);
  126. });
  127. };
  128. /**
  129. * Used as a stub for middleware
  130. *
  131. * ####NOTE:
  132. *
  133. * _This is a no-op. Does not actually save the doc to the db._
  134. *
  135. * @param {Function} [fn]
  136. * @method $__save
  137. * @api private
  138. */
  139. EmbeddedDocument.prototype.$__save = function(fn) {
  140. return immediate(() => fn(null, this));
  141. };
  142. /*!
  143. * Registers remove event listeners for triggering
  144. * on subdocuments.
  145. *
  146. * @param {EmbeddedDocument} sub
  147. * @api private
  148. */
  149. function registerRemoveListener(sub) {
  150. let owner = sub.ownerDocument();
  151. function emitRemove() {
  152. owner.removeListener('save', emitRemove);
  153. owner.removeListener('remove', emitRemove);
  154. sub.emit('remove', sub);
  155. sub.constructor.emit('remove', sub);
  156. owner = sub = null;
  157. }
  158. owner.on('save', emitRemove);
  159. owner.on('remove', emitRemove);
  160. }
  161. /*!
  162. * no-op for hooks
  163. */
  164. EmbeddedDocument.prototype.$__remove = function(cb) {
  165. return cb(null, this);
  166. };
  167. /**
  168. * Removes the subdocument from its parent array.
  169. *
  170. * @param {Object} [options]
  171. * @param {Function} [fn]
  172. * @api public
  173. */
  174. EmbeddedDocument.prototype.remove = function(options, fn) {
  175. if ( typeof options === 'function' && !fn ) {
  176. fn = options;
  177. options = undefined;
  178. }
  179. if (!this.__parentArray || (options && options.noop)) {
  180. fn && fn(null);
  181. return this;
  182. }
  183. let _id;
  184. if (!this.willRemove) {
  185. _id = this._doc._id;
  186. if (!_id) {
  187. throw new Error('For your own good, Mongoose does not know ' +
  188. 'how to remove an EmbeddedDocument that has no _id');
  189. }
  190. this.__parentArray.pull({_id: _id});
  191. this.willRemove = true;
  192. registerRemoveListener(this);
  193. }
  194. if (fn) {
  195. fn(null);
  196. }
  197. return this;
  198. };
  199. /**
  200. * Override #update method of parent documents.
  201. * @api private
  202. */
  203. EmbeddedDocument.prototype.update = function() {
  204. throw new Error('The #update method is not available on EmbeddedDocuments');
  205. };
  206. /**
  207. * Helper for console.log
  208. *
  209. * @api public
  210. */
  211. EmbeddedDocument.prototype.inspect = function() {
  212. return this.toObject({
  213. transform: false,
  214. virtuals: false,
  215. flattenDecimals: false
  216. });
  217. };
  218. if (util.inspect.custom) {
  219. /*!
  220. * Avoid Node deprecation warning DEP0079
  221. */
  222. EmbeddedDocument.prototype[util.inspect.custom] = EmbeddedDocument.prototype.inspect;
  223. }
  224. /**
  225. * Marks a path as invalid, causing validation to fail.
  226. *
  227. * @param {String} path the field to invalidate
  228. * @param {String|Error} err error which states the reason `path` was invalid
  229. * @return {Boolean}
  230. * @api public
  231. */
  232. EmbeddedDocument.prototype.invalidate = function(path, err, val) {
  233. Document.prototype.invalidate.call(this, path, err, val);
  234. if (!this[documentArrayParent] || this.__index == null) {
  235. if (err[validatorErrorSymbol] || err.name === 'ValidationError') {
  236. return true;
  237. }
  238. throw err;
  239. }
  240. const index = this.__index;
  241. const parentPath = this.__parentArray.$path();
  242. const fullPath = [parentPath, index, path].join('.');
  243. this[documentArrayParent].invalidate(fullPath, err, val);
  244. return true;
  245. };
  246. /**
  247. * Marks a path as valid, removing existing validation errors.
  248. *
  249. * @param {String} path the field to mark as valid
  250. * @api private
  251. * @method $markValid
  252. * @receiver EmbeddedDocument
  253. */
  254. EmbeddedDocument.prototype.$markValid = function(path) {
  255. if (!this[documentArrayParent]) {
  256. return;
  257. }
  258. const index = this.__index;
  259. if (typeof index !== 'undefined') {
  260. const parentPath = this.__parentArray.$path();
  261. const fullPath = [parentPath, index, path].join('.');
  262. this[documentArrayParent].$markValid(fullPath);
  263. }
  264. };
  265. /*!
  266. * ignore
  267. */
  268. EmbeddedDocument.prototype.$ignore = function(path) {
  269. Document.prototype.$ignore.call(this, path);
  270. if (!this[documentArrayParent]) {
  271. return;
  272. }
  273. const index = this.__index;
  274. if (typeof index !== 'undefined') {
  275. const parentPath = this.__parentArray.$path();
  276. const fullPath = [parentPath, index, path].join('.');
  277. this[documentArrayParent].$ignore(fullPath);
  278. }
  279. };
  280. /**
  281. * Checks if a path is invalid
  282. *
  283. * @param {String} path the field to check
  284. * @api private
  285. * @method $isValid
  286. * @receiver EmbeddedDocument
  287. */
  288. EmbeddedDocument.prototype.$isValid = function(path) {
  289. const index = this.__index;
  290. if (typeof index !== 'undefined' && this[documentArrayParent]) {
  291. return !this[documentArrayParent].$__.validationError ||
  292. !this[documentArrayParent].$__.validationError.errors[this.$__fullPath(path)];
  293. }
  294. return true;
  295. };
  296. /**
  297. * Returns the top level document of this sub-document.
  298. *
  299. * @return {Document}
  300. */
  301. EmbeddedDocument.prototype.ownerDocument = function() {
  302. if (this.$__.ownerDocument) {
  303. return this.$__.ownerDocument;
  304. }
  305. let parent = this[documentArrayParent];
  306. if (!parent) {
  307. return this;
  308. }
  309. while (parent[documentArrayParent] || parent.$parent) {
  310. parent = parent[documentArrayParent] || parent.$parent;
  311. }
  312. this.$__.ownerDocument = parent;
  313. return this.$__.ownerDocument;
  314. };
  315. /**
  316. * Returns the full path to this document. If optional `path` is passed, it is appended to the full path.
  317. *
  318. * @param {String} [path]
  319. * @return {String}
  320. * @api private
  321. * @method $__fullPath
  322. * @memberOf EmbeddedDocument
  323. * @instance
  324. */
  325. EmbeddedDocument.prototype.$__fullPath = function(path) {
  326. if (!this.$__.fullPath) {
  327. let parent = this; // eslint-disable-line consistent-this
  328. if (!parent[documentArrayParent]) {
  329. return path;
  330. }
  331. const paths = [];
  332. while (parent[documentArrayParent] || parent.$parent) {
  333. if (parent[documentArrayParent]) {
  334. paths.unshift(parent.__parentArray.$path());
  335. } else {
  336. paths.unshift(parent.$basePath);
  337. }
  338. parent = parent[documentArrayParent] || parent.$parent;
  339. }
  340. this.$__.fullPath = paths.join('.');
  341. if (!this.$__.ownerDocument) {
  342. // optimization
  343. this.$__.ownerDocument = parent;
  344. }
  345. }
  346. return path
  347. ? this.$__.fullPath + '.' + path
  348. : this.$__.fullPath;
  349. };
  350. /**
  351. * Returns this sub-documents parent document.
  352. *
  353. * @api public
  354. */
  355. EmbeddedDocument.prototype.parent = function() {
  356. return this[documentArrayParent];
  357. };
  358. /**
  359. * Returns this sub-documents parent array.
  360. *
  361. * @api public
  362. */
  363. EmbeddedDocument.prototype.parentArray = function() {
  364. return this.__parentArray;
  365. };
  366. /*!
  367. * Module exports.
  368. */
  369. module.exports = EmbeddedDocument;