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');
  6var CastError = SchemaType.CastError;
  7var Types = {
  8  Boolean: require('./boolean'),
  9  Date: require('./date'),
 10  Number: require('./number'),
 11  String: require('./string'),
 12  ObjectId: require('./objectid'),
 13  Buffer: require('./buffer')
 14};
 15var MongooseArray = require('../types').Array;
 16var EmbeddedDoc = require('../types').Embedded;
 17var Mixed = require('./mixed');
 18var cast = require('../cast');
 19var util = require('util');
 20var utils = require('../utils');
 21var isMongooseObject = utils.isMongooseObject;
 22var castToNumber = require('./operators/helpers').castToNumber;
 23var geospatial = require('./operators/geospatial');
 24
 25/**
 26 * Array SchemaType constructor
 27 *
 28 * @param {String} key
 29 * @param {SchemaType} cast
 30 * @param {Object} options
 31 * @inherits SchemaType
 32 * @api public
 33 */
 34
 35function SchemaArray(key, cast, options) {
 36  if (cast) {
 37    var castOptions = {};
 38
 39    if (utils.getFunctionName(cast.constructor) === 'Object') {
 40      if (cast.type) {
 41        // support { type: Woot }
 42        castOptions = utils.clone(cast); // do not alter user arguments
 43        delete castOptions.type;
 44        cast = cast.type;
 45      } else {
 46        cast = Mixed;
 47      }
 48    }
 49
 50    // support { type: 'String' }
 51    var name = typeof cast === 'string'
 52        ? cast
 53        : utils.getFunctionName(cast);
 54
 55    var caster = name in Types
 56        ? Types[name]
 57        : cast;
 58
 59    this.casterConstructor = caster;
 60    if (typeof caster === 'function') {
 61      this.caster = new caster(null, castOptions);
 62    } else {
 63      this.caster = caster;
 64    }
 65
 66    if (!(this.caster instanceof EmbeddedDoc)) {
 67      this.caster.path = key;
 68    }
 69  }
 70
 71  SchemaType.call(this, key, options, 'Array');
 72
 73  var _this = this;
 74  var defaultArr;
 75  var fn;
 76
 77  if (this.defaultValue) {
 78    defaultArr = this.defaultValue;
 79    fn = typeof defaultArr === 'function';
 80  }
 81
 82  if (!('defaultValue' in this) || this.defaultValue !== void 0) {
 83    this.default(function() {
 84      var arr = fn ? defaultArr() : defaultArr || [];
 85      return new MongooseArray(arr, _this.path, this);
 86    });
 87  }
 88}
 89
 90/**
 91 * This schema type's name, to defend against minifiers that mangle
 92 * function names.
 93 *
 94 * @api public
 95 */
 96SchemaArray.schemaName = 'Array';
 97
 98/*!
 99 * Inherits from SchemaType.
100 */
101SchemaArray.prototype = Object.create(SchemaType.prototype);
102SchemaArray.prototype.constructor = SchemaArray;
103
104/**
105 * Check if the given value satisfies a required validator. The given value
106 * must be not null nor undefined, and have a non-zero length.
107 *
108 * @param {Any} value
109 * @return {Boolean}
110 * @api public
111 */
112
113SchemaArray.prototype.checkRequired = function(value) {
114  return !!(value && value.length);
115};
116
117/**
118 * Overrides the getters application for the population special-case
119 *
120 * @param {Object} value
121 * @param {Object} scope
122 * @api private
123 */
124
125SchemaArray.prototype.applyGetters = function(value, scope) {
126  if (this.caster.options && this.caster.options.ref) {
127    // means the object id was populated
128    return value;
129  }
130
131  return SchemaType.prototype.applyGetters.call(this, value, scope);
132};
133
134/**
135 * Casts values for set().
136 *
137 * @param {Object} value
138 * @param {Document} doc document that triggers the casting
139 * @param {Boolean} init whether this is an initialization cast
140 * @api private
141 */
142
143SchemaArray.prototype.cast = function(value, doc, init) {
144  if (Array.isArray(value)) {
145    if (!value.length && doc) {
146      var indexes = doc.schema.indexedPaths();
147
148      for (var i = 0, l = indexes.length; i < l; ++i) {
149        var pathIndex = indexes[i][0][this.path];
150        if (pathIndex === '2dsphere' || pathIndex === '2d') {
151          return;
152        }
153      }
154    }
155
156    if (!(value && value.isMongooseArray)) {
157      value = new MongooseArray(value, this.path, doc);
158    }
159
160    if (this.caster) {
161      try {
162        for (i = 0, l = value.length; i < l; i++) {
163          value[i] = this.caster.cast(value[i], doc, init);
164        }
165      } catch (e) {
166        // rethrow
167        throw new CastError('[' + e.kind + ']', util.inspect(value), this.path, e);
168      }
169    }
170
171    return value;
172  }
173  // gh-2442: if we're loading this from the db and its not an array, mark
174  // the whole array as modified.
175  if (!!doc && !!init) {
176    doc.markModified(this.path);
177  }
178  return this.cast([value], doc, init);
179};
180
181/**
182 * Casts values for queries.
183 *
184 * @param {String} $conditional
185 * @param {any} [value]
186 * @api private
187 */
188
189SchemaArray.prototype.castForQuery = function($conditional, value) {
190  var handler,
191      val;
192
193  if (arguments.length === 2) {
194    handler = this.$conditionalHandlers[$conditional];
195
196    if (!handler) {
197      throw new Error('Can\'t use ' + $conditional + ' with Array.');
198    }
199
200    val = handler.call(this, value);
201  } else {
202    val = $conditional;
203    var proto = this.casterConstructor.prototype;
204    var method = proto.castForQuery || proto.cast;
205    var caster = this.caster;
206
207    if (Array.isArray(val)) {
208      val = val.map(function(v) {
209        if (utils.isObject(v) && v.$elemMatch) {
210          return v;
211        }
212        if (method) {
213          v = method.call(caster, v);
214        }
215        return isMongooseObject(v) ?
216            v.toObject({virtuals: false}) :
217            v;
218      });
219    } else if (method) {
220      val = method.call(caster, val);
221    }
222  }
223
224  return val && isMongooseObject(val) ?
225      val.toObject({virtuals: false}) :
226      val;
227};
228
229function cast$all(val) {
230  if (!Array.isArray(val)) {
231    val = [val];
232  }
233
234  val = val.map(function(v) {
235    if (utils.isObject(v)) {
236      var o = {};
237      o[this.path] = v;
238      return cast(this.casterConstructor.schema, o)[this.path];
239    }
240    return v;
241  }, this);
242
243  return this.castForQuery(val);
244}
245
246function cast$elemMatch(val) {
247  var keys = Object.keys(val);
248  var numKeys = keys.length;
249  var key;
250  var value;
251  for (var i = 0; i < numKeys; ++i) {
252    key = keys[i];
253    value = val[key];
254    if (key.indexOf('$') === 0 && value) {
255      val[key] = this.castForQuery(key, value);
256    }
257  }
258
259  return cast(this.casterConstructor.schema, val);
260}
261
262var handle = SchemaArray.prototype.$conditionalHandlers = {};
263
264handle.$all = cast$all;
265handle.$options = String;
266handle.$elemMatch = cast$elemMatch;
267handle.$geoIntersects = geospatial.cast$geoIntersects;
268handle.$or = handle.$and = function(val) {
269  if (!Array.isArray(val)) {
270    throw new TypeError('conditional $or/$and require array');
271  }
272
273  var ret = [];
274  for (var i = 0; i < val.length; ++i) {
275    ret.push(cast(this.casterConstructor.schema, val[i]));
276  }
277
278  return ret;
279};
280
281handle.$near =
282handle.$nearSphere = geospatial.cast$near;
283
284handle.$within =
285handle.$geoWithin = geospatial.cast$within;
286
287handle.$size =
288handle.$minDistance =
289handle.$maxDistance = castToNumber;
290
291handle.$eq =
292handle.$gt =
293handle.$gte =
294handle.$in =
295handle.$lt =
296handle.$lte =
297handle.$ne =
298handle.$nin =
299handle.$regex = SchemaArray.prototype.castForQuery;
300
301/*!
302 * Module exports.
303 */
304
305module.exports = SchemaArray;