/*! * Module dependencies. */ var utils = require('./utils'); var Types = require('./schema/index'); /** * Handles internal casting for queries * * @param {Schema} schema * @param {Object obj Object to cast * @api private */ var cast = module.exports = function(schema, obj) { var paths = Object.keys(obj) , i = paths.length , any$conditionals , schematype , nested , path , type , val; while (i--) { path = paths[i]; val = obj[path]; if ('$or' === path || '$nor' === path || '$and' === path) { var k = val.length; var orComponentQuery; while (k--) { val[k] = cast(schema, val[k]); } } else if (path === '$where') { type = typeof val; if ('string' !== type && 'function' !== type) { throw new Error("Must have a string or function for $where"); } if ('function' === type) { obj[path] = val.toString(); } continue; } else { if (!schema) { // no casting for Mixed types continue; } schematype = schema.path(path); if (!schematype) { // Handle potential embedded array queries var split = path.split('.') , j = split.length , pathFirstHalf , pathLastHalf , remainingConds , castingQuery; // Find the part of the var path that is a path of the Schema while (j--) { pathFirstHalf = split.slice(0, j).join('.'); schematype = schema.path(pathFirstHalf); if (schematype) break; } // If a substring of the input path resolves to an actual real path... if (schematype) { // Apply the casting; similar code for $elemMatch in schema/array.js if (schematype.caster && schematype.caster.schema) { remainingConds = {}; pathLastHalf = split.slice(j).join('.'); remainingConds[pathLastHalf] = val; obj[path] = cast(schematype.caster.schema, remainingConds)[pathLastHalf]; } else { obj[path] = val; } continue; } if (utils.isObject(val)) { // handle geo schemas that use object notation // { loc: { long: Number, lat: Number } var geo = val.$near ? '$near' : val.$nearSphere ? '$nearSphere' : val.$within ? '$within' : val.$geoIntersects ? '$geoIntersects' : ''; if (!geo) { continue; } var numbertype = new Types.Number('__QueryCasting__') var value = val[geo]; if (val.$maxDistance) { val.$maxDistance = numbertype.castForQuery(val.$maxDistance); } if ('$within' == geo) { var withinType = value.$center || value.$centerSphere || value.$box || value.$polygon; if (!withinType) { throw new Error('Bad $within paramater: ' + JSON.stringify(val)); } value = withinType; } else if ('$near' == geo && 'string' == typeof value.type && Array.isArray(value.coordinates)) { // geojson; cast the coordinates value = value.coordinates; } else if (('$near' == geo || '$nearSphere' == geo || '$geoIntersects' == geo) && value.$geometry && 'string' == typeof value.$geometry.type && Array.isArray(value.$geometry.coordinates)) { // geojson; cast the coordinates value = value.$geometry.coordinates; } ;(function _cast (val) { if (Array.isArray(val)) { val.forEach(function (item, i) { if (Array.isArray(item) || utils.isObject(item)) { return _cast(item); } val[i] = numbertype.castForQuery(item); }); } else { var nearKeys= Object.keys(val); var nearLen = nearKeys.length; while (nearLen--) { var nkey = nearKeys[nearLen]; var item = val[nkey]; if (Array.isArray(item) || utils.isObject(item)) { _cast(item); val[nkey] = item; } else { val[nkey] = numbertype.castForQuery(item); } } } })(value); } } else if (val === null || val === undefined) { continue; } else if ('Object' === val.constructor.name) { any$conditionals = Object.keys(val).some(function (k) { return k.charAt(0) === '$' && k !== '$id' && k !== '$ref'; }); if (!any$conditionals) { obj[path] = schematype.castForQuery(val); } else { var ks = Object.keys(val) , k = ks.length , $cond; while (k--) { $cond = ks[k]; nested = val[$cond]; if ('$exists' === $cond) { if ('boolean' !== typeof nested) { throw new Error("$exists parameter must be Boolean"); } continue; } if ('$type' === $cond) { if ('number' !== typeof nested) { throw new Error("$type parameter must be Number"); } continue; } if ('$not' === $cond) { cast(schema, nested); } else { val[$cond] = schematype.castForQuery($cond, nested); } } } } else { obj[path] = schematype.castForQuery(val); } } } return obj; }