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.

170 lines
3.9 KiB

  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. var SchemaType = require('../schematype');
  6. var Subdocument = require('../types/subdocument');
  7. var castToNumber = require('./operators/helpers').castToNumber;
  8. var geospatial = require('./operators/geospatial');
  9. module.exports = Embedded;
  10. /**
  11. * Sub-schema schematype constructor
  12. *
  13. * @param {Schema} schema
  14. * @param {String} key
  15. * @param {Object} options
  16. * @inherits SchemaType
  17. * @api public
  18. */
  19. function Embedded(schema, path, options) {
  20. var _embedded = function(value, path, parent) {
  21. var _this = this;
  22. Subdocument.apply(this, arguments);
  23. this.$parent = parent;
  24. if (parent) {
  25. parent.on('save', function() {
  26. _this.emit('save', _this);
  27. });
  28. parent.on('isNew', function(val) {
  29. _this.isNew = val;
  30. _this.emit('isNew', val);
  31. });
  32. }
  33. };
  34. _embedded.prototype = Object.create(Subdocument.prototype);
  35. _embedded.prototype.$__setSchema(schema);
  36. _embedded.schema = schema;
  37. _embedded.$isSingleNested = true;
  38. _embedded.prototype.$basePath = path;
  39. // apply methods
  40. for (var i in schema.methods) {
  41. _embedded.prototype[i] = schema.methods[i];
  42. }
  43. // apply statics
  44. for (i in schema.statics) {
  45. _embedded[i] = schema.statics[i];
  46. }
  47. this.caster = _embedded;
  48. this.schema = schema;
  49. this.$isSingleNested = true;
  50. SchemaType.call(this, path, options, 'Embedded');
  51. }
  52. Embedded.prototype = Object.create(SchemaType.prototype);
  53. /**
  54. * Special case for when users use a common location schema to represent
  55. * locations for use with $geoWithin.
  56. * https://docs.mongodb.org/manual/reference/operator/query/geoWithin/
  57. *
  58. * @param {Object} val
  59. * @api private
  60. */
  61. Embedded.prototype.$conditionalHandlers.$geoWithin = function(val) {
  62. return { $geometry: this.castForQuery(val.$geometry) };
  63. };
  64. /*!
  65. * ignore
  66. */
  67. Embedded.prototype.$conditionalHandlers.$near =
  68. Embedded.prototype.$conditionalHandlers.$nearSphere = geospatial.cast$near;
  69. Embedded.prototype.$conditionalHandlers.$within =
  70. Embedded.prototype.$conditionalHandlers.$geoWithin = geospatial.cast$within;
  71. Embedded.prototype.$conditionalHandlers.$geoIntersects =
  72. geospatial.cast$geoIntersects;
  73. Embedded.prototype.$conditionalHandlers.$minDistance = castToNumber;
  74. Embedded.prototype.$conditionalHandlers.$maxDistance = castToNumber;
  75. /**
  76. * Casts contents
  77. *
  78. * @param {Object} value
  79. * @api private
  80. */
  81. Embedded.prototype.cast = function(val, doc, init) {
  82. if (val && val.$isSingleNested) {
  83. return val;
  84. }
  85. var subdoc = new this.caster(void 0, doc ? doc.$__.selected : void 0, doc);
  86. if (init) {
  87. subdoc.init(val);
  88. } else {
  89. subdoc.set(val, undefined, true);
  90. }
  91. return subdoc;
  92. };
  93. /**
  94. * Casts contents for query
  95. *
  96. * @param {string} [$conditional] optional query operator (like `$eq` or `$in`)
  97. * @param {any} value
  98. * @api private
  99. */
  100. Embedded.prototype.castForQuery = function($conditional, val) {
  101. var handler;
  102. if (arguments.length === 2) {
  103. handler = this.$conditionalHandlers[$conditional];
  104. if (!handler) {
  105. throw new Error('Can\'t use ' + $conditional);
  106. }
  107. return handler.call(this, val);
  108. }
  109. val = $conditional;
  110. if (val == null) {
  111. return val;
  112. }
  113. return new this.caster(val).toObject({virtuals: false});
  114. };
  115. /**
  116. * Async validation on this single nested doc.
  117. *
  118. * @api private
  119. */
  120. Embedded.prototype.doValidate = function(value, fn) {
  121. SchemaType.prototype.doValidate.call(this, value, function(error) {
  122. if (error) {
  123. return fn(error);
  124. }
  125. if (!value) {
  126. return fn(null);
  127. }
  128. value.validate(fn, {__noPromise: true});
  129. });
  130. };
  131. /**
  132. * Synchronously validate this single nested doc
  133. *
  134. * @api private
  135. */
  136. Embedded.prototype.doValidateSync = function(value) {
  137. var schemaTypeError = SchemaType.prototype.doValidateSync.call(this, value);
  138. if (schemaTypeError) {
  139. return schemaTypeError;
  140. }
  141. if (!value) {
  142. return;
  143. }
  144. return value.validateSync();
  145. };