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.

schematype.js 40KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. const MongooseError = require('./error');
  6. const $exists = require('./schema/operators/exists');
  7. const $type = require('./schema/operators/type');
  8. const get = require('./helpers/get');
  9. const handleImmutable = require('./helpers/schematype/handleImmutable');
  10. const immediate = require('./helpers/immediate');
  11. const schemaTypeSymbol = require('./helpers/symbols').schemaTypeSymbol;
  12. const util = require('util');
  13. const utils = require('./utils');
  14. const validatorErrorSymbol = require('./helpers/symbols').validatorErrorSymbol;
  15. const CastError = MongooseError.CastError;
  16. const ValidatorError = MongooseError.ValidatorError;
  17. /**
  18. * SchemaType constructor. Do **not** instantiate `SchemaType` directly.
  19. * Mongoose converts your schema paths into SchemaTypes automatically.
  20. *
  21. * ####Example:
  22. *
  23. * const schema = new Schema({ name: String });
  24. * schema.path('name') instanceof SchemaType; // true
  25. *
  26. * @param {String} path
  27. * @param {Object} [options]
  28. * @param {String} [instance]
  29. * @api public
  30. */
  31. function SchemaType(path, options, instance) {
  32. this[schemaTypeSymbol] = true;
  33. this.path = path;
  34. this.instance = instance;
  35. this.validators = [];
  36. this.getters = this.constructor.hasOwnProperty('getters') ?
  37. this.constructor.getters.slice() :
  38. [];
  39. this.setters = [];
  40. this.options = options;
  41. this._index = null;
  42. this.selected;
  43. if (utils.hasUserDefinedProperty(options, 'immutable')) {
  44. this.$immutable = options.immutable;
  45. handleImmutable(this);
  46. }
  47. for (const prop in options) {
  48. if (this[prop] && typeof this[prop] === 'function') {
  49. // { unique: true, index: true }
  50. if (prop === 'index' && this._index) {
  51. if (options.index === false) {
  52. const index = this._index;
  53. if (typeof index === 'object' && index != null) {
  54. if (index.unique) {
  55. throw new Error('Path "' + this.path + '" may not have `index` ' +
  56. 'set to false and `unique` set to true');
  57. }
  58. if (index.sparse) {
  59. throw new Error('Path "' + this.path + '" may not have `index` ' +
  60. 'set to false and `sparse` set to true');
  61. }
  62. }
  63. this._index = false;
  64. }
  65. continue;
  66. }
  67. const val = options[prop];
  68. // Special case so we don't screw up array defaults, see gh-5780
  69. if (prop === 'default') {
  70. this.default(val);
  71. continue;
  72. }
  73. const opts = Array.isArray(val) ? val : [val];
  74. this[prop].apply(this, opts);
  75. }
  76. }
  77. Object.defineProperty(this, '$$context', {
  78. enumerable: false,
  79. configurable: false,
  80. writable: true,
  81. value: null
  82. });
  83. }
  84. /**
  85. * Get/set the function used to cast arbitrary values to this type.
  86. *
  87. * ####Example:
  88. *
  89. * // Disallow `null` for numbers, and don't try to cast any values to
  90. * // numbers, so even strings like '123' will cause a CastError.
  91. * mongoose.Number.cast(function(v) {
  92. * assert.ok(v === undefined || typeof v === 'number');
  93. * return v;
  94. * });
  95. *
  96. * @param {Function|false} caster Function that casts arbitrary values to this type, or throws an error if casting failed
  97. * @return {Function}
  98. * @static
  99. * @receiver SchemaType
  100. * @function cast
  101. * @api public
  102. */
  103. SchemaType.cast = function cast(caster) {
  104. if (arguments.length === 0) {
  105. return this._cast;
  106. }
  107. if (caster === false) {
  108. caster = v => v;
  109. }
  110. this._cast = caster;
  111. return this._cast;
  112. };
  113. /**
  114. * Attaches a getter for all instances of this schema type.
  115. *
  116. * ####Example:
  117. *
  118. * // Make all numbers round down
  119. * mongoose.Number.get(function(v) { return Math.floor(v); });
  120. *
  121. * @param {Function} getter
  122. * @return {this}
  123. * @static
  124. * @receiver SchemaType
  125. * @function get
  126. * @api public
  127. */
  128. SchemaType.get = function(getter) {
  129. this.getters = this.hasOwnProperty('getters') ? this.getters : [];
  130. this.getters.push(getter);
  131. };
  132. /**
  133. * Sets a default value for this SchemaType.
  134. *
  135. * ####Example:
  136. *
  137. * var schema = new Schema({ n: { type: Number, default: 10 })
  138. * var M = db.model('M', schema)
  139. * var m = new M;
  140. * console.log(m.n) // 10
  141. *
  142. * Defaults can be either `functions` which return the value to use as the default or the literal value itself. Either way, the value will be cast based on its schema type before being set during document creation.
  143. *
  144. * ####Example:
  145. *
  146. * // values are cast:
  147. * var schema = new Schema({ aNumber: { type: Number, default: 4.815162342 }})
  148. * var M = db.model('M', schema)
  149. * var m = new M;
  150. * console.log(m.aNumber) // 4.815162342
  151. *
  152. * // default unique objects for Mixed types:
  153. * var schema = new Schema({ mixed: Schema.Types.Mixed });
  154. * schema.path('mixed').default(function () {
  155. * return {};
  156. * });
  157. *
  158. * // if we don't use a function to return object literals for Mixed defaults,
  159. * // each document will receive a reference to the same object literal creating
  160. * // a "shared" object instance:
  161. * var schema = new Schema({ mixed: Schema.Types.Mixed });
  162. * schema.path('mixed').default({});
  163. * var M = db.model('M', schema);
  164. * var m1 = new M;
  165. * m1.mixed.added = 1;
  166. * console.log(m1.mixed); // { added: 1 }
  167. * var m2 = new M;
  168. * console.log(m2.mixed); // { added: 1 }
  169. *
  170. * @param {Function|any} val the default value
  171. * @return {defaultValue}
  172. * @api public
  173. */
  174. SchemaType.prototype.default = function(val) {
  175. if (arguments.length === 1) {
  176. if (val === void 0) {
  177. this.defaultValue = void 0;
  178. return void 0;
  179. }
  180. this.defaultValue = val;
  181. return this.defaultValue;
  182. } else if (arguments.length > 1) {
  183. this.defaultValue = utils.args(arguments);
  184. }
  185. return this.defaultValue;
  186. };
  187. /**
  188. * Declares the index options for this schematype.
  189. *
  190. * ####Example:
  191. *
  192. * var s = new Schema({ name: { type: String, index: true })
  193. * var s = new Schema({ loc: { type: [Number], index: 'hashed' })
  194. * var s = new Schema({ loc: { type: [Number], index: '2d', sparse: true })
  195. * var s = new Schema({ loc: { type: [Number], index: { type: '2dsphere', sparse: true }})
  196. * var s = new Schema({ date: { type: Date, index: { unique: true, expires: '1d' }})
  197. * Schema.path('my.path').index(true);
  198. * Schema.path('my.date').index({ expires: 60 });
  199. * Schema.path('my.path').index({ unique: true, sparse: true });
  200. *
  201. * ####NOTE:
  202. *
  203. * _Indexes are created [in the background](https://docs.mongodb.com/manual/core/index-creation/#index-creation-background)
  204. * by default. If `background` is set to `false`, MongoDB will not execute any
  205. * read/write operations you send until the index build.
  206. * Specify `background: false` to override Mongoose's default._
  207. *
  208. * @param {Object|Boolean|String} options
  209. * @return {SchemaType} this
  210. * @api public
  211. */
  212. SchemaType.prototype.index = function(options) {
  213. this._index = options;
  214. utils.expires(this._index);
  215. return this;
  216. };
  217. /**
  218. * Declares an unique index.
  219. *
  220. * ####Example:
  221. *
  222. * var s = new Schema({ name: { type: String, unique: true }});
  223. * Schema.path('name').index({ unique: true });
  224. *
  225. * _NOTE: violating the constraint returns an `E11000` error from MongoDB when saving, not a Mongoose validation error._
  226. *
  227. * @param {Boolean} bool
  228. * @return {SchemaType} this
  229. * @api public
  230. */
  231. SchemaType.prototype.unique = function(bool) {
  232. if (this._index === false) {
  233. if (!bool) {
  234. return;
  235. }
  236. throw new Error('Path "' + this.path + '" may not have `index` set to ' +
  237. 'false and `unique` set to true');
  238. }
  239. if (this._index == null || this._index === true) {
  240. this._index = {};
  241. } else if (typeof this._index === 'string') {
  242. this._index = {type: this._index};
  243. }
  244. this._index.unique = bool;
  245. return this;
  246. };
  247. /**
  248. * Declares a full text index.
  249. *
  250. * ###Example:
  251. *
  252. * var s = new Schema({name : {type: String, text : true })
  253. * Schema.path('name').index({text : true});
  254. * @param {Boolean} bool
  255. * @return {SchemaType} this
  256. * @api public
  257. */
  258. SchemaType.prototype.text = function(bool) {
  259. if (this._index === false) {
  260. if (!bool) {
  261. return;
  262. }
  263. throw new Error('Path "' + this.path + '" may not have `index` set to ' +
  264. 'false and `text` set to true');
  265. }
  266. if (this._index === null || this._index === undefined ||
  267. typeof this._index === 'boolean') {
  268. this._index = {};
  269. } else if (typeof this._index === 'string') {
  270. this._index = {type: this._index};
  271. }
  272. this._index.text = bool;
  273. return this;
  274. };
  275. /**
  276. * Declares a sparse index.
  277. *
  278. * ####Example:
  279. *
  280. * var s = new Schema({ name: { type: String, sparse: true } });
  281. * Schema.path('name').index({ sparse: true });
  282. *
  283. * @param {Boolean} bool
  284. * @return {SchemaType} this
  285. * @api public
  286. */
  287. SchemaType.prototype.sparse = function(bool) {
  288. if (this._index === false) {
  289. if (!bool) {
  290. return;
  291. }
  292. throw new Error('Path "' + this.path + '" may not have `index` set to ' +
  293. 'false and `sparse` set to true');
  294. }
  295. if (this._index == null || typeof this._index === 'boolean') {
  296. this._index = {};
  297. } else if (typeof this._index === 'string') {
  298. this._index = {type: this._index};
  299. }
  300. this._index.sparse = bool;
  301. return this;
  302. };
  303. /**
  304. * Defines this path as immutable. Mongoose prevents you from changing
  305. * immutable paths unless the parent document has [`isNew: true`](/docs/api.html#document_Document-isNew).
  306. *
  307. * ####Example:
  308. *
  309. * const schema = new Schema({
  310. * name: { type: String, immutable: true },
  311. * age: Number
  312. * });
  313. * const Model = mongoose.model('Test', schema);
  314. *
  315. * await Model.create({ name: 'test' });
  316. * const doc = await Model.findOne();
  317. *
  318. * doc.isNew; // false
  319. * doc.name = 'new name';
  320. * doc.name; // 'test', because `name` is immutable
  321. *
  322. * Mongoose also prevents changing immutable properties using `updateOne()`
  323. * and `updateMany()` based on [strict mode](/docs/guide.html#strict).
  324. *
  325. * ####Example:
  326. *
  327. * // Mongoose will strip out the `name` update, because `name` is immutable
  328. * Model.updateOne({}, { $set: { name: 'test2' }, $inc: { age: 1 } });
  329. *
  330. * // If `strict` is set to 'throw', Mongoose will throw an error if you
  331. * // update `name`
  332. * const err = await Model.updateOne({}, { name: 'test2' }, { strict: 'throw' }).
  333. * then(() => null, err => err);
  334. * err.name; // StrictModeError
  335. *
  336. * // If `strict` is `false`, Mongoose allows updating `name` even though
  337. * // the property is immutable.
  338. * Model.updateOne({}, { name: 'test2' }, { strict: false });
  339. *
  340. * @param {Boolean} bool
  341. * @return {SchemaType} this
  342. * @see isNew /docs/api.html#document_Document-isNew
  343. * @api public
  344. */
  345. SchemaType.prototype.immutable = function(bool) {
  346. this.$immutable = bool;
  347. handleImmutable(this);
  348. return this;
  349. };
  350. /**
  351. * Adds a setter to this schematype.
  352. *
  353. * ####Example:
  354. *
  355. * function capitalize (val) {
  356. * if (typeof val !== 'string') val = '';
  357. * return val.charAt(0).toUpperCase() + val.substring(1);
  358. * }
  359. *
  360. * // defining within the schema
  361. * var s = new Schema({ name: { type: String, set: capitalize }});
  362. *
  363. * // or with the SchemaType
  364. * var s = new Schema({ name: String })
  365. * s.path('name').set(capitalize);
  366. *
  367. * Setters allow you to transform the data before it gets to the raw mongodb
  368. * document or query.
  369. *
  370. * Suppose you are implementing user registration for a website. Users provide
  371. * an email and password, which gets saved to mongodb. The email is a string
  372. * that you will want to normalize to lower case, in order to avoid one email
  373. * having more than one account -- e.g., otherwise, avenue@q.com can be registered for 2 accounts via avenue@q.com and AvEnUe@Q.CoM.
  374. *
  375. * You can set up email lower case normalization easily via a Mongoose setter.
  376. *
  377. * function toLower(v) {
  378. * return v.toLowerCase();
  379. * }
  380. *
  381. * var UserSchema = new Schema({
  382. * email: { type: String, set: toLower }
  383. * });
  384. *
  385. * var User = db.model('User', UserSchema);
  386. *
  387. * var user = new User({email: 'AVENUE@Q.COM'});
  388. * console.log(user.email); // 'avenue@q.com'
  389. *
  390. * // or
  391. * var user = new User();
  392. * user.email = 'Avenue@Q.com';
  393. * console.log(user.email); // 'avenue@q.com'
  394. * User.updateOne({ _id: _id }, { $set: { email: 'AVENUE@Q.COM' } }); // update to 'avenue@q.com'
  395. *
  396. * As you can see above, setters allow you to transform the data before it
  397. * stored in MongoDB, or before executing a query.
  398. *
  399. * _NOTE: we could have also just used the built-in `lowercase: true` SchemaType option instead of defining our own function._
  400. *
  401. * new Schema({ email: { type: String, lowercase: true }})
  402. *
  403. * Setters are also passed a second argument, the schematype on which the setter was defined. This allows for tailored behavior based on options passed in the schema.
  404. *
  405. * function inspector (val, schematype) {
  406. * if (schematype.options.required) {
  407. * return schematype.path + ' is required';
  408. * } else {
  409. * return val;
  410. * }
  411. * }
  412. *
  413. * var VirusSchema = new Schema({
  414. * name: { type: String, required: true, set: inspector },
  415. * taxonomy: { type: String, set: inspector }
  416. * })
  417. *
  418. * var Virus = db.model('Virus', VirusSchema);
  419. * var v = new Virus({ name: 'Parvoviridae', taxonomy: 'Parvovirinae' });
  420. *
  421. * console.log(v.name); // name is required
  422. * console.log(v.taxonomy); // Parvovirinae
  423. *
  424. * You can also use setters to modify other properties on the document. If
  425. * you're setting a property `name` on a document, the setter will run with
  426. * `this` as the document. Be careful, in mongoose 5 setters will also run
  427. * when querying by `name` with `this` as the query.
  428. *
  429. * ```javascript
  430. * const nameSchema = new Schema({ name: String, keywords: [String] });
  431. * nameSchema.path('name').set(function(v) {
  432. * // Need to check if `this` is a document, because in mongoose 5
  433. * // setters will also run on queries, in which case `this` will be a
  434. * // mongoose query object.
  435. * if (this instanceof Document && v != null) {
  436. * this.keywords = v.split(' ');
  437. * }
  438. * return v;
  439. * });
  440. * ```
  441. *
  442. * @param {Function} fn
  443. * @return {SchemaType} this
  444. * @api public
  445. */
  446. SchemaType.prototype.set = function(fn) {
  447. if (typeof fn !== 'function') {
  448. throw new TypeError('A setter must be a function.');
  449. }
  450. this.setters.push(fn);
  451. return this;
  452. };
  453. /**
  454. * Adds a getter to this schematype.
  455. *
  456. * ####Example:
  457. *
  458. * function dob (val) {
  459. * if (!val) return val;
  460. * return (val.getMonth() + 1) + "/" + val.getDate() + "/" + val.getFullYear();
  461. * }
  462. *
  463. * // defining within the schema
  464. * var s = new Schema({ born: { type: Date, get: dob })
  465. *
  466. * // or by retreiving its SchemaType
  467. * var s = new Schema({ born: Date })
  468. * s.path('born').get(dob)
  469. *
  470. * Getters allow you to transform the representation of the data as it travels from the raw mongodb document to the value that you see.
  471. *
  472. * Suppose you are storing credit card numbers and you want to hide everything except the last 4 digits to the mongoose user. You can do so by defining a getter in the following way:
  473. *
  474. * function obfuscate (cc) {
  475. * return '****-****-****-' + cc.slice(cc.length-4, cc.length);
  476. * }
  477. *
  478. * var AccountSchema = new Schema({
  479. * creditCardNumber: { type: String, get: obfuscate }
  480. * });
  481. *
  482. * var Account = db.model('Account', AccountSchema);
  483. *
  484. * Account.findById(id, function (err, found) {
  485. * console.log(found.creditCardNumber); // '****-****-****-1234'
  486. * });
  487. *
  488. * Getters are also passed a second argument, the schematype on which the getter was defined. This allows for tailored behavior based on options passed in the schema.
  489. *
  490. * function inspector (val, schematype) {
  491. * if (schematype.options.required) {
  492. * return schematype.path + ' is required';
  493. * } else {
  494. * return schematype.path + ' is not';
  495. * }
  496. * }
  497. *
  498. * var VirusSchema = new Schema({
  499. * name: { type: String, required: true, get: inspector },
  500. * taxonomy: { type: String, get: inspector }
  501. * })
  502. *
  503. * var Virus = db.model('Virus', VirusSchema);
  504. *
  505. * Virus.findById(id, function (err, virus) {
  506. * console.log(virus.name); // name is required
  507. * console.log(virus.taxonomy); // taxonomy is not
  508. * })
  509. *
  510. * @param {Function} fn
  511. * @return {SchemaType} this
  512. * @api public
  513. */
  514. SchemaType.prototype.get = function(fn) {
  515. if (typeof fn !== 'function') {
  516. throw new TypeError('A getter must be a function.');
  517. }
  518. this.getters.push(fn);
  519. return this;
  520. };
  521. /**
  522. * Adds validator(s) for this document path.
  523. *
  524. * Validators always receive the value to validate as their first argument and
  525. * must return `Boolean`. Returning `false` or throwing an error means
  526. * validation failed.
  527. *
  528. * The error message argument is optional. If not passed, the [default generic error message template](#error_messages_MongooseError-messages) will be used.
  529. *
  530. * ####Examples:
  531. *
  532. * // make sure every value is equal to "something"
  533. * function validator (val) {
  534. * return val == 'something';
  535. * }
  536. * new Schema({ name: { type: String, validate: validator }});
  537. *
  538. * // with a custom error message
  539. *
  540. * var custom = [validator, 'Uh oh, {PATH} does not equal "something".']
  541. * new Schema({ name: { type: String, validate: custom }});
  542. *
  543. * // adding many validators at a time
  544. *
  545. * var many = [
  546. * { validator: validator, msg: 'uh oh' }
  547. * , { validator: anotherValidator, msg: 'failed' }
  548. * ]
  549. * new Schema({ name: { type: String, validate: many }});
  550. *
  551. * // or utilizing SchemaType methods directly:
  552. *
  553. * var schema = new Schema({ name: 'string' });
  554. * schema.path('name').validate(validator, 'validation of `{PATH}` failed with value `{VALUE}`');
  555. *
  556. * ####Error message templates:
  557. *
  558. * From the examples above, you may have noticed that error messages support
  559. * basic templating. There are a few other template keywords besides `{PATH}`
  560. * and `{VALUE}` too. To find out more, details are available
  561. * [here](#error_messages_MongooseError.messages).
  562. *
  563. * If Mongoose's built-in error message templating isn't enough, Mongoose
  564. * supports setting the `message` property to a function.
  565. *
  566. * schema.path('name').validate({
  567. * validator: function() { return v.length > 5; },
  568. * // `errors['name']` will be "name must have length 5, got 'foo'"
  569. * message: function(props) {
  570. * return `${props.path} must have length 5, got '${props.value}'`;
  571. * }
  572. * });
  573. *
  574. * To bypass Mongoose's error messages and just copy the error message that
  575. * the validator throws, do this:
  576. *
  577. * schema.path('name').validate({
  578. * validator: function() { throw new Error('Oops!'); },
  579. * // `errors['name']` will be "Oops!"
  580. * message: function(props) { return props.reason.message; }
  581. * });
  582. *
  583. * ####Asynchronous validation:
  584. *
  585. * Mongoose supports validators that return a promise. A validator that returns
  586. * a promise is called an _async validator_. Async validators run in
  587. * parallel, and `validate()` will wait until all async validators have settled.
  588. *
  589. * schema.path('name').validate({
  590. * validator: function (value) {
  591. * return new Promise(function (resolve, reject) {
  592. * resolve(false); // validation failed
  593. * });
  594. * }
  595. * });
  596. *
  597. * You might use asynchronous validators to retreive other documents from the database to validate against or to meet other I/O bound validation needs.
  598. *
  599. * Validation occurs `pre('save')` or whenever you manually execute [document#validate](#document_Document-validate).
  600. *
  601. * If validation fails during `pre('save')` and no callback was passed to receive the error, an `error` event will be emitted on your Models associated db [connection](#connection_Connection), passing the validation error object along.
  602. *
  603. * var conn = mongoose.createConnection(..);
  604. * conn.on('error', handleError);
  605. *
  606. * var Product = conn.model('Product', yourSchema);
  607. * var dvd = new Product(..);
  608. * dvd.save(); // emits error on the `conn` above
  609. *
  610. * If you want to handle these errors at the Model level, add an `error`
  611. * listener to your Model as shown below.
  612. *
  613. * // registering an error listener on the Model lets us handle errors more locally
  614. * Product.on('error', handleError);
  615. *
  616. * @param {RegExp|Function|Object} obj validator function, or hash describing options
  617. * @param {Function} [obj.validator] validator function. If the validator function returns `undefined` or a truthy value, validation succeeds. If it returns falsy (except `undefined`) or throws an error, validation fails.
  618. * @param {String|Function} [obj.message] optional error message. If function, should return the error message as a string
  619. * @param {Boolean} [obj.propsParameter=false] If true, Mongoose will pass the validator properties object (with the `validator` function, `message`, etc.) as the 2nd arg to the validator function. This is disabled by default because many validators [rely on positional args](https://github.com/chriso/validator.js#validators), so turning this on may cause unpredictable behavior in external validators.
  620. * @param {String|Function} [errorMsg] optional error message. If function, should return the error message as a string
  621. * @param {String} [type] optional validator type
  622. * @return {SchemaType} this
  623. * @api public
  624. */
  625. SchemaType.prototype.validate = function(obj, message, type) {
  626. if (typeof obj === 'function' || obj && utils.getFunctionName(obj.constructor) === 'RegExp') {
  627. let properties;
  628. if (message instanceof Object && !type) {
  629. properties = utils.clone(message);
  630. if (!properties.message) {
  631. properties.message = properties.msg;
  632. }
  633. properties.validator = obj;
  634. properties.type = properties.type || 'user defined';
  635. } else {
  636. if (!message) {
  637. message = MongooseError.messages.general.default;
  638. }
  639. if (!type) {
  640. type = 'user defined';
  641. }
  642. properties = {message: message, type: type, validator: obj};
  643. }
  644. if (properties.isAsync) {
  645. handleIsAsync();
  646. }
  647. this.validators.push(properties);
  648. return this;
  649. }
  650. let i;
  651. let length;
  652. let arg;
  653. for (i = 0, length = arguments.length; i < length; i++) {
  654. arg = arguments[i];
  655. if (!utils.isPOJO(arg)) {
  656. const msg = 'Invalid validator. Received (' + typeof arg + ') '
  657. + arg
  658. + '. See http://mongoosejs.com/docs/api.html#schematype_SchemaType-validate';
  659. throw new Error(msg);
  660. }
  661. this.validate(arg.validator, arg);
  662. }
  663. return this;
  664. };
  665. /*!
  666. * ignore
  667. */
  668. const handleIsAsync = util.deprecate(function handleIsAsync() {},
  669. 'Mongoose: the `isAsync` option for custom validators is deprecated. Make ' +
  670. 'your async validators return a promise instead: ' +
  671. 'https://mongoosejs.com/docs/validation.html#async-custom-validators');
  672. /**
  673. * Adds a required validator to this SchemaType. The validator gets added
  674. * to the front of this SchemaType's validators array using `unshift()`.
  675. *
  676. * ####Example:
  677. *
  678. * var s = new Schema({ born: { type: Date, required: true })
  679. *
  680. * // or with custom error message
  681. *
  682. * var s = new Schema({ born: { type: Date, required: '{PATH} is required!' })
  683. *
  684. * // or with a function
  685. *
  686. * var s = new Schema({
  687. * userId: ObjectId,
  688. * username: {
  689. * type: String,
  690. * required: function() { return this.userId != null; }
  691. * }
  692. * })
  693. *
  694. * // or with a function and a custom message
  695. * var s = new Schema({
  696. * userId: ObjectId,
  697. * username: {
  698. * type: String,
  699. * required: [
  700. * function() { return this.userId != null; },
  701. * 'username is required if id is specified'
  702. * ]
  703. * }
  704. * })
  705. *
  706. * // or through the path API
  707. *
  708. * Schema.path('name').required(true);
  709. *
  710. * // with custom error messaging
  711. *
  712. * Schema.path('name').required(true, 'grrr :( ');
  713. *
  714. * // or make a path conditionally required based on a function
  715. * var isOver18 = function() { return this.age >= 18; };
  716. * Schema.path('voterRegistrationId').required(isOver18);
  717. *
  718. * The required validator uses the SchemaType's `checkRequired` function to
  719. * determine whether a given value satisfies the required validator. By default,
  720. * a value satisfies the required validator if `val != null` (that is, if
  721. * the value is not null nor undefined). However, most built-in mongoose schema
  722. * types override the default `checkRequired` function:
  723. *
  724. * @param {Boolean|Function|Object} required enable/disable the validator, or function that returns required boolean, or options object
  725. * @param {Boolean|Function} [options.isRequired] enable/disable the validator, or function that returns required boolean
  726. * @param {Function} [options.ErrorConstructor] custom error constructor. The constructor receives 1 parameter, an object containing the validator properties.
  727. * @param {String} [message] optional custom error message
  728. * @return {SchemaType} this
  729. * @see Customized Error Messages #error_messages_MongooseError-messages
  730. * @see SchemaArray#checkRequired #schema_array_SchemaArray.checkRequired
  731. * @see SchemaBoolean#checkRequired #schema_boolean_SchemaBoolean-checkRequired
  732. * @see SchemaBuffer#checkRequired #schema_buffer_SchemaBuffer.schemaName
  733. * @see SchemaNumber#checkRequired #schema_number_SchemaNumber-min
  734. * @see SchemaObjectId#checkRequired #schema_objectid_ObjectId-auto
  735. * @see SchemaString#checkRequired #schema_string_SchemaString-checkRequired
  736. * @api public
  737. */
  738. SchemaType.prototype.required = function(required, message) {
  739. let customOptions = {};
  740. if (typeof required === 'object') {
  741. customOptions = required;
  742. message = customOptions.message || message;
  743. required = required.isRequired;
  744. }
  745. if (required === false) {
  746. this.validators = this.validators.filter(function(v) {
  747. return v.validator !== this.requiredValidator;
  748. }, this);
  749. this.isRequired = false;
  750. delete this.originalRequiredValue;
  751. return this;
  752. }
  753. const _this = this;
  754. this.isRequired = true;
  755. this.requiredValidator = function(v) {
  756. const cachedRequired = get(this, '$__.cachedRequired');
  757. // no validation when this path wasn't selected in the query.
  758. if (cachedRequired != null && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
  759. return true;
  760. }
  761. // `$cachedRequired` gets set in `_evaluateRequiredFunctions()` so we
  762. // don't call required functions multiple times in one validate call
  763. // See gh-6801
  764. if (cachedRequired != null && _this.path in cachedRequired) {
  765. const res = cachedRequired[_this.path] ?
  766. _this.checkRequired(v, this) :
  767. true;
  768. delete cachedRequired[_this.path];
  769. return res;
  770. } else if (typeof required === 'function') {
  771. return required.apply(this) ? _this.checkRequired(v, this) : true;
  772. }
  773. return _this.checkRequired(v, this);
  774. };
  775. this.originalRequiredValue = required;
  776. if (typeof required === 'string') {
  777. message = required;
  778. required = undefined;
  779. }
  780. const msg = message || MongooseError.messages.general.required;
  781. this.validators.unshift(Object.assign({}, customOptions, {
  782. validator: this.requiredValidator,
  783. message: msg,
  784. type: 'required'
  785. }));
  786. return this;
  787. };
  788. /**
  789. * Set the model that this path refers to. This is the option that [populate](https://mongoosejs.com/docs/populate.html)
  790. * looks at to determine the foreign collection it should query.
  791. *
  792. * ####Example:
  793. * const userSchema = new Schema({ name: String });
  794. * const User = mongoose.model('User', userSchema);
  795. *
  796. * const postSchema = new Schema({ user: mongoose.ObjectId });
  797. * postSchema.path('user').ref('User'); // By model name
  798. * postSchema.path('user').ref(User); // Can pass the model as well
  799. *
  800. * // Or you can just declare the `ref` inline in your schema
  801. * const postSchema2 = new Schema({
  802. * user: { type: mongoose.ObjectId, ref: User }
  803. * });
  804. *
  805. * @param {String|Model|Function} ref either a model name, a [Model](https://mongoosejs.com/docs/models.html), or a function that returns a model name or model.
  806. * @return {SchemaType} this
  807. * @api public
  808. */
  809. SchemaType.prototype.ref = function(ref) {
  810. this.options.ref = ref;
  811. return this;
  812. };
  813. /**
  814. * Gets the default value
  815. *
  816. * @param {Object} scope the scope which callback are executed
  817. * @param {Boolean} init
  818. * @api private
  819. */
  820. SchemaType.prototype.getDefault = function(scope, init) {
  821. let ret = typeof this.defaultValue === 'function'
  822. ? this.defaultValue.call(scope)
  823. : this.defaultValue;
  824. if (ret !== null && ret !== undefined) {
  825. if (typeof ret === 'object' && (!this.options || !this.options.shared)) {
  826. ret = utils.clone(ret);
  827. }
  828. const casted = this.cast(ret, scope, init);
  829. if (casted && casted.$isSingleNested) {
  830. casted.$parent = scope;
  831. }
  832. return casted;
  833. }
  834. return ret;
  835. };
  836. /*!
  837. * Applies setters without casting
  838. *
  839. * @api private
  840. */
  841. SchemaType.prototype._applySetters = function(value, scope, init, priorVal) {
  842. let v = value;
  843. const setters = this.setters;
  844. let len = setters.length;
  845. const caster = this.caster;
  846. while (len--) {
  847. v = setters[len].call(scope, v, this);
  848. }
  849. if (Array.isArray(v) && caster && caster.setters) {
  850. const newVal = [];
  851. for (let i = 0; i < v.length; i++) {
  852. newVal.push(caster.applySetters(v[i], scope, init, priorVal));
  853. }
  854. v = newVal;
  855. }
  856. return v;
  857. };
  858. /**
  859. * Applies setters
  860. *
  861. * @param {Object} value
  862. * @param {Object} scope
  863. * @param {Boolean} init
  864. * @api private
  865. */
  866. SchemaType.prototype.applySetters = function(value, scope, init, priorVal, options) {
  867. let v = this._applySetters(value, scope, init, priorVal, options);
  868. if (v == null) {
  869. return v;
  870. }
  871. // do not cast until all setters are applied #665
  872. v = this.cast(v, scope, init, priorVal, options);
  873. return v;
  874. };
  875. /**
  876. * Applies getters to a value
  877. *
  878. * @param {Object} value
  879. * @param {Object} scope
  880. * @api private
  881. */
  882. SchemaType.prototype.applyGetters = function(value, scope) {
  883. let v = value;
  884. const getters = this.getters;
  885. const len = getters.length;
  886. if (len === 0) {
  887. return v;
  888. }
  889. for (let i = 0; i < len; ++i) {
  890. v = getters[i].call(scope, v, this);
  891. }
  892. return v;
  893. };
  894. /**
  895. * Sets default `select()` behavior for this path.
  896. *
  897. * Set to `true` if this path should always be included in the results, `false` if it should be excluded by default. This setting can be overridden at the query level.
  898. *
  899. * ####Example:
  900. *
  901. * T = db.model('T', new Schema({ x: { type: String, select: true }}));
  902. * T.find(..); // field x will always be selected ..
  903. * // .. unless overridden;
  904. * T.find().select('-x').exec(callback);
  905. *
  906. * @param {Boolean} val
  907. * @return {SchemaType} this
  908. * @api public
  909. */
  910. SchemaType.prototype.select = function select(val) {
  911. this.selected = !!val;
  912. return this;
  913. };
  914. /**
  915. * Performs a validation of `value` using the validators declared for this SchemaType.
  916. *
  917. * @param {any} value
  918. * @param {Function} callback
  919. * @param {Object} scope
  920. * @api private
  921. */
  922. SchemaType.prototype.doValidate = function(value, fn, scope, options) {
  923. let err = false;
  924. const path = this.path;
  925. // Avoid non-object `validators`
  926. const validators = this.validators.
  927. filter(v => v != null && typeof v === 'object');
  928. let count = validators.length;
  929. if (!count) {
  930. return fn(null);
  931. }
  932. const validate = function(ok, validatorProperties) {
  933. if (err) {
  934. return;
  935. }
  936. if (ok === undefined || ok) {
  937. if (--count <= 0) {
  938. immediate(function() {
  939. fn(null);
  940. });
  941. }
  942. } else {
  943. const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError;
  944. err = new ErrorConstructor(validatorProperties);
  945. err[validatorErrorSymbol] = true;
  946. immediate(function() {
  947. fn(err);
  948. });
  949. }
  950. };
  951. const _this = this;
  952. validators.forEach(function(v) {
  953. if (err) {
  954. return;
  955. }
  956. const validator = v.validator;
  957. let ok;
  958. const validatorProperties = utils.clone(v);
  959. validatorProperties.path = options && options.path ? options.path : path;
  960. validatorProperties.value = value;
  961. if (validator instanceof RegExp) {
  962. validate(validator.test(value), validatorProperties);
  963. } else if (typeof validator === 'function') {
  964. if (value === undefined && validator !== _this.requiredValidator) {
  965. validate(true, validatorProperties);
  966. return;
  967. }
  968. if (validatorProperties.isAsync) {
  969. asyncValidate(validator, scope, value, validatorProperties, validate);
  970. } else {
  971. try {
  972. if (validatorProperties.propsParameter) {
  973. ok = validator.call(scope, value, validatorProperties);
  974. } else {
  975. ok = validator.call(scope, value);
  976. }
  977. } catch (error) {
  978. ok = false;
  979. validatorProperties.reason = error;
  980. if (error.message) {
  981. validatorProperties.message = error.message;
  982. }
  983. }
  984. if (ok != null && typeof ok.then === 'function') {
  985. ok.then(
  986. function(ok) { validate(ok, validatorProperties); },
  987. function(error) {
  988. validatorProperties.reason = error;
  989. validatorProperties.message = error.message;
  990. ok = false;
  991. validate(ok, validatorProperties);
  992. });
  993. } else {
  994. validate(ok, validatorProperties);
  995. }
  996. }
  997. }
  998. });
  999. };
  1000. /*!
  1001. * Handle async validators
  1002. */
  1003. function asyncValidate(validator, scope, value, props, cb) {
  1004. let called = false;
  1005. const returnVal = validator.call(scope, value, function(ok, customMsg) {
  1006. if (called) {
  1007. return;
  1008. }
  1009. called = true;
  1010. if (customMsg) {
  1011. props.message = customMsg;
  1012. }
  1013. cb(ok, props);
  1014. });
  1015. if (typeof returnVal === 'boolean') {
  1016. called = true;
  1017. cb(returnVal, props);
  1018. } else if (returnVal && typeof returnVal.then === 'function') {
  1019. // Promise
  1020. returnVal.then(
  1021. function(ok) {
  1022. if (called) {
  1023. return;
  1024. }
  1025. called = true;
  1026. cb(ok, props);
  1027. },
  1028. function(error) {
  1029. if (called) {
  1030. return;
  1031. }
  1032. called = true;
  1033. props.reason = error;
  1034. props.message = error.message;
  1035. cb(false, props);
  1036. });
  1037. }
  1038. }
  1039. /**
  1040. * Performs a validation of `value` using the validators declared for this SchemaType.
  1041. *
  1042. * ####Note:
  1043. *
  1044. * This method ignores the asynchronous validators.
  1045. *
  1046. * @param {any} value
  1047. * @param {Object} scope
  1048. * @return {MongooseError|undefined}
  1049. * @api private
  1050. */
  1051. SchemaType.prototype.doValidateSync = function(value, scope, options) {
  1052. let err = null;
  1053. const path = this.path;
  1054. const count = this.validators.length;
  1055. if (!count) {
  1056. return null;
  1057. }
  1058. const validate = function(ok, validatorProperties) {
  1059. if (err) {
  1060. return;
  1061. }
  1062. if (ok !== undefined && !ok) {
  1063. const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError;
  1064. err = new ErrorConstructor(validatorProperties);
  1065. err[validatorErrorSymbol] = true;
  1066. }
  1067. };
  1068. let validators = this.validators;
  1069. if (value === void 0) {
  1070. if (this.validators.length > 0 && this.validators[0].type === 'required') {
  1071. validators = [this.validators[0]];
  1072. } else {
  1073. return null;
  1074. }
  1075. }
  1076. validators.forEach(function(v) {
  1077. if (err) {
  1078. return;
  1079. }
  1080. if (v == null || typeof v !== 'object') {
  1081. return;
  1082. }
  1083. const validator = v.validator;
  1084. const validatorProperties = utils.clone(v);
  1085. validatorProperties.path = options && options.path ? options.path : path;
  1086. validatorProperties.value = value;
  1087. let ok;
  1088. // Skip any explicit async validators. Validators that return a promise
  1089. // will still run, but won't trigger any errors.
  1090. if (validator.isAsync) {
  1091. return;
  1092. }
  1093. if (validator instanceof RegExp) {
  1094. validate(validator.test(value), validatorProperties);
  1095. } else if (typeof validator === 'function') {
  1096. try {
  1097. if (validatorProperties.propsParameter) {
  1098. ok = validator.call(scope, value, validatorProperties);
  1099. } else {
  1100. ok = validator.call(scope, value);
  1101. }
  1102. } catch (error) {
  1103. ok = false;
  1104. validatorProperties.reason = error;
  1105. }
  1106. // Skip any validators that return a promise, we can't handle those
  1107. // synchronously
  1108. if (ok != null && typeof ok.then === 'function') {
  1109. return;
  1110. }
  1111. validate(ok, validatorProperties);
  1112. }
  1113. });
  1114. return err;
  1115. };
  1116. /**
  1117. * Determines if value is a valid Reference.
  1118. *
  1119. * @param {SchemaType} self
  1120. * @param {Object} value
  1121. * @param {Document} doc
  1122. * @param {Boolean} init
  1123. * @return {Boolean}
  1124. * @api private
  1125. */
  1126. SchemaType._isRef = function(self, value, doc, init) {
  1127. // fast path
  1128. let ref = init && self.options && (self.options.ref || self.options.refPath);
  1129. if (!ref && doc && doc.$__ != null) {
  1130. // checks for
  1131. // - this populated with adhoc model and no ref was set in schema OR
  1132. // - setting / pushing values after population
  1133. const path = doc.$__fullPath(self.path);
  1134. const owner = doc.ownerDocument ? doc.ownerDocument() : doc;
  1135. ref = owner.populated(path);
  1136. }
  1137. if (ref) {
  1138. if (value == null) {
  1139. return true;
  1140. }
  1141. if (!Buffer.isBuffer(value) && // buffers are objects too
  1142. value._bsontype !== 'Binary' // raw binary value from the db
  1143. && utils.isObject(value) // might have deselected _id in population query
  1144. ) {
  1145. return true;
  1146. }
  1147. }
  1148. return false;
  1149. };
  1150. /*!
  1151. * ignore
  1152. */
  1153. function handleSingle(val) {
  1154. return this.castForQuery(val);
  1155. }
  1156. /*!
  1157. * ignore
  1158. */
  1159. function handleArray(val) {
  1160. const _this = this;
  1161. if (!Array.isArray(val)) {
  1162. return [this.castForQuery(val)];
  1163. }
  1164. return val.map(function(m) {
  1165. return _this.castForQuery(m);
  1166. });
  1167. }
  1168. /*!
  1169. * Just like handleArray, except also allows `[]` because surprisingly
  1170. * `$in: [1, []]` works fine
  1171. */
  1172. function handle$in(val) {
  1173. const _this = this;
  1174. if (!Array.isArray(val)) {
  1175. return [this.castForQuery(val)];
  1176. }
  1177. return val.map(function(m) {
  1178. if (Array.isArray(m) && m.length === 0) {
  1179. return m;
  1180. }
  1181. return _this.castForQuery(m);
  1182. });
  1183. }
  1184. /*!
  1185. * ignore
  1186. */
  1187. SchemaType.prototype.$conditionalHandlers = {
  1188. $all: handleArray,
  1189. $eq: handleSingle,
  1190. $in: handle$in,
  1191. $ne: handleSingle,
  1192. $nin: handle$in,
  1193. $exists: $exists,
  1194. $type: $type
  1195. };
  1196. /*!
  1197. * Wraps `castForQuery` to handle context
  1198. */
  1199. SchemaType.prototype.castForQueryWrapper = function(params) {
  1200. this.$$context = params.context;
  1201. if ('$conditional' in params) {
  1202. return this.castForQuery(params.$conditional, params.val);
  1203. }
  1204. if (params.$skipQueryCastForUpdate) {
  1205. return this._castForQuery(params.val);
  1206. }
  1207. return this.castForQuery(params.val);
  1208. };
  1209. /**
  1210. * Cast the given value with the given optional query operator.
  1211. *
  1212. * @param {String} [$conditional] query operator, like `$eq` or `$in`
  1213. * @param {any} val
  1214. * @api private
  1215. */
  1216. SchemaType.prototype.castForQuery = function($conditional, val) {
  1217. let handler;
  1218. if (arguments.length === 2) {
  1219. handler = this.$conditionalHandlers[$conditional];
  1220. if (!handler) {
  1221. throw new Error('Can\'t use ' + $conditional);
  1222. }
  1223. return handler.call(this, val);
  1224. }
  1225. val = $conditional;
  1226. return this._castForQuery(val);
  1227. };
  1228. /*!
  1229. * Internal switch for runSetters
  1230. *
  1231. * @api private
  1232. */
  1233. SchemaType.prototype._castForQuery = function(val) {
  1234. return this.applySetters(val, this.$$context);
  1235. };
  1236. /**
  1237. * Override the function the required validator uses to check whether a value
  1238. * passes the `required` check. Override this on the individual SchemaType.
  1239. *
  1240. * ####Example:
  1241. *
  1242. * // Use this to allow empty strings to pass the `required` validator
  1243. * mongoose.Schema.Types.String.checkRequired(v => typeof v === 'string');
  1244. *
  1245. * @param {Function} fn
  1246. * @return {Function}
  1247. * @static
  1248. * @receiver SchemaType
  1249. * @function checkRequired
  1250. * @api public
  1251. */
  1252. SchemaType.checkRequired = function(fn) {
  1253. if (arguments.length > 0) {
  1254. this._checkRequired = fn;
  1255. }
  1256. return this._checkRequired;
  1257. };
  1258. /**
  1259. * Default check for if this path satisfies the `required` validator.
  1260. *
  1261. * @param {any} val
  1262. * @api private
  1263. */
  1264. SchemaType.prototype.checkRequired = function(val) {
  1265. return val != null;
  1266. };
  1267. /*!
  1268. * ignore
  1269. */
  1270. SchemaType.prototype.clone = function() {
  1271. const options = Object.assign({}, this.options);
  1272. const schematype = new this.constructor(this.path, options, this.instance);
  1273. schematype.validators = this.validators.slice();
  1274. return schematype;
  1275. };
  1276. /*!
  1277. * Module exports.
  1278. */
  1279. module.exports = exports = SchemaType;
  1280. exports.CastError = CastError;
  1281. exports.ValidatorError = ValidatorError;