UNPKG

mongoose-diff-tracking

Version:
205 lines (175 loc) 5.67 kB
/*! * 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 */ module.exports = function cast(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 (path === '$or' || path === '$nor' || path === '$and') { var k = val.length; while (k--) { val[k] = cast(schema, val[k]); } } else if (path === '$where') { type = typeof val; if (type !== 'string' && type !== 'function') { throw new Error('Must have a string or function for $where'); } if (type === 'function') { obj[path] = val.toString(); } continue; } else if (path === '$elemMatch') { val = cast(schema, val); } 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; // 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 (geo === '$within') { 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 (geo === '$near' && typeof value.type === 'string' && Array.isArray(value.coordinates)) { // geojson; cast the coordinates value = value.coordinates; } else if ((geo === '$near' || geo === '$nearSphere' || geo === '$geoIntersects') && value.$geometry && typeof value.$geometry.type === 'string' && Array.isArray(value.$geometry.coordinates)) { // geojson; cast the coordinates value = value.$geometry.coordinates; } _cast(value, numbertype); } } else if (val === null || val === undefined) { obj[path] = null; continue; } else if (val.constructor.name === 'Object') { 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), $cond; k = ks.length; while (k--) { $cond = ks[k]; nested = val[$cond]; if ($cond === '$exists') { if (typeof nested !== 'boolean') { throw new Error('$exists parameter must be Boolean'); } continue; } if ($cond === '$type') { if (typeof nested !== 'number') { throw new Error('$type parameter must be Number'); } continue; } if ($cond === '$not') { cast(schema, nested); } else { val[$cond] = schematype.castForQuery($cond, nested); } } } } else { obj[path] = schematype.castForQuery(val); } } } return obj; }; function _cast(val, numbertype) { if (Array.isArray(val)) { val.forEach(function(item, i) { if (Array.isArray(item) || utils.isObject(item)) { return _cast(item, numbertype); } 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, numbertype); val[nkey] = item; } else { val[nkey] = numbertype.castForQuery(item); } } } }