123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- /*!
- * 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;
- }
|