arnaucube
arnaucube/thoughtsPublic Self-hosted repository
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;