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;