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.

290 lines
7.2 KiB

  1. /*!
  2. * Module requirements.
  3. */
  4. var SchemaType = require('../schematype');
  5. var CastError = SchemaType.CastError;
  6. var handleBitwiseOperator = require('./operators/bitwise');
  7. var MongooseError = require('../error');
  8. var utils = require('../utils');
  9. var Document;
  10. /**
  11. * Number SchemaType constructor.
  12. *
  13. * @param {String} key
  14. * @param {Object} options
  15. * @inherits SchemaType
  16. * @api public
  17. */
  18. function SchemaNumber(key, options) {
  19. SchemaType.call(this, key, options, 'Number');
  20. }
  21. /**
  22. * This schema type's name, to defend against minifiers that mangle
  23. * function names.
  24. *
  25. * @api public
  26. */
  27. SchemaNumber.schemaName = 'Number';
  28. /*!
  29. * Inherits from SchemaType.
  30. */
  31. SchemaNumber.prototype = Object.create(SchemaType.prototype);
  32. SchemaNumber.prototype.constructor = SchemaNumber;
  33. /**
  34. * Check if the given value satisfies a required validator.
  35. *
  36. * @param {Any} value
  37. * @param {Document} doc
  38. * @return {Boolean}
  39. * @api public
  40. */
  41. SchemaNumber.prototype.checkRequired = function checkRequired(value, doc) {
  42. if (SchemaType._isRef(this, value, doc, true)) {
  43. return !!value;
  44. }
  45. return typeof value === 'number' || value instanceof Number;
  46. };
  47. /**
  48. * Sets a minimum number validator.
  49. *
  50. * ####Example:
  51. *
  52. * var s = new Schema({ n: { type: Number, min: 10 })
  53. * var M = db.model('M', s)
  54. * var m = new M({ n: 9 })
  55. * m.save(function (err) {
  56. * console.error(err) // validator error
  57. * m.n = 10;
  58. * m.save() // success
  59. * })
  60. *
  61. * // custom error messages
  62. * // We can also use the special {MIN} token which will be replaced with the invalid value
  63. * var min = [10, 'The value of path `{PATH}` ({VALUE}) is beneath the limit ({MIN}).'];
  64. * var schema = new Schema({ n: { type: Number, min: min })
  65. * var M = mongoose.model('Measurement', schema);
  66. * var s= new M({ n: 4 });
  67. * s.validate(function (err) {
  68. * console.log(String(err)) // ValidationError: The value of path `n` (4) is beneath the limit (10).
  69. * })
  70. *
  71. * @param {Number} value minimum number
  72. * @param {String} [message] optional custom error message
  73. * @return {SchemaType} this
  74. * @see Customized Error Messages #error_messages_MongooseError-messages
  75. * @api public
  76. */
  77. SchemaNumber.prototype.min = function(value, message) {
  78. if (this.minValidator) {
  79. this.validators = this.validators.filter(function(v) {
  80. return v.validator !== this.minValidator;
  81. }, this);
  82. }
  83. if (value !== null && value !== undefined) {
  84. var msg = message || MongooseError.messages.Number.min;
  85. msg = msg.replace(/{MIN}/, value);
  86. this.validators.push({
  87. validator: this.minValidator = function(v) {
  88. return v == null || v >= value;
  89. },
  90. message: msg,
  91. type: 'min',
  92. min: value
  93. });
  94. }
  95. return this;
  96. };
  97. /**
  98. * Sets a maximum number validator.
  99. *
  100. * ####Example:
  101. *
  102. * var s = new Schema({ n: { type: Number, max: 10 })
  103. * var M = db.model('M', s)
  104. * var m = new M({ n: 11 })
  105. * m.save(function (err) {
  106. * console.error(err) // validator error
  107. * m.n = 10;
  108. * m.save() // success
  109. * })
  110. *
  111. * // custom error messages
  112. * // We can also use the special {MAX} token which will be replaced with the invalid value
  113. * var max = [10, 'The value of path `{PATH}` ({VALUE}) exceeds the limit ({MAX}).'];
  114. * var schema = new Schema({ n: { type: Number, max: max })
  115. * var M = mongoose.model('Measurement', schema);
  116. * var s= new M({ n: 4 });
  117. * s.validate(function (err) {
  118. * console.log(String(err)) // ValidationError: The value of path `n` (4) exceeds the limit (10).
  119. * })
  120. *
  121. * @param {Number} maximum number
  122. * @param {String} [message] optional custom error message
  123. * @return {SchemaType} this
  124. * @see Customized Error Messages #error_messages_MongooseError-messages
  125. * @api public
  126. */
  127. SchemaNumber.prototype.max = function(value, message) {
  128. if (this.maxValidator) {
  129. this.validators = this.validators.filter(function(v) {
  130. return v.validator !== this.maxValidator;
  131. }, this);
  132. }
  133. if (value !== null && value !== undefined) {
  134. var msg = message || MongooseError.messages.Number.max;
  135. msg = msg.replace(/{MAX}/, value);
  136. this.validators.push({
  137. validator: this.maxValidator = function(v) {
  138. return v == null || v <= value;
  139. },
  140. message: msg,
  141. type: 'max',
  142. max: value
  143. });
  144. }
  145. return this;
  146. };
  147. /**
  148. * Casts to number
  149. *
  150. * @param {Object} value value to cast
  151. * @param {Document} doc document that triggers the casting
  152. * @param {Boolean} init
  153. * @api private
  154. */
  155. SchemaNumber.prototype.cast = function(value, doc, init) {
  156. if (SchemaType._isRef(this, value, doc, init)) {
  157. // wait! we may need to cast this to a document
  158. if (value === null || value === undefined) {
  159. return value;
  160. }
  161. // lazy load
  162. Document || (Document = require('./../document'));
  163. if (value instanceof Document) {
  164. value.$__.wasPopulated = true;
  165. return value;
  166. }
  167. // setting a populated path
  168. if (typeof value === 'number') {
  169. return value;
  170. } else if (Buffer.isBuffer(value) || !utils.isObject(value)) {
  171. throw new CastError('number', value, this.path);
  172. }
  173. // Handle the case where user directly sets a populated
  174. // path to a plain object; cast to the Model used in
  175. // the population query.
  176. var path = doc.$__fullPath(this.path);
  177. var owner = doc.ownerDocument ? doc.ownerDocument() : doc;
  178. var pop = owner.populated(path, true);
  179. var ret = new pop.options.model(value);
  180. ret.$__.wasPopulated = true;
  181. return ret;
  182. }
  183. var val = value && value._id
  184. ? value._id // documents
  185. : value;
  186. if (!isNaN(val)) {
  187. if (val === null) {
  188. return val;
  189. }
  190. if (val === '') {
  191. return null;
  192. }
  193. if (typeof val === 'string' || typeof val === 'boolean') {
  194. val = Number(val);
  195. }
  196. if (val instanceof Number) {
  197. return val;
  198. }
  199. if (typeof val === 'number') {
  200. return val;
  201. }
  202. if (val.toString && !Array.isArray(val) && val.toString() == Number(val)) {
  203. return new Number(val);
  204. }
  205. }
  206. throw new CastError('number', value, this.path);
  207. };
  208. /*!
  209. * ignore
  210. */
  211. function handleSingle(val) {
  212. return this.cast(val);
  213. }
  214. function handleArray(val) {
  215. var _this = this;
  216. if (!Array.isArray(val)) {
  217. return [this.cast(val)];
  218. }
  219. return val.map(function(m) {
  220. return _this.cast(m);
  221. });
  222. }
  223. SchemaNumber.prototype.$conditionalHandlers =
  224. utils.options(SchemaType.prototype.$conditionalHandlers, {
  225. $bitsAllClear: handleBitwiseOperator,
  226. $bitsAnyClear: handleBitwiseOperator,
  227. $bitsAllSet: handleBitwiseOperator,
  228. $bitsAnySet: handleBitwiseOperator,
  229. $gt: handleSingle,
  230. $gte: handleSingle,
  231. $lt: handleSingle,
  232. $lte: handleSingle,
  233. $mod: handleArray
  234. });
  235. /**
  236. * Casts contents for queries.
  237. *
  238. * @param {String} $conditional
  239. * @param {any} [value]
  240. * @api private
  241. */
  242. SchemaNumber.prototype.castForQuery = function($conditional, val) {
  243. var handler;
  244. if (arguments.length === 2) {
  245. handler = this.$conditionalHandlers[$conditional];
  246. if (!handler) {
  247. throw new Error('Can\'t use ' + $conditional + ' with Number.');
  248. }
  249. return handler.call(this, val);
  250. }
  251. val = this.cast($conditional);
  252. return val;
  253. };
  254. /*!
  255. * Module exports.
  256. */
  257. module.exports = SchemaNumber;