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;