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 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. const CastError = require('../error/cast');
  6. const EventEmitter = require('events').EventEmitter;
  7. const ObjectExpectedError = require('../error/objectExpected');
  8. const SchemaType = require('../schematype');
  9. const $exists = require('./operators/exists');
  10. const castToNumber = require('./operators/helpers').castToNumber;
  11. const discriminator = require('../helpers/model/discriminator');
  12. const geospatial = require('./operators/geospatial');
  13. const get = require('../helpers/get');
  14. const getConstructor = require('../helpers/discriminator/getConstructor');
  15. const internalToObjectOptions = require('../options').internalToObjectOptions;
  16. let Subdocument;
  17. module.exports = Embedded;
  18. /**
  19. * Sub-schema schematype constructor
  20. *
  21. * @param {Schema} schema
  22. * @param {String} key
  23. * @param {Object} options
  24. * @inherits SchemaType
  25. * @api public
  26. */
  27. function Embedded(schema, path, options) {
  28. this.caster = _createConstructor(schema);
  29. this.caster.path = path;
  30. this.caster.prototype.$basePath = path;
  31. this.schema = schema;
  32. this.$isSingleNested = true;
  33. SchemaType.call(this, path, options, 'Embedded');
  34. }
  35. /*!
  36. * ignore
  37. */
  38. Embedded.prototype = Object.create(SchemaType.prototype);
  39. Embedded.prototype.constructor = Embedded;
  40. /*!
  41. * ignore
  42. */
  43. function _createConstructor(schema) {
  44. // lazy load
  45. Subdocument || (Subdocument = require('../types/subdocument'));
  46. const _embedded = function SingleNested(value, path, parent) {
  47. const _this = this;
  48. this.$parent = parent;
  49. Subdocument.apply(this, arguments);
  50. this.$session(this.ownerDocument().$session());
  51. if (parent) {
  52. parent.on('save', function() {
  53. _this.emit('save', _this);
  54. _this.constructor.emit('save', _this);
  55. });
  56. parent.on('isNew', function(val) {
  57. _this.isNew = val;
  58. _this.emit('isNew', val);
  59. _this.constructor.emit('isNew', val);
  60. });
  61. }
  62. };
  63. _embedded.prototype = Object.create(Subdocument.prototype);
  64. _embedded.prototype.$__setSchema(schema);
  65. _embedded.prototype.constructor = _embedded;
  66. _embedded.schema = schema;
  67. _embedded.$isSingleNested = true;
  68. _embedded.events = new EventEmitter();
  69. _embedded.prototype.toBSON = function() {
  70. return this.toObject(internalToObjectOptions);
  71. };
  72. // apply methods
  73. for (const i in schema.methods) {
  74. _embedded.prototype[i] = schema.methods[i];
  75. }
  76. // apply statics
  77. for (const i in schema.statics) {
  78. _embedded[i] = schema.statics[i];
  79. }
  80. for (const i in EventEmitter.prototype) {
  81. _embedded[i] = EventEmitter.prototype[i];
  82. }
  83. return _embedded;
  84. }
  85. /*!
  86. * Special case for when users use a common location schema to represent
  87. * locations for use with $geoWithin.
  88. * https://docs.mongodb.org/manual/reference/operator/query/geoWithin/
  89. *
  90. * @param {Object} val
  91. * @api private
  92. */
  93. Embedded.prototype.$conditionalHandlers.$geoWithin = function handle$geoWithin(val) {
  94. return { $geometry: this.castForQuery(val.$geometry) };
  95. };
  96. /*!
  97. * ignore
  98. */
  99. Embedded.prototype.$conditionalHandlers.$near =
  100. Embedded.prototype.$conditionalHandlers.$nearSphere = geospatial.cast$near;
  101. Embedded.prototype.$conditionalHandlers.$within =
  102. Embedded.prototype.$conditionalHandlers.$geoWithin = geospatial.cast$within;
  103. Embedded.prototype.$conditionalHandlers.$geoIntersects =
  104. geospatial.cast$geoIntersects;
  105. Embedded.prototype.$conditionalHandlers.$minDistance = castToNumber;
  106. Embedded.prototype.$conditionalHandlers.$maxDistance = castToNumber;
  107. Embedded.prototype.$conditionalHandlers.$exists = $exists;
  108. /**
  109. * Casts contents
  110. *
  111. * @param {Object} value
  112. * @api private
  113. */
  114. Embedded.prototype.cast = function(val, doc, init, priorVal) {
  115. if (val && val.$isSingleNested) {
  116. return val;
  117. }
  118. if (val != null && (typeof val !== 'object' || Array.isArray(val))) {
  119. throw new ObjectExpectedError(this.path, val);
  120. }
  121. const Constructor = getConstructor(this.caster, val);
  122. let subdoc;
  123. // Only pull relevant selected paths and pull out the base path
  124. const parentSelected = get(doc, '$__.selected', {});
  125. const path = this.path;
  126. const selected = Object.keys(parentSelected).reduce((obj, key) => {
  127. if (key.startsWith(path + '.')) {
  128. obj[key.substr(path.length + 1)] = parentSelected[key];
  129. }
  130. return obj;
  131. }, {});
  132. if (init) {
  133. subdoc = new Constructor(void 0, selected, doc);
  134. subdoc.init(val);
  135. } else {
  136. if (Object.keys(val).length === 0) {
  137. return new Constructor({}, selected, doc);
  138. }
  139. return new Constructor(val, selected, doc, undefined, { priorDoc: priorVal });
  140. }
  141. return subdoc;
  142. };
  143. /**
  144. * Casts contents for query
  145. *
  146. * @param {string} [$conditional] optional query operator (like `$eq` or `$in`)
  147. * @param {any} value
  148. * @api private
  149. */
  150. Embedded.prototype.castForQuery = function($conditional, val) {
  151. let handler;
  152. if (arguments.length === 2) {
  153. handler = this.$conditionalHandlers[$conditional];
  154. if (!handler) {
  155. throw new Error('Can\'t use ' + $conditional);
  156. }
  157. return handler.call(this, val);
  158. }
  159. val = $conditional;
  160. if (val == null) {
  161. return val;
  162. }
  163. if (this.options.runSetters) {
  164. val = this._applySetters(val);
  165. }
  166. const Constructor = getConstructor(this.caster, val);
  167. try {
  168. val = new Constructor(val);
  169. } catch (error) {
  170. // Make sure we always wrap in a CastError (gh-6803)
  171. if (!(error instanceof CastError)) {
  172. throw new CastError('Embedded', val, this.path, error);
  173. }
  174. throw error;
  175. }
  176. return val;
  177. };
  178. /**
  179. * Async validation on this single nested doc.
  180. *
  181. * @api private
  182. */
  183. Embedded.prototype.doValidate = function(value, fn, scope, options) {
  184. const Constructor = getConstructor(this.caster, value);
  185. if (options && options.skipSchemaValidators) {
  186. if (!(value instanceof Constructor)) {
  187. value = new Constructor(value, null, scope);
  188. }
  189. return value.validate(fn);
  190. }
  191. SchemaType.prototype.doValidate.call(this, value, function(error) {
  192. if (error) {
  193. return fn(error);
  194. }
  195. if (!value) {
  196. return fn(null);
  197. }
  198. value.validate(fn);
  199. }, scope);
  200. };
  201. /**
  202. * Synchronously validate this single nested doc
  203. *
  204. * @api private
  205. */
  206. Embedded.prototype.doValidateSync = function(value, scope, options) {
  207. if (!options || !options.skipSchemaValidators) {
  208. const schemaTypeError = SchemaType.prototype.doValidateSync.call(this, value, scope);
  209. if (schemaTypeError) {
  210. return schemaTypeError;
  211. }
  212. }
  213. if (!value) {
  214. return;
  215. }
  216. return value.validateSync();
  217. };
  218. /**
  219. * Adds a discriminator to this property
  220. *
  221. * @param {String} name
  222. * @param {Schema} schema fields to add to the schema for instances of this sub-class
  223. * @api public
  224. */
  225. Embedded.prototype.discriminator = function(name, schema) {
  226. discriminator(this.caster, name, schema);
  227. this.caster.discriminators[name] = _createConstructor(schema);
  228. return this.caster.discriminators[name];
  229. };
  230. /*!
  231. * ignore
  232. */
  233. Embedded.prototype.clone = function() {
  234. const options = Object.assign({}, this.options);
  235. const schematype = new this.constructor(this.schema, this.path, options);
  236. schematype.validators = this.validators.slice();
  237. schematype.caster.discriminators = Object.assign({}, this.caster.discriminators);
  238. return schematype;
  239. };