thoughts/node_modules/mongoose/lib/schema/documentarray.js
node_modules/mongoose/lib/schema/documentarray.jsRaw
1
2/*!
3 * Module dependencies.
4 */
5
6var SchemaType = require('../schematype')
7 , ArrayType = require('./array')
8 , MongooseDocumentArray = require('../types/documentarray')
9 , Subdocument = require('../types/embedded')
10 , Document = require('../document');
11
12/**
13 * SubdocsArray SchemaType constructor
14 *
15 * @param {String} key
16 * @param {Schema} schema
17 * @param {Object} options
18 * @inherits SchemaArray
19 * @api private
20 */
21
22function DocumentArray (key, schema, options) {
23
24 // compile an embedded document for this schema
25 function EmbeddedDocument () {
26 Subdocument.apply(this, arguments);
27 }
28
29 EmbeddedDocument.prototype.__proto__ = Subdocument.prototype;
30 EmbeddedDocument.prototype.$__setSchema(schema);
31 EmbeddedDocument.schema = schema;
32
33 // apply methods
34 for (var i in schema.methods) {
35 EmbeddedDocument.prototype[i] = schema.methods[i];
36 }
37
38 // apply statics
39 for (var i in schema.statics)
40 EmbeddedDocument[i] = schema.statics[i];
41
42 EmbeddedDocument.options = options;
43 this.schema = schema;
44
45 ArrayType.call(this, key, EmbeddedDocument, options);
46
47 this.schema = schema;
48 var path = this.path;
49 var fn = this.defaultValue;
50
51 this.default(function(){
52 var arr = fn.call(this);
53 if (!Array.isArray(arr)) arr = [arr];
54 return new MongooseDocumentArray(arr, path, this);
55 });
56};
57
58/*!
59 * Inherits from ArrayType.
60 */
61
62DocumentArray.prototype.__proto__ = ArrayType.prototype;
63
64/**
65 * Performs local validations first, then validations on each embedded doc
66 *
67 * @api private
68 */
69
70DocumentArray.prototype.doValidate = function (array, fn, scope) {
71 var self = this;
72
73 SchemaType.prototype.doValidate.call(this, array, function (err) {
74 if (err) return fn(err);
75
76 var count = array && array.length
77 , error;
78
79 if (!count) return fn();
80
81 // handle sparse arrays, do not use array.forEach which does not
82 // iterate over sparse elements yet reports array.length including
83 // them :(
84
85 for (var i = 0, len = count; i < len; ++i) {
86 // sidestep sparse entries
87 var doc = array[i];
88 if (!doc) {
89 --count || fn();
90 continue;
91 }
92
93 ;(function (i) {
94 doc.validate(function (err) {
95 if (err && !error) {
96 // rewrite the key
97 err.key = self.key + '.' + i + '.' + err.key;
98 return fn(error = err);
99 }
100 --count || fn();
101 });
102 })(i);
103 }
104 }, scope);
105};
106
107/**
108 * Casts contents
109 *
110 * @param {Object} value
111 * @param {Document} document that triggers the casting
112 * @api private
113 */
114
115DocumentArray.prototype.cast = function (value, doc, init, prev) {
116 var selected
117 , subdoc
118 , i
119
120 if (!Array.isArray(value)) {
121 return this.cast([value], doc, init, prev);
122 }
123
124 if (!(value instanceof MongooseDocumentArray)) {
125 value = new MongooseDocumentArray(value, this.path, doc);
126 }
127
128 i = value.length;
129
130 while (i--) {
131 if (!(value[i] instanceof Subdocument) && value[i]) {
132 if (init) {
133 selected || (selected = scopePaths(this, doc.$__.selected, init));
134 subdoc = new this.casterConstructor(null, value, true, selected);
135 value[i] = subdoc.init(value[i]);
136 } else {
137 if (prev && (subdoc = prev.id(value[i]._id))) {
138 // handle resetting doc with existing id but differing data
139 // doc.array = [{ doc: 'val' }]
140 subdoc.set(value[i]);
141 } else {
142 subdoc = new this.casterConstructor(value[i], value);
143 }
144
145 // if set() is hooked it will have no return value
146 // see gh-746
147 value[i] = subdoc;
148 }
149 }
150 }
151
152 return value;
153}
154
155/*!
156 * Scopes paths selected in a query to this array.
157 * Necessary for proper default application of subdocument values.
158 *
159 * @param {DocumentArray} array - the array to scope `fields` paths
160 * @param {Object|undefined} fields - the root fields selected in the query
161 * @param {Boolean|undefined} init - if we are being created part of a query result
162 */
163
164function scopePaths (array, fields, init) {
165 if (!(init && fields)) return undefined;
166
167 var path = array.path + '.'
168 , keys = Object.keys(fields)
169 , i = keys.length
170 , selected = {}
171 , hasKeys
172 , key
173
174 while (i--) {
175 key = keys[i];
176 if (0 === key.indexOf(path)) {
177 hasKeys || (hasKeys = true);
178 selected[key.substring(path.length)] = fields[key];
179 }
180 }
181
182 return hasKeys && selected || undefined;
183}
184
185/*!
186 * Module exports.
187 */
188
189module.exports = DocumentArray;