thoughts/node_modules/mongoose/lib/schematype.js
node_modules/mongoose/lib/schematype.jsRaw
1/*!
2 * Module dependencies.
3 */
4
5var utils = require('./utils');
6var MongooseError = require('./error');
7var CastError = MongooseError.CastError;
8var ValidatorError = MongooseError.ValidatorError;
9
10/**
11 * SchemaType constructor
12 *
13 * @param {String} path
14 * @param {Object} [options]
15 * @param {String} [instance]
16 * @api public
17 */
18
19function SchemaType(path, options, instance) {
20 this.path = path;
21 this.instance = instance;
22 this.validators = [];
23 this.setters = [];
24 this.getters = [];
25 this.options = options;
26 this._index = null;
27 this.selected;
28
29 for (var i in options) {
30 if (this[i] && typeof this[i] === 'function') {
31 // { unique: true, index: true }
32 if (i === 'index' && this._index) {
33 continue;
34 }
35
36 var opts = Array.isArray(options[i])
37 ? options[i]
38 : [options[i]];
39
40 this[i].apply(this, opts);
41 }
42 }
43}
44
45/**
46 * Sets a default value for this SchemaType.
47 *
48 * ####Example:
49 *
50 * var schema = new Schema({ n: { type: Number, default: 10 })
51 * var M = db.model('M', schema)
52 * var m = new M;
53 * console.log(m.n) // 10
54 *
55 * 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.
56 *
57 * ####Example:
58 *
59 * // values are cast:
60 * var schema = new Schema({ aNumber: { type: Number, default: 4.815162342 }})
61 * var M = db.model('M', schema)
62 * var m = new M;
63 * console.log(m.aNumber) // 4.815162342
64 *
65 * // default unique objects for Mixed types:
66 * var schema = new Schema({ mixed: Schema.Types.Mixed });
67 * schema.path('mixed').default(function () {
68 * return {};
69 * });
70 *
71 * // if we don't use a function to return object literals for Mixed defaults,
72 * // each document will receive a reference to the same object literal creating
73 * // a "shared" object instance:
74 * var schema = new Schema({ mixed: Schema.Types.Mixed });
75 * schema.path('mixed').default({});
76 * var M = db.model('M', schema);
77 * var m1 = new M;
78 * m1.mixed.added = 1;
79 * console.log(m1.mixed); // { added: 1 }
80 * var m2 = new M;
81 * console.log(m2.mixed); // { added: 1 }
82 *
83 * @param {Function|any} val the default value
84 * @return {defaultValue}
85 * @api public
86 */
87
88SchemaType.prototype.default = function(val) {
89 if (arguments.length === 1) {
90 if (val === void 0) {
91 this.defaultValue = void 0;
92 return void 0;
93 }
94 this.defaultValue = val;
95 return this.defaultValue;
96 } else if (arguments.length > 1) {
97 this.defaultValue = utils.args(arguments);
98 }
99 return this.defaultValue;
100};
101
102/**
103 * Declares the index options for this schematype.
104 *
105 * ####Example:
106 *
107 * var s = new Schema({ name: { type: String, index: true })
108 * var s = new Schema({ loc: { type: [Number], index: 'hashed' })
109 * var s = new Schema({ loc: { type: [Number], index: '2d', sparse: true })
110 * var s = new Schema({ loc: { type: [Number], index: { type: '2dsphere', sparse: true }})
111 * var s = new Schema({ date: { type: Date, index: { unique: true, expires: '1d' }})
112 * Schema.path('my.path').index(true);
113 * Schema.path('my.date').index({ expires: 60 });
114 * Schema.path('my.path').index({ unique: true, sparse: true });
115 *
116 * ####NOTE:
117 *
118 * _Indexes are created in the background by default. Specify `background: false` to override._
119 *
120 * [Direction doesn't matter for single key indexes](http://www.mongodb.org/display/DOCS/Indexes#Indexes-CompoundKeysIndexes)
121 *
122 * @param {Object|Boolean|String} options
123 * @return {SchemaType} this
124 * @api public
125 */
126
127SchemaType.prototype.index = function(options) {
128 this._index = options;
129 utils.expires(this._index);
130 return this;
131};
132
133/**
134 * Declares an unique index.
135 *
136 * ####Example:
137 *
138 * var s = new Schema({ name: { type: String, unique: true }});
139 * Schema.path('name').index({ unique: true });
140 *
141 * _NOTE: violating the constraint returns an `E11000` error from MongoDB when saving, not a Mongoose validation error._
142 *
143 * @param {Boolean} bool
144 * @return {SchemaType} this
145 * @api public
146 */
147
148SchemaType.prototype.unique = function(bool) {
149 if (this._index === false) {
150 if (!bool) {
151 return;
152 }
153 throw new Error('Path "' + this.path + '" may not have `index` set to ' +
154 'false and `unique` set to true');
155 }
156 if (this._index == null || this._index === true) {
157 this._index = {};
158 } else if (typeof this._index === 'string') {
159 this._index = {type: this._index};
160 }
161
162 this._index.unique = bool;
163 return this;
164};
165
166/**
167 * Declares a full text index.
168 *
169 * ###Example:
170 *
171 * var s = new Schema({name : {type: String, text : true })
172 * Schema.path('name').index({text : true});
173 * @param {Boolean} bool
174 * @return {SchemaType} this
175 * @api public
176 */
177
178SchemaType.prototype.text = function(bool) {
179 if (this._index === null || this._index === undefined ||
180 typeof this._index === 'boolean') {
181 this._index = {};
182 } else if (typeof this._index === 'string') {
183 this._index = {type: this._index};
184 }
185
186 this._index.text = bool;
187 return this;
188};
189
190/**
191 * Declares a sparse index.
192 *
193 * ####Example:
194 *
195 * var s = new Schema({ name: { type: String, sparse: true })
196 * Schema.path('name').index({ sparse: true });
197 *
198 * @param {Boolean} bool
199 * @return {SchemaType} this
200 * @api public
201 */
202
203SchemaType.prototype.sparse = function(bool) {
204 if (this._index === null || this._index === undefined ||
205 typeof this._index === 'boolean') {
206 this._index = {};
207 } else if (typeof this._index === 'string') {
208 this._index = {type: this._index};
209 }
210
211 this._index.sparse = bool;
212 return this;
213};
214
215/**
216 * Adds a setter to this schematype.
217 *
218 * ####Example:
219 *
220 * function capitalize (val) {
221 * if (typeof val !== 'string') val = '';
222 * return val.charAt(0).toUpperCase() + val.substring(1);
223 * }
224 *
225 * // defining within the schema
226 * var s = new Schema({ name: { type: String, set: capitalize }})
227 *
228 * // or by retreiving its SchemaType
229 * var s = new Schema({ name: String })
230 * s.path('name').set(capitalize)
231 *
232 * Setters allow you to transform the data before it gets to the raw mongodb document and is set as a value on an actual key.
233 *
234 * Suppose you are implementing user registration for a website. Users provide an email and password, which gets saved to mongodb. The email is a string that you will want to normalize to lower case, in order to avoid one email 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.
235 *
236 * You can set up email lower case normalization easily via a Mongoose setter.
237 *
238 * function toLower (v) {
239 * return v.toLowerCase();
240 * }
241 *
242 * var UserSchema = new Schema({
243 * email: { type: String, set: toLower }
244 * })
245 *
246 * var User = db.model('User', UserSchema)
247 *
248 * var user = new User({email: 'AVENUE@Q.COM'})
249 * console.log(user.email); // 'avenue@q.com'
250 *
251 * // or
252 * var user = new User
253 * user.email = 'Avenue@Q.com'
254 * console.log(user.email) // 'avenue@q.com'
255 *
256 * As you can see above, setters allow you to transform the data before it gets to the raw mongodb document and is set as a value on an actual key.
257 *
258 * _NOTE: we could have also just used the built-in `lowercase: true` SchemaType option instead of defining our own function._
259 *
260 * new Schema({ email: { type: String, lowercase: true }})
261 *
262 * 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.
263 *
264 * function inspector (val, schematype) {
265 * if (schematype.options.required) {
266 * return schematype.path + ' is required';
267 * } else {
268 * return val;
269 * }
270 * }
271 *
272 * var VirusSchema = new Schema({
273 * name: { type: String, required: true, set: inspector },
274 * taxonomy: { type: String, set: inspector }
275 * })
276 *
277 * var Virus = db.model('Virus', VirusSchema);
278 * var v = new Virus({ name: 'Parvoviridae', taxonomy: 'Parvovirinae' });
279 *
280 * console.log(v.name); // name is required
281 * console.log(v.taxonomy); // Parvovirinae
282 *
283 * @param {Function} fn
284 * @return {SchemaType} this
285 * @api public
286 */
287
288SchemaType.prototype.set = function(fn) {
289 if (typeof fn !== 'function') {
290 throw new TypeError('A setter must be a function.');
291 }
292 this.setters.push(fn);
293 return this;
294};
295
296/**
297 * Adds a getter to this schematype.
298 *
299 * ####Example:
300 *
301 * function dob (val) {
302 * if (!val) return val;
303 * return (val.getMonth() + 1) + "/" + val.getDate() + "/" + val.getFullYear();
304 * }
305 *
306 * // defining within the schema
307 * var s = new Schema({ born: { type: Date, get: dob })
308 *
309 * // or by retreiving its SchemaType
310 * var s = new Schema({ born: Date })
311 * s.path('born').get(dob)
312 *
313 * Getters allow you to transform the representation of the data as it travels from the raw mongodb document to the value that you see.
314 *
315 * 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:
316 *
317 * function obfuscate (cc) {
318 * return '****-****-****-' + cc.slice(cc.length-4, cc.length);
319 * }
320 *
321 * var AccountSchema = new Schema({
322 * creditCardNumber: { type: String, get: obfuscate }
323 * });
324 *
325 * var Account = db.model('Account', AccountSchema);
326 *
327 * Account.findById(id, function (err, found) {
328 * console.log(found.creditCardNumber); // '****-****-****-1234'
329 * });
330 *
331 * 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.
332 *
333 * function inspector (val, schematype) {
334 * if (schematype.options.required) {
335 * return schematype.path + ' is required';
336 * } else {
337 * return schematype.path + ' is not';
338 * }
339 * }
340 *
341 * var VirusSchema = new Schema({
342 * name: { type: String, required: true, get: inspector },
343 * taxonomy: { type: String, get: inspector }
344 * })
345 *
346 * var Virus = db.model('Virus', VirusSchema);
347 *
348 * Virus.findById(id, function (err, virus) {
349 * console.log(virus.name); // name is required
350 * console.log(virus.taxonomy); // taxonomy is not
351 * })
352 *
353 * @param {Function} fn
354 * @return {SchemaType} this
355 * @api public
356 */
357
358SchemaType.prototype.get = function(fn) {
359 if (typeof fn !== 'function') {
360 throw new TypeError('A getter must be a function.');
361 }
362 this.getters.push(fn);
363 return this;
364};
365
366/**
367 * Adds validator(s) for this document path.
368 *
369 * Validators always receive the value to validate as their first argument and must return `Boolean`. Returning `false` means validation failed.
370 *
371 * The error message argument is optional. If not passed, the [default generic error message template](#error_messages_MongooseError-messages) will be used.
372 *
373 * ####Examples:
374 *
375 * // make sure every value is equal to "something"
376 * function validator (val) {
377 * return val == 'something';
378 * }
379 * new Schema({ name: { type: String, validate: validator }});
380 *
381 * // with a custom error message
382 *
383 * var custom = [validator, 'Uh oh, {PATH} does not equal "something".']
384 * new Schema({ name: { type: String, validate: custom }});
385 *
386 * // adding many validators at a time
387 *
388 * var many = [
389 * { validator: validator, msg: 'uh oh' }
390 * , { validator: anotherValidator, msg: 'failed' }
391 * ]
392 * new Schema({ name: { type: String, validate: many }});
393 *
394 * // or utilizing SchemaType methods directly:
395 *
396 * var schema = new Schema({ name: 'string' });
397 * schema.path('name').validate(validator, 'validation of `{PATH}` failed with value `{VALUE}`');
398 *
399 * ####Error message templates:
400 *
401 * From the examples above, you may have noticed that error messages support basic templating. There are a few other template keywords besides `{PATH}` and `{VALUE}` too. To find out more, details are available [here](#error_messages_MongooseError-messages)
402 *
403 * ####Asynchronous validation:
404 *
405 * Passing a validator function that receives two arguments tells mongoose that the validator is an asynchronous validator. The first argument passed to the validator function is the value being validated. The second argument is a callback function that must called when you finish validating the value and passed either `true` or `false` to communicate either success or failure respectively.
406 *
407 * schema.path('name').validate(function (value, respond) {
408 * doStuff(value, function () {
409 * ...
410 * respond(false); // validation failed
411 * })
412 * }, '{PATH} failed validation.');
413 *
414 * // or with dynamic message
415 *
416 * schema.path('name').validate(function (value, respond) {
417 * doStuff(value, function () {
418 * ...
419 * respond(false, 'this message gets to the validation error');
420 * });
421 * }, 'this message does not matter');
422 *
423 * You might use asynchronous validators to retreive other documents from the database to validate against or to meet other I/O bound validation needs.
424 *
425 * Validation occurs `pre('save')` or whenever you manually execute [document#validate](#document_Document-validate).
426 *
427 * 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.
428 *
429 * var conn = mongoose.createConnection(..);
430 * conn.on('error', handleError);
431 *
432 * var Product = conn.model('Product', yourSchema);
433 * var dvd = new Product(..);
434 * dvd.save(); // emits error on the `conn` above
435 *
436 * If you desire handling these errors at the Model level, attach an `error` listener to your Model and the event will instead be emitted there.
437 *
438 * // registering an error listener on the Model lets us handle errors more locally
439 * Product.on('error', handleError);
440 *
441 * @param {RegExp|Function|Object} obj validator
442 * @param {String} [errorMsg] optional error message
443 * @param {String} [type] optional validator type
444 * @return {SchemaType} this
445 * @api public
446 */
447
448SchemaType.prototype.validate = function(obj, message, type) {
449 if (typeof obj === 'function' || obj && utils.getFunctionName(obj.constructor) === 'RegExp') {
450 var properties;
451 if (message instanceof Object && !type) {
452 properties = utils.clone(message);
453 if (!properties.message) {
454 properties.message = properties.msg;
455 }
456 properties.validator = obj;
457 properties.type = properties.type || 'user defined';
458 } else {
459 if (!message) {
460 message = MongooseError.messages.general.default;
461 }
462 if (!type) {
463 type = 'user defined';
464 }
465 properties = {message: message, type: type, validator: obj};
466 }
467 this.validators.push(properties);
468 return this;
469 }
470
471 var i,
472 length,
473 arg;
474
475 for (i = 0, length = arguments.length; i < length; i++) {
476 arg = arguments[i];
477 if (!(arg && utils.getFunctionName(arg.constructor) === 'Object')) {
478 var msg = 'Invalid validator. Received (' + typeof arg + ') '
479 + arg
480 + '. See http://mongoosejs.com/docs/api.html#schematype_SchemaType-validate';
481
482 throw new Error(msg);
483 }
484 this.validate(arg.validator, arg);
485 }
486
487 return this;
488};
489
490/**
491 * Adds a required validator to this SchemaType. The validator gets added
492 * to the front of this SchemaType's validators array using `unshift()`.
493 *
494 * ####Example:
495 *
496 * var s = new Schema({ born: { type: Date, required: true })
497 *
498 * // or with custom error message
499 *
500 * var s = new Schema({ born: { type: Date, required: '{PATH} is required!' })
501 *
502 * // or through the path API
503 *
504 * Schema.path('name').required(true);
505 *
506 * // with custom error messaging
507 *
508 * Schema.path('name').required(true, 'grrr :( ');
509 *
510 * // or make a path conditionally required based on a function
511 * var isOver18 = function() { return this.age >= 18; };
512 * Schema.path('voterRegistrationId').required(isOver18);
513 *
514 * The required validator uses the SchemaType's `checkRequired` function to
515 * determine whether a given value satisfies the required validator. By default,
516 * a value satisfies the required validator if `val != null` (that is, if
517 * the value is not null nor undefined). However, most built-in mongoose schema
518 * types override the default `checkRequired` function:
519 *
520 * @param {Boolean} required enable/disable the validator
521 * @param {String} [message] optional custom error message
522 * @return {SchemaType} this
523 * @see Customized Error Messages #error_messages_MongooseError-messages
524 * @see SchemaArray#checkRequired #schema_array_SchemaArray.checkRequired
525 * @see SchemaBoolean#checkRequired #schema_boolean_SchemaBoolean-checkRequired
526 * @see SchemaBuffer#checkRequired #schema_buffer_SchemaBuffer.schemaName
527 * @see SchemaNumber#checkRequired #schema_number_SchemaNumber-min
528 * @see SchemaObjectId#checkRequired #schema_objectid_ObjectId-auto
529 * @see SchemaString#checkRequired #schema_string_SchemaString-checkRequired
530 * @api public
531 */
532
533SchemaType.prototype.required = function(required, message) {
534 if (required === false) {
535 this.validators = this.validators.filter(function(v) {
536 return v.validator !== this.requiredValidator;
537 }, this);
538
539 this.isRequired = false;
540 return this;
541 }
542
543 var _this = this;
544 this.isRequired = true;
545
546 this.requiredValidator = function(v) {
547 // in here, `this` refers to the validating document.
548 // no validation when this path wasn't selected in the query.
549 if ('isSelected' in this && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
550 return true;
551 }
552
553 return ((typeof required === 'function') && !required.apply(this)) ||
554 _this.checkRequired(v, this);
555 };
556 this.originalRequiredValue = required;
557
558 if (typeof required === 'string') {
559 message = required;
560 required = undefined;
561 }
562
563 var msg = message || MongooseError.messages.general.required;
564 this.validators.unshift({
565 validator: this.requiredValidator,
566 message: msg,
567 type: 'required'
568 });
569
570 return this;
571};
572
573/**
574 * Gets the default value
575 *
576 * @param {Object} scope the scope which callback are executed
577 * @param {Boolean} init
578 * @api private
579 */
580
581SchemaType.prototype.getDefault = function(scope, init) {
582 var ret = typeof this.defaultValue === 'function'
583 ? this.defaultValue.call(scope)
584 : this.defaultValue;
585
586 if (ret !== null && ret !== undefined) {
587 var casted = this.cast(ret, scope, init);
588 if (casted && casted.$isSingleNested) {
589 casted.$parent = scope;
590 }
591 return casted;
592 }
593 return ret;
594};
595
596/**
597 * Applies setters
598 *
599 * @param {Object} value
600 * @param {Object} scope
601 * @param {Boolean} init
602 * @api private
603 */
604
605SchemaType.prototype.applySetters = function(value, scope, init, priorVal, options) {
606 var v = value,
607 setters = this.setters,
608 len = setters.length,
609 caster = this.caster;
610
611 while (len--) {
612 v = setters[len].call(scope, v, this);
613 }
614
615 if (Array.isArray(v) && caster && caster.setters) {
616 var newVal = [];
617 for (var i = 0; i < v.length; i++) {
618 newVal.push(caster.applySetters(v[i], scope, init, priorVal));
619 }
620 v = newVal;
621 }
622
623 if (v === null || v === undefined) {
624 return v;
625 }
626
627 // do not cast until all setters are applied #665
628 v = this.cast(v, scope, init, priorVal, options);
629
630 return v;
631};
632
633/**
634 * Applies getters to a value
635 *
636 * @param {Object} value
637 * @param {Object} scope
638 * @api private
639 */
640
641SchemaType.prototype.applyGetters = function(value, scope) {
642 var v = value,
643 getters = this.getters,
644 len = getters.length;
645
646 if (!len) {
647 return v;
648 }
649
650 while (len--) {
651 v = getters[len].call(scope, v, this);
652 }
653
654 return v;
655};
656
657/**
658 * Sets default `select()` behavior for this path.
659 *
660 * 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.
661 *
662 * ####Example:
663 *
664 * T = db.model('T', new Schema({ x: { type: String, select: true }}));
665 * T.find(..); // field x will always be selected ..
666 * // .. unless overridden;
667 * T.find().select('-x').exec(callback);
668 *
669 * @param {Boolean} val
670 * @return {SchemaType} this
671 * @api public
672 */
673
674SchemaType.prototype.select = function select(val) {
675 this.selected = !!val;
676 return this;
677};
678
679/**
680 * Performs a validation of `value` using the validators declared for this SchemaType.
681 *
682 * @param {any} value
683 * @param {Function} callback
684 * @param {Object} scope
685 * @api private
686 */
687
688SchemaType.prototype.doValidate = function(value, fn, scope) {
689 var err = false,
690 path = this.path,
691 count = this.validators.length;
692
693 if (!count) {
694 return fn(null);
695 }
696
697 var validate = function(ok, validatorProperties) {
698 if (err) {
699 return;
700 }
701 if (ok === undefined || ok) {
702 --count || fn(null);
703 } else {
704 err = new ValidatorError(validatorProperties);
705 fn(err);
706 }
707 };
708
709 var _this = this;
710 this.validators.forEach(function(v) {
711 if (err) {
712 return;
713 }
714
715 var validator = v.validator;
716
717 var validatorProperties = utils.clone(v);
718 validatorProperties.path = path;
719 validatorProperties.value = value;
720
721 if (validator instanceof RegExp) {
722 validate(validator.test(value), validatorProperties);
723 } else if (typeof validator === 'function') {
724 if (value === undefined && !_this.isRequired) {
725 validate(true, validatorProperties);
726 return;
727 }
728 if (validator.length === 2) {
729 var returnVal = validator.call(scope, value, function(ok, customMsg) {
730 if (typeof returnVal === 'boolean') {
731 return;
732 }
733 if (customMsg) {
734 validatorProperties.message = customMsg;
735 }
736 validate(ok, validatorProperties);
737 });
738 if (typeof returnVal === 'boolean') {
739 validate(returnVal, validatorProperties);
740 }
741 } else {
742 validate(validator.call(scope, value), validatorProperties);
743 }
744 }
745 });
746};
747
748/**
749 * Performs a validation of `value` using the validators declared for this SchemaType.
750 *
751 * ####Note:
752 *
753 * This method ignores the asynchronous validators.
754 *
755 * @param {any} value
756 * @param {Object} scope
757 * @return {MongooseError|undefined}
758 * @api private
759 */
760
761SchemaType.prototype.doValidateSync = function(value, scope) {
762 var err = null,
763 path = this.path,
764 count = this.validators.length;
765
766 if (!count) {
767 return null;
768 }
769
770 var validate = function(ok, validatorProperties) {
771 if (err) {
772 return;
773 }
774 if (ok !== undefined && !ok) {
775 err = new ValidatorError(validatorProperties);
776 }
777 };
778
779 var _this = this;
780 if (value === undefined && !_this.isRequired) {
781 return null;
782 }
783
784 this.validators.forEach(function(v) {
785 if (err) {
786 return;
787 }
788
789 var validator = v.validator;
790 var validatorProperties = utils.clone(v);
791 validatorProperties.path = path;
792 validatorProperties.value = value;
793
794 if (validator instanceof RegExp) {
795 validate(validator.test(value), validatorProperties);
796 } else if (typeof validator === 'function') {
797 // if not async validators
798 if (validator.length !== 2) {
799 validate(validator.call(scope, value), validatorProperties);
800 }
801 }
802 });
803
804 return err;
805};
806
807/**
808 * Determines if value is a valid Reference.
809 *
810 * @param {SchemaType} self
811 * @param {Object} value
812 * @param {Document} doc
813 * @param {Boolean} init
814 * @return {Boolean}
815 * @api private
816 */
817
818SchemaType._isRef = function(self, value, doc, init) {
819 // fast path
820 var ref = init && self.options && self.options.ref;
821
822 if (!ref && doc && doc.$__fullPath) {
823 // checks for
824 // - this populated with adhoc model and no ref was set in schema OR
825 // - setting / pushing values after population
826 var path = doc.$__fullPath(self.path);
827 var owner = doc.ownerDocument ? doc.ownerDocument() : doc;
828 ref = owner.populated(path);
829 }
830
831 if (ref) {
832 if (value == null) {
833 return true;
834 }
835 if (!Buffer.isBuffer(value) && // buffers are objects too
836 value._bsontype !== 'Binary' // raw binary value from the db
837 && utils.isObject(value) // might have deselected _id in population query
838 ) {
839 return true;
840 }
841 }
842
843 return false;
844};
845
846/*!
847 * ignore
848 */
849
850function handleSingle(val) {
851 return this.castForQuery(val);
852}
853
854/*!
855 * ignore
856 */
857
858function handleArray(val) {
859 var _this = this;
860 if (!Array.isArray(val)) {
861 return [this.castForQuery(val)];
862 }
863 return val.map(function(m) {
864 return _this.castForQuery(m);
865 });
866}
867
868/*!
869 * ignore
870 */
871
872SchemaType.prototype.$conditionalHandlers = {
873 $all: handleArray,
874 $eq: handleSingle,
875 $in: handleArray,
876 $ne: handleSingle,
877 $nin: handleArray
878};
879
880/**
881 * Cast the given value with the given optional query operator.
882 *
883 * @param {String} [$conditional] query operator, like `$eq` or `$in`
884 * @param {any} val
885 * @api private
886 */
887
888SchemaType.prototype.castForQuery = function($conditional, val) {
889 var handler;
890 if (arguments.length === 2) {
891 handler = this.$conditionalHandlers[$conditional];
892 if (!handler) {
893 throw new Error('Can\'t use ' + $conditional);
894 }
895 return handler.call(this, val);
896 }
897 val = $conditional;
898 return this.cast(val);
899};
900
901/**
902 * Default check for if this path satisfies the `required` validator.
903 *
904 * @param {any} val
905 * @api private
906 */
907
908SchemaType.prototype.checkRequired = function(val) {
909 return val != null;
910};
911
912/*!
913 * Module exports.
914 */
915
916module.exports = exports = SchemaType;
917
918exports.CastError = CastError;
919
920exports.ValidatorError = ValidatorError;