arnaucube
arnaucube/thoughtsPublic Self-hosted repository
thoughts/node_modules/mongoose/lib/cast.js
node_modules/mongoose/lib/cast.jsRaw
  1/*!
  2 * Module dependencies.
  3 */
  4
  5var utils = require('./utils');
  6var Types = require('./schema/index');
  7
  8var ALLOWED_GEOWITHIN_GEOJSON_TYPES = ['Polygon', 'MultiPolygon'];
  9
 10/**
 11 * Handles internal casting for queries
 12 *
 13 * @param {Schema} schema
 14 * @param {Object} obj Object to cast
 15 * @api private
 16 */
 17module.exports = function cast(schema, obj) {
 18  var paths = Object.keys(obj),
 19      i = paths.length,
 20      any$conditionals,
 21      schematype,
 22      nested,
 23      path,
 24      type,
 25      val;
 26
 27  while (i--) {
 28    path = paths[i];
 29    val = obj[path];
 30
 31    if (path === '$or' || path === '$nor' || path === '$and') {
 32      var k = val.length;
 33
 34      while (k--) {
 35        val[k] = cast(schema, val[k]);
 36      }
 37    } else if (path === '$where') {
 38      type = typeof val;
 39
 40      if (type !== 'string' && type !== 'function') {
 41        throw new Error('Must have a string or function for $where');
 42      }
 43
 44      if (type === 'function') {
 45        obj[path] = val.toString();
 46      }
 47
 48      continue;
 49    } else if (path === '$elemMatch') {
 50      val = cast(schema, val);
 51    } else {
 52      if (!schema) {
 53        // no casting for Mixed types
 54        continue;
 55      }
 56
 57      schematype = schema.path(path);
 58
 59      if (!schematype) {
 60        // Handle potential embedded array queries
 61        var split = path.split('.'),
 62            j = split.length,
 63            pathFirstHalf,
 64            pathLastHalf,
 65            remainingConds;
 66
 67        // Find the part of the var path that is a path of the Schema
 68        while (j--) {
 69          pathFirstHalf = split.slice(0, j).join('.');
 70          schematype = schema.path(pathFirstHalf);
 71          if (schematype) {
 72            break;
 73          }
 74        }
 75
 76        // If a substring of the input path resolves to an actual real path...
 77        if (schematype) {
 78          // Apply the casting; similar code for $elemMatch in schema/array.js
 79          if (schematype.caster && schematype.caster.schema) {
 80            remainingConds = {};
 81            pathLastHalf = split.slice(j).join('.');
 82            remainingConds[pathLastHalf] = val;
 83            obj[path] = cast(schematype.caster.schema, remainingConds)[pathLastHalf];
 84          } else {
 85            obj[path] = val;
 86          }
 87          continue;
 88        }
 89
 90        if (utils.isObject(val)) {
 91          // handle geo schemas that use object notation
 92          // { loc: { long: Number, lat: Number }
 93
 94          var geo = '';
 95          if (val.$near) {
 96            geo = '$near';
 97          } else if (val.$nearSphere) {
 98            geo = '$nearSphere';
 99          } else if (val.$within) {
100            geo = '$within';
101          } else if (val.$geoIntersects) {
102            geo = '$geoIntersects';
103          } else if (val.$geoWithin) {
104            geo = '$geoWithin';
105          }
106
107          if (!geo) {
108            continue;
109          }
110
111          var numbertype = new Types.Number('__QueryCasting__');
112          var value = val[geo];
113
114          if (val.$maxDistance != null) {
115            val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
116          }
117          if (val.$minDistance != null) {
118            val.$minDistance = numbertype.castForQuery(val.$minDistance);
119          }
120
121          if (geo === '$within') {
122            var withinType = value.$center
123                || value.$centerSphere
124                || value.$box
125                || value.$polygon;
126
127            if (!withinType) {
128              throw new Error('Bad $within paramater: ' + JSON.stringify(val));
129            }
130
131            value = withinType;
132          } else if (geo === '$near' &&
133              typeof value.type === 'string' && Array.isArray(value.coordinates)) {
134            // geojson; cast the coordinates
135            value = value.coordinates;
136          } else if ((geo === '$near' || geo === '$nearSphere' || geo === '$geoIntersects') &&
137              value.$geometry && typeof value.$geometry.type === 'string' &&
138              Array.isArray(value.$geometry.coordinates)) {
139            if (value.$maxDistance != null) {
140              value.$maxDistance = numbertype.castForQuery(value.$maxDistance);
141            }
142            if (value.$minDistance != null) {
143              value.$minDistance = numbertype.castForQuery(value.$minDistance);
144            }
145            if (utils.isMongooseObject(value.$geometry)) {
146              value.$geometry = value.$geometry.toObject({ virtuals: false });
147            }
148            value = value.$geometry.coordinates;
149          } else if (geo === '$geoWithin') {
150            if (!value.$geometry) {
151              throw new Error('$geoWithin must specify $geometry');
152            }
153            if (utils.isMongooseObject(value.$geometry)) {
154              value.$geometry = value.$geometry.toObject({ virtuals: false });
155            }
156            var geoWithinType = value.$geometry.type;
157            if (ALLOWED_GEOWITHIN_GEOJSON_TYPES.indexOf(geoWithinType) === -1) {
158              throw new Error('Invalid geoJSON type for $geoWithin "' +
159                geoWithinType + '", must be "Polygon" or "MultiPolygon"');
160            }
161            value = value.$geometry.coordinates;
162          }
163
164          _cast(value, numbertype);
165        }
166      } else if (val === null || val === undefined) {
167        obj[path] = null;
168        continue;
169      } else if (val.constructor.name === 'Object') {
170        any$conditionals = Object.keys(val).some(function(k) {
171          return k.charAt(0) === '$' && k !== '$id' && k !== '$ref';
172        });
173
174        if (!any$conditionals) {
175          obj[path] = schematype.castForQuery(val);
176        } else {
177          var ks = Object.keys(val),
178              $cond;
179
180          k = ks.length;
181
182          while (k--) {
183            $cond = ks[k];
184            nested = val[$cond];
185
186            if ($cond === '$exists') {
187              if (typeof nested !== 'boolean') {
188                throw new Error('$exists parameter must be Boolean');
189              }
190              continue;
191            }
192
193            if ($cond === '$type') {
194              if (typeof nested !== 'number' && typeof nested !== 'string') {
195                throw new Error('$type parameter must be number or string');
196              }
197              continue;
198            }
199
200            if ($cond === '$not') {
201              cast(schema, nested);
202            } else {
203              val[$cond] = schematype.castForQuery($cond, nested);
204            }
205          }
206        }
207      } else {
208        obj[path] = schematype.castForQuery(val);
209      }
210    }
211  }
212
213  return obj;
214};
215
216function _cast(val, numbertype) {
217  if (Array.isArray(val)) {
218    val.forEach(function(item, i) {
219      if (Array.isArray(item) || utils.isObject(item)) {
220        return _cast(item, numbertype);
221      }
222      val[i] = numbertype.castForQuery(item);
223    });
224  } else {
225    var nearKeys = Object.keys(val);
226    var nearLen = nearKeys.length;
227    while (nearLen--) {
228      var nkey = nearKeys[nearLen];
229      var item = val[nkey];
230      if (Array.isArray(item) || utils.isObject(item)) {
231        _cast(item, numbertype);
232        val[nkey] = item;
233      } else {
234        val[nkey] = numbertype.castForQuery(item);
235      }
236    }
237  }
238}