embedded.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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. _embedded.prototype.toBSON = function() {
  40. return this.toObject({
  41. transform: false,
  42. retainKeyOrder: schema.options.retainKeyOrder,
  43. virtuals: false
  44. });
  45. };
  46. // apply methods
  47. for (var i in schema.methods) {
  48. _embedded.prototype[i] = schema.methods[i];
  49. }
  50. // apply statics
  51. for (i in schema.statics) {
  52. _embedded[i] = schema.statics[i];
  53. }
  54. this.caster = _embedded;
  55. this.schema = schema;
  56. this.$isSingleNested = true;
  57. SchemaType.call(this, path, options, 'Embedded');
  58. }
  59. Embedded.prototype = Object.create(SchemaType.prototype);
  60. /**
  61. * Special case for when users use a common location schema to represent
  62. * locations for use with $geoWithin.
  63. * https://docs.mongodb.org/manual/reference/operator/query/geoWithin/
  64. *
  65. * @param {Object} val
  66. * @api private
  67. */
  68. Embedded.prototype.$conditionalHandlers.$geoWithin = function(val) {
  69. return { $geometry: this.castForQuery(val.$geometry) };
  70. };
  71. /*!
  72. * ignore
  73. */
  74. Embedded.prototype.$conditionalHandlers.$near =
  75. Embedded.prototype.$conditionalHandlers.$nearSphere = geospatial.cast$near;
  76. Embedded.prototype.$conditionalHandlers.$within =
  77. Embedded.prototype.$conditionalHandlers.$geoWithin = geospatial.cast$within;
  78. Embedded.prototype.$conditionalHandlers.$geoIntersects =
  79. geospatial.cast$geoIntersects;
  80. Embedded.prototype.$conditionalHandlers.$minDistance = castToNumber;
  81. Embedded.prototype.$conditionalHandlers.$maxDistance = castToNumber;
  82. /**
  83. * Casts contents
  84. *
  85. * @param {Object} value
  86. * @api private
  87. */
  88. Embedded.prototype.cast = function(val, doc, init) {
  89. if (val && val.$isSingleNested) {
  90. return val;
  91. }
  92. var subdoc = new this.caster(void 0, doc ? doc.$__.selected : void 0, doc);
  93. if (init) {
  94. subdoc.init(val);
  95. } else {
  96. subdoc.set(val, undefined, true);
  97. }
  98. return subdoc;
  99. };
  100. /**
  101. * Casts contents for query
  102. *
  103. * @param {string} [$conditional] optional query operator (like `$eq` or `$in`)
  104. * @param {any} value
  105. * @api private
  106. */
  107. Embedded.prototype.castForQuery = function($conditional, val) {
  108. var handler;
  109. if (arguments.length === 2) {
  110. handler = this.$conditionalHandlers[$conditional];
  111. if (!handler) {
  112. throw new Error('Can\'t use ' + $conditional);
  113. }
  114. return handler.call(this, val);
  115. }
  116. val = $conditional;
  117. if (val == null) {
  118. return val;
  119. }
  120. return new this.caster(val);
  121. };
  122. /**
  123. * Async validation on this single nested doc.
  124. *
  125. * @api private
  126. */
  127. Embedded.prototype.doValidate = function(value, fn, scope) {
  128. SchemaType.prototype.doValidate.call(this, value, function(error) {
  129. if (error) {
  130. return fn(error);
  131. }
  132. if (!value) {
  133. return fn(null);
  134. }
  135. value.validate(fn, {__noPromise: true});
  136. }, scope);
  137. };
  138. /**
  139. * Synchronously validate this single nested doc
  140. *
  141. * @api private
  142. */
  143. Embedded.prototype.doValidateSync = function(value, scope) {
  144. var schemaTypeError = SchemaType.prototype.doValidateSync.call(this, value, scope);
  145. if (schemaTypeError) {
  146. return schemaTypeError;
  147. }
  148. if (!value) {
  149. return;
  150. }
  151. return value.validateSync();
  152. };