number.js 6.8 KB

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