arnaucube
arnaucube/thoughtsPublic Self-hosted repository
thoughts/node_modules/mongoose/lib/schema/array.js
node_modules/mongoose/lib/schema/array.jsRaw
  1/*!
  2 * Module dependencies.
  3 */
  4
  5var SchemaType = require('../schematype')
  6  , CastError = SchemaType.CastError
  7  , NumberSchema = require('./number')
  8  , Types = {
  9        Boolean: require('./boolean')
 10      , Date: require('./date')
 11      , Number: require('./number')
 12      , String: require('./string')
 13      , ObjectId: require('./objectid')
 14      , Buffer: require('./buffer')
 15    }
 16  , MongooseArray = require('../types').Array
 17  , EmbeddedDoc = require('../types').Embedded
 18  , Mixed = require('./mixed')
 19  , Query = require('../query')
 20  , utils = require('../utils')
 21  , isMongooseObject = utils.isMongooseObject
 22
 23/**
 24 * Array SchemaType constructor
 25 *
 26 * @param {String} key
 27 * @param {SchemaType} cast
 28 * @param {Object} options
 29 * @inherits SchemaType
 30 * @api private
 31 */
 32
 33function SchemaArray (key, cast, options) {
 34  if (cast) {
 35    var castOptions = {};
 36
 37    if ('Object' === cast.constructor.name) {
 38      if (cast.type) {
 39        // support { type: Woot }
 40        castOptions = utils.clone(cast); // do not alter user arguments
 41        delete castOptions.type;
 42        cast = cast.type;
 43      } else {
 44        cast = Mixed;
 45      }
 46    }
 47
 48    // support { type: 'String' }
 49    var name = 'string' == typeof cast
 50      ? cast
 51      : cast.name;
 52
 53    var caster = name in Types
 54      ? Types[name]
 55      : cast;
 56
 57    this.casterConstructor = caster;
 58    this.caster = new caster(null, castOptions);
 59    if (!(this.caster instanceof EmbeddedDoc)) {
 60      this.caster.path = key;
 61    }
 62  }
 63
 64  SchemaType.call(this, key, options);
 65
 66  var self = this
 67    , defaultArr
 68    , fn;
 69
 70  if (this.defaultValue) {
 71    defaultArr = this.defaultValue;
 72    fn = 'function' == typeof defaultArr;
 73  }
 74
 75  this.default(function(){
 76    var arr = fn ? defaultArr() : defaultArr || [];
 77    return new MongooseArray(arr, self.path, this);
 78  });
 79};
 80
 81/*!
 82 * Inherits from SchemaType.
 83 */
 84
 85SchemaArray.prototype.__proto__ = SchemaType.prototype;
 86
 87/**
 88 * Check required
 89 *
 90 * @param {Array} value
 91 * @api private
 92 */
 93
 94SchemaArray.prototype.checkRequired = function (value) {
 95  return !!(value && value.length);
 96};
 97
 98/**
 99 * Overrides the getters application for the population special-case
100 *
101 * @param {Object} value
102 * @param {Object} scope
103 * @api private
104 */
105
106SchemaArray.prototype.applyGetters = function (value, scope) {
107  if (this.caster.options && this.caster.options.ref) {
108    // means the object id was populated
109    return value;
110  }
111
112  return SchemaType.prototype.applyGetters.call(this, value, scope);
113};
114
115/**
116 * Casts contents
117 *
118 * @param {Object} value
119 * @param {Document} doc document that triggers the casting
120 * @param {Boolean} init whether this is an initialization cast
121 * @api private
122 */
123
124SchemaArray.prototype.cast = function (value, doc, init) {
125  if (Array.isArray(value)) {
126    if (!(value instanceof MongooseArray)) {
127      value = new MongooseArray(value, this.path, doc);
128    }
129
130    if (this.caster) {
131      try {
132        for (var i = 0, l = value.length; i < l; i++) {
133          value[i] = this.caster.cast(value[i], doc, init);
134        }
135      } catch (e) {
136        // rethrow
137        throw new CastError(e.type, value, this.path);
138      }
139    }
140
141    return value;
142  } else {
143    return this.cast([value], doc, init);
144  }
145};
146
147/**
148 * Casts contents for queries.
149 *
150 * @param {String} $conditional
151 * @param {any} [value]
152 * @api private
153 */
154
155SchemaArray.prototype.castForQuery = function ($conditional, value) {
156  var handler
157    , val;
158  if (arguments.length === 2) {
159    handler = this.$conditionalHandlers[$conditional];
160    if (!handler)
161      throw new Error("Can't use " + $conditional + " with Array.");
162    val = handler.call(this, value);
163  } else {
164    val = $conditional;
165    var proto = this.casterConstructor.prototype;
166    var method = proto.castForQuery || proto.cast;
167
168    var caster = this.caster;
169    if (Array.isArray(val)) {
170      val = val.map(function (v) {
171        if (method) v = method.call(caster, v);
172
173        return isMongooseObject(v)
174          ? v.toObject()
175          : v;
176      });
177    } else if (method) {
178      val = method.call(caster, val);
179    }
180  }
181  return val && isMongooseObject(val)
182    ? val.toObject()
183    : val;
184};
185
186/*!
187 * @ignore
188 */
189
190function castToNumber (val) {
191  return Types.Number.prototype.cast.call(this, val);
192}
193
194function castArray (arr, self) {
195  self || (self = this);
196
197  arr.forEach(function (v, i) {
198    if (Array.isArray(v)) {
199      castArray(v, self);
200    } else {
201      arr[i] = castToNumber.call(self, v);
202    }
203  });
204}
205
206SchemaArray.prototype.$conditionalHandlers = {
207    '$all': function handle$all (val) {
208      if (!Array.isArray(val)) {
209        val = [val];
210      }
211
212      val = val.map(function (v) {
213        if (v && 'Object' === v.constructor.name) {
214          var o = {};
215          o[this.path] = v;
216          var query = new Query(o);
217          query.cast(this.casterConstructor);
218          return query._conditions[this.path];
219        }
220        return v;
221      }, this);
222
223      return this.castForQuery(val);
224    }
225  , '$elemMatch': function (val) {
226      if (val.$in) {
227        val.$in = this.castForQuery('$in', val.$in);
228        return val;
229      }
230
231      var query = new Query(val);
232      query.cast(this.casterConstructor);
233      return query._conditions;
234    }
235  , '$size': castToNumber
236  , '$ne': SchemaArray.prototype.castForQuery
237  , '$in': SchemaArray.prototype.castForQuery
238  , '$nin': SchemaArray.prototype.castForQuery
239  , '$regex': SchemaArray.prototype.castForQuery
240  , '$options': String
241  , '$near': SchemaArray.prototype.castForQuery
242  , '$nearSphere': SchemaArray.prototype.castForQuery
243  , '$gt': SchemaArray.prototype.castForQuery
244  , '$gte': SchemaArray.prototype.castForQuery
245  , '$lt': SchemaArray.prototype.castForQuery
246  , '$lte': SchemaArray.prototype.castForQuery
247  , '$within': function (val) {
248      var self = this;
249
250      if (val.$maxDistance) {
251        val.$maxDistance = castToNumber.call(this, val.$maxDistance);
252      }
253
254      if (val.$box || val.$polygon) {
255        var type = val.$box ? '$box' : '$polygon';
256        val[type].forEach(function (arr) {
257          if (!Array.isArray(arr)) {
258            var msg = 'Invalid $within $box argument. '
259                    + 'Expected an array, received ' + arr;
260            throw new TypeError(msg);
261          }
262          arr.forEach(function (v, i) {
263            arr[i] = castToNumber.call(this, v);
264          });
265        })
266      } else if (val.$center || val.$centerSphere) {
267        var type = val.$center ? '$center' : '$centerSphere';
268        val[type].forEach(function (item, i) {
269          if (Array.isArray(item)) {
270            item.forEach(function (v, j) {
271              item[j] = castToNumber.call(this, v);
272            });
273          } else {
274            val[type][i] = castToNumber.call(this, item);
275          }
276        })
277      } else if (val.$geometry) {
278        switch (val.$geometry.type) {
279          case 'Polygon':
280          case 'LineString':
281          case 'Point':
282            val.$geometry.coordinates.forEach(castArray);
283            break;
284          default:
285            // ignore unknowns
286            break;
287        }
288      }
289
290      return val;
291    }
292  , '$geoIntersects': function (val) {
293      var geo = val.$geometry;
294      if (!geo) return;
295
296      switch (val.$geometry.type) {
297        case 'Polygon':
298        case 'LineString':
299        case 'Point':
300          val.$geometry.coordinates.forEach(castArray);
301          break;
302        default:
303          // ignore unknowns
304          break;
305      }
306
307      return val;
308    }
309  , '$maxDistance': castToNumber
310};
311
312/*!
313 * Module exports.
314 */
315
316module.exports = SchemaArray;