|
|
/*! * Module dependencies. */
var SchemaType = require('../schematype') , CastError = SchemaType.CastError , NumberSchema = require('./number') , Types = { Boolean: require('./boolean') , Date: require('./date') , Number: require('./number') , String: require('./string') , ObjectId: require('./objectid') , Buffer: require('./buffer') } , MongooseArray = require('../types').Array , EmbeddedDoc = require('../types').Embedded , Mixed = require('./mixed') , Query = require('../query') , utils = require('../utils') , isMongooseObject = utils.isMongooseObject
/** * Array SchemaType constructor * * @param {String} key * @param {SchemaType} cast * @param {Object} options * @inherits SchemaType * @api private */
function SchemaArray (key, cast, options) { if (cast) { var castOptions = {};
if ('Object' === cast.constructor.name) { if (cast.type) { // support { type: Woot }
castOptions = utils.clone(cast); // do not alter user arguments
delete castOptions.type; cast = cast.type; } else { cast = Mixed; } }
// support { type: 'String' }
var name = 'string' == typeof cast ? cast : cast.name;
var caster = name in Types ? Types[name] : cast;
this.casterConstructor = caster; this.caster = new caster(null, castOptions); if (!(this.caster instanceof EmbeddedDoc)) { this.caster.path = key; } }
SchemaType.call(this, key, options);
var self = this , defaultArr , fn;
if (this.defaultValue) { defaultArr = this.defaultValue; fn = 'function' == typeof defaultArr; }
this.default(function(){ var arr = fn ? defaultArr() : defaultArr || []; return new MongooseArray(arr, self.path, this); }); };
/*! * Inherits from SchemaType. */
SchemaArray.prototype.__proto__ = SchemaType.prototype;
/** * Check required * * @param {Array} value * @api private */
SchemaArray.prototype.checkRequired = function (value) { return !!(value && value.length); };
/** * Overrides the getters application for the population special-case * * @param {Object} value * @param {Object} scope * @api private */
SchemaArray.prototype.applyGetters = function (value, scope) { if (this.caster.options && this.caster.options.ref) { // means the object id was populated
return value; }
return SchemaType.prototype.applyGetters.call(this, value, scope); };
/** * Casts contents * * @param {Object} value * @param {Document} doc document that triggers the casting * @param {Boolean} init whether this is an initialization cast * @api private */
SchemaArray.prototype.cast = function (value, doc, init) { if (Array.isArray(value)) { if (!(value instanceof MongooseArray)) { value = new MongooseArray(value, this.path, doc); }
if (this.caster) { try { for (var i = 0, l = value.length; i < l; i++) { value[i] = this.caster.cast(value[i], doc, init); } } catch (e) { // rethrow
throw new CastError(e.type, value, this.path); } }
return value; } else { return this.cast([value], doc, init); } };
/** * Casts contents for queries. * * @param {String} $conditional * @param {any} [value] * @api private */
SchemaArray.prototype.castForQuery = function ($conditional, value) { var handler , val; if (arguments.length === 2) { handler = this.$conditionalHandlers[$conditional]; if (!handler) throw new Error("Can't use " + $conditional + " with Array."); val = handler.call(this, value); } else { val = $conditional; var proto = this.casterConstructor.prototype; var method = proto.castForQuery || proto.cast;
var caster = this.caster; if (Array.isArray(val)) { val = val.map(function (v) { if (method) v = method.call(caster, v);
return isMongooseObject(v) ? v.toObject() : v; }); } else if (method) { val = method.call(caster, val); } } return val && isMongooseObject(val) ? val.toObject() : val; };
/*! * @ignore */
function castToNumber (val) { return Types.Number.prototype.cast.call(this, val); }
function castArray (arr, self) { self || (self = this);
arr.forEach(function (v, i) { if (Array.isArray(v)) { castArray(v, self); } else { arr[i] = castToNumber.call(self, v); } }); }
SchemaArray.prototype.$conditionalHandlers = { '$all': function handle$all (val) { if (!Array.isArray(val)) { val = [val]; }
val = val.map(function (v) { if (v && 'Object' === v.constructor.name) { var o = {}; o[this.path] = v; var query = new Query(o); query.cast(this.casterConstructor); return query._conditions[this.path]; } return v; }, this);
return this.castForQuery(val); } , '$elemMatch': function (val) { if (val.$in) { val.$in = this.castForQuery('$in', val.$in); return val; }
var query = new Query(val); query.cast(this.casterConstructor); return query._conditions; } , '$size': castToNumber , '$ne': SchemaArray.prototype.castForQuery , '$in': SchemaArray.prototype.castForQuery , '$nin': SchemaArray.prototype.castForQuery , '$regex': SchemaArray.prototype.castForQuery , '$options': String , '$near': SchemaArray.prototype.castForQuery , '$nearSphere': SchemaArray.prototype.castForQuery , '$gt': SchemaArray.prototype.castForQuery , '$gte': SchemaArray.prototype.castForQuery , '$lt': SchemaArray.prototype.castForQuery , '$lte': SchemaArray.prototype.castForQuery , '$within': function (val) { var self = this;
if (val.$maxDistance) { val.$maxDistance = castToNumber.call(this, val.$maxDistance); }
if (val.$box || val.$polygon) { var type = val.$box ? '$box' : '$polygon'; val[type].forEach(function (arr) { if (!Array.isArray(arr)) { var msg = 'Invalid $within $box argument. ' + 'Expected an array, received ' + arr; throw new TypeError(msg); } arr.forEach(function (v, i) { arr[i] = castToNumber.call(this, v); }); }) } else if (val.$center || val.$centerSphere) { var type = val.$center ? '$center' : '$centerSphere'; val[type].forEach(function (item, i) { if (Array.isArray(item)) { item.forEach(function (v, j) { item[j] = castToNumber.call(this, v); }); } else { val[type][i] = castToNumber.call(this, item); } }) } else if (val.$geometry) { switch (val.$geometry.type) { case 'Polygon': case 'LineString': case 'Point': val.$geometry.coordinates.forEach(castArray); break; default: // ignore unknowns
break; } }
return val; } , '$geoIntersects': function (val) { var geo = val.$geometry; if (!geo) return;
switch (val.$geometry.type) { case 'Polygon': case 'LineString': case 'Point': val.$geometry.coordinates.forEach(castArray); break; default: // ignore unknowns
break; }
return val; } , '$maxDistance': castToNumber };
/*! * Module exports. */
module.exports = SchemaArray;
|