summaryrefslogtreecommitdiffstats
path: root/common/src/main/webapp/usageguide/appserver/node_modules/mongoose/lib/schema.js
diff options
context:
space:
mode:
Diffstat (limited to 'common/src/main/webapp/usageguide/appserver/node_modules/mongoose/lib/schema.js')
-rw-r--r--common/src/main/webapp/usageguide/appserver/node_modules/mongoose/lib/schema.js1768
1 files changed, 1768 insertions, 0 deletions
diff --git a/common/src/main/webapp/usageguide/appserver/node_modules/mongoose/lib/schema.js b/common/src/main/webapp/usageguide/appserver/node_modules/mongoose/lib/schema.js
new file mode 100644
index 0000000..7e28f58
--- /dev/null
+++ b/common/src/main/webapp/usageguide/appserver/node_modules/mongoose/lib/schema.js
@@ -0,0 +1,1768 @@
+/*!
+ * Module dependencies.
+ */
+
+var readPref = require('./drivers').ReadPreference;
+var EventEmitter = require('events').EventEmitter;
+var VirtualType = require('./virtualtype');
+var utils = require('./utils');
+var MongooseTypes;
+var Kareem = require('kareem');
+var each = require('async/each');
+var SchemaType = require('./schematype');
+
+var IS_KAREEM_HOOK = {
+ count: true,
+ find: true,
+ findOne: true,
+ findOneAndUpdate: true,
+ findOneAndRemove: true,
+ insertMany: true,
+ update: true
+};
+
+/**
+ * Schema constructor.
+ *
+ * ####Example:
+ *
+ * var child = new Schema({ name: String });
+ * var schema = new Schema({ name: String, age: Number, children: [child] });
+ * var Tree = mongoose.model('Tree', schema);
+ *
+ * // setting schema options
+ * new Schema({ name: String }, { _id: false, autoIndex: false })
+ *
+ * ####Options:
+ *
+ * - [autoIndex](/docs/guide.html#autoIndex): bool - defaults to null (which means use the connection's autoIndex option)
+ * - [bufferCommands](/docs/guide.html#bufferCommands): bool - defaults to true
+ * - [capped](/docs/guide.html#capped): bool - defaults to false
+ * - [collection](/docs/guide.html#collection): string - no default
+ * - [emitIndexErrors](/docs/guide.html#emitIndexErrors): bool - defaults to false.
+ * - [id](/docs/guide.html#id): bool - defaults to true
+ * - [_id](/docs/guide.html#_id): bool - defaults to true
+ * - `minimize`: bool - controls [document#toObject](#document_Document-toObject) behavior when called manually - defaults to true
+ * - [read](/docs/guide.html#read): string
+ * - [safe](/docs/guide.html#safe): bool - defaults to true.
+ * - [shardKey](/docs/guide.html#shardKey): bool - defaults to `null`
+ * - [strict](/docs/guide.html#strict): bool - defaults to true
+ * - [toJSON](/docs/guide.html#toJSON) - object - no default
+ * - [toObject](/docs/guide.html#toObject) - object - no default
+ * - [typeKey](/docs/guide.html#typeKey) - string - defaults to 'type'
+ * - [useNestedStrict](/docs/guide.html#useNestedStrict) - boolean - defaults to false
+ * - [validateBeforeSave](/docs/guide.html#validateBeforeSave) - bool - defaults to `true`
+ * - [versionKey](/docs/guide.html#versionKey): string - defaults to "__v"
+ *
+ * ####Note:
+ *
+ * _When nesting schemas, (`children` in the example above), always declare the child schema first before passing it into its parent._
+ *
+ * @param {Object} definition
+ * @param {Object} [options]
+ * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
+ * @event `init`: Emitted after the schema is compiled into a `Model`.
+ * @api public
+ */
+
+function Schema(obj, options) {
+ if (!(this instanceof Schema)) {
+ return new Schema(obj, options);
+ }
+
+ this.obj = obj;
+ this.paths = {};
+ this.subpaths = {};
+ this.virtuals = {};
+ this.singleNestedPaths = {};
+ this.nested = {};
+ this.inherits = {};
+ this.callQueue = [];
+ this._indexes = [];
+ this.methods = {};
+ this.statics = {};
+ this.tree = {};
+ this._requiredpaths = undefined;
+ this.discriminatorMapping = undefined;
+ this._indexedpaths = undefined;
+ this.query = {};
+ this.childSchemas = [];
+
+ this.s = {
+ hooks: new Kareem(),
+ kareemHooks: IS_KAREEM_HOOK
+ };
+
+ this.options = this.defaultOptions(options);
+
+ // build paths
+ if (obj) {
+ this.add(obj);
+ }
+
+ // check if _id's value is a subdocument (gh-2276)
+ var _idSubDoc = obj && obj._id && utils.isObject(obj._id);
+
+ // ensure the documents get an auto _id unless disabled
+ var auto_id = !this.paths['_id'] &&
+ (!this.options.noId && this.options._id) && !_idSubDoc;
+
+ if (auto_id) {
+ obj = {_id: {auto: true}};
+ obj._id[this.options.typeKey] = Schema.ObjectId;
+ this.add(obj);
+ }
+
+ // ensure the documents receive an id getter unless disabled
+ var autoid = !this.paths['id'] &&
+ (!this.options.noVirtualId && this.options.id);
+ if (autoid) {
+ this.virtual('id').get(idGetter);
+ }
+
+ for (var i = 0; i < this._defaultMiddleware.length; ++i) {
+ var m = this._defaultMiddleware[i];
+ this[m.kind](m.hook, !!m.isAsync, m.fn);
+ }
+
+ if (this.options.timestamps) {
+ this.setupTimestamp(this.options.timestamps);
+ }
+}
+
+/*!
+ * Returns this documents _id cast to a string.
+ */
+
+function idGetter() {
+ if (this.$__._id) {
+ return this.$__._id;
+ }
+
+ this.$__._id = this._id == null
+ ? null
+ : String(this._id);
+ return this.$__._id;
+}
+
+/*!
+ * Inherit from EventEmitter.
+ */
+Schema.prototype = Object.create(EventEmitter.prototype);
+Schema.prototype.constructor = Schema;
+Schema.prototype.instanceOfSchema = true;
+
+/**
+ * Default middleware attached to a schema. Cannot be changed.
+ *
+ * This field is used to make sure discriminators don't get multiple copies of
+ * built-in middleware. Declared as a constant because changing this at runtime
+ * may lead to instability with Model.prototype.discriminator().
+ *
+ * @api private
+ * @property _defaultMiddleware
+ */
+Object.defineProperty(Schema.prototype, '_defaultMiddleware', {
+ configurable: false,
+ enumerable: false,
+ writable: false,
+ value: [
+ {
+ kind: 'pre',
+ hook: 'save',
+ fn: function(next, options) {
+ var _this = this;
+ // Nested docs have their own presave
+ if (this.ownerDocument) {
+ return next();
+ }
+
+ var hasValidateBeforeSaveOption = options &&
+ (typeof options === 'object') &&
+ ('validateBeforeSave' in options);
+
+ var shouldValidate;
+ if (hasValidateBeforeSaveOption) {
+ shouldValidate = !!options.validateBeforeSave;
+ } else {
+ shouldValidate = this.schema.options.validateBeforeSave;
+ }
+
+ // Validate
+ if (shouldValidate) {
+ // HACK: use $__original_validate to avoid promises so bluebird doesn't
+ // complain
+ if (this.$__original_validate) {
+ this.$__original_validate({__noPromise: true}, function(error) {
+ return _this.schema.s.hooks.execPost('save:error', _this, [_this], { error: error }, function(error) {
+ next(error);
+ });
+ });
+ } else {
+ this.validate({__noPromise: true}, function(error) {
+ return _this.schema.s.hooks.execPost('save:error', _this, [ _this], { error: error }, function(error) {
+ next(error);
+ });
+ });
+ }
+ } else {
+ next();
+ }
+ }
+ },
+ {
+ kind: 'pre',
+ hook: 'save',
+ isAsync: true,
+ fn: function(next, done) {
+ var _this = this;
+ var subdocs = this.$__getAllSubdocs();
+
+ if (!subdocs.length || this.$__preSavingFromParent) {
+ done();
+ next();
+ return;
+ }
+
+ each(subdocs, function(subdoc, cb) {
+ subdoc.$__preSavingFromParent = true;
+ subdoc.save(function(err) {
+ cb(err);
+ });
+ }, function(error) {
+ for (var i = 0; i < subdocs.length; ++i) {
+ delete subdocs[i].$__preSavingFromParent;
+ }
+ if (error) {
+ return _this.schema.s.hooks.execPost('save:error', _this, [_this], { error: error }, function(error) {
+ done(error);
+ });
+ }
+ next();
+ done();
+ });
+ }
+ },
+ {
+ kind: 'pre',
+ hook: 'validate',
+ isAsync: true,
+ fn: function(next, done) {
+ // Hack to ensure that we always wrap validate() in a promise
+ next();
+ done();
+ }
+ },
+ {
+ kind: 'pre',
+ hook: 'remove',
+ isAsync: true,
+ fn: function(next, done) {
+ if (this.ownerDocument) {
+ done();
+ next();
+ return;
+ }
+
+ var subdocs = this.$__getAllSubdocs();
+
+ if (!subdocs.length || this.$__preSavingFromParent) {
+ done();
+ next();
+ return;
+ }
+
+ each(subdocs, function(subdoc, cb) {
+ subdoc.remove({ noop: true }, function(err) {
+ cb(err);
+ });
+ }, function(error) {
+ if (error) {
+ done(error);
+ return;
+ }
+ next();
+ done();
+ });
+ }
+ }
+ ]
+});
+
+
+/**
+ * The original object passed to the schema constructor
+ *
+ * ####Example:
+ *
+ * var schema = new Schema({ a: String }).add({ b: String });
+ * schema.obj; // { a: String }
+ *
+ * @api public
+ * @property obj
+ */
+
+Schema.prototype.obj;
+
+/**
+ * Schema as flat paths
+ *
+ * ####Example:
+ * {
+ * '_id' : SchemaType,
+ * , 'nested.key' : SchemaType,
+ * }
+ *
+ * @api private
+ * @property paths
+ */
+
+Schema.prototype.paths;
+
+/**
+ * Schema as a tree
+ *
+ * ####Example:
+ * {
+ * '_id' : ObjectId
+ * , 'nested' : {
+ * 'key' : String
+ * }
+ * }
+ *
+ * @api private
+ * @property tree
+ */
+
+Schema.prototype.tree;
+
+/**
+ * Returns default options for this schema, merged with `options`.
+ *
+ * @param {Object} options
+ * @return {Object}
+ * @api private
+ */
+
+Schema.prototype.defaultOptions = function(options) {
+ if (options && options.safe === false) {
+ options.safe = {w: 0};
+ }
+
+ if (options && options.safe && options.safe.w === 0) {
+ // if you turn off safe writes, then versioning goes off as well
+ options.versionKey = false;
+ }
+
+ options = utils.options({
+ strict: true,
+ bufferCommands: true,
+ capped: false, // { size, max, autoIndexId }
+ versionKey: '__v',
+ discriminatorKey: '__t',
+ minimize: true,
+ autoIndex: null,
+ shardKey: null,
+ read: null,
+ validateBeforeSave: true,
+ // the following are only applied at construction time
+ noId: false, // deprecated, use { _id: false }
+ _id: true,
+ noVirtualId: false, // deprecated, use { id: false }
+ id: true,
+ typeKey: 'type',
+ retainKeyOrder: false
+ }, options);
+
+ if (options.read) {
+ options.read = readPref(options.read);
+ }
+
+ return options;
+};
+
+/**
+ * Adds key path / schema type pairs to this schema.
+ *
+ * ####Example:
+ *
+ * var ToySchema = new Schema;
+ * ToySchema.add({ name: 'string', color: 'string', price: 'number' });
+ *
+ * @param {Object} obj
+ * @param {String} prefix
+ * @api public
+ */
+
+Schema.prototype.add = function add(obj, prefix) {
+ prefix = prefix || '';
+ var keys = Object.keys(obj);
+
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+
+ if (obj[key] == null) {
+ throw new TypeError('Invalid value for schema path `' + prefix + key + '`');
+ }
+
+ if (Array.isArray(obj[key]) && obj[key].length === 1 && obj[key][0] == null) {
+ throw new TypeError('Invalid value for schema Array path `' + prefix + key + '`');
+ }
+
+ if (utils.isObject(obj[key]) &&
+ (!obj[key].constructor || utils.getFunctionName(obj[key].constructor) === 'Object') &&
+ (!obj[key][this.options.typeKey] || (this.options.typeKey === 'type' && obj[key].type.type))) {
+ if (Object.keys(obj[key]).length) {
+ // nested object { last: { name: String }}
+ this.nested[prefix + key] = true;
+ this.add(obj[key], prefix + key + '.');
+ } else {
+ if (prefix) {
+ this.nested[prefix.substr(0, prefix.length - 1)] = true;
+ }
+ this.path(prefix + key, obj[key]); // mixed type
+ }
+ } else {
+ if (prefix) {
+ this.nested[prefix.substr(0, prefix.length - 1)] = true;
+ }
+ this.path(prefix + key, obj[key]);
+ }
+ }
+};
+
+/**
+ * Reserved document keys.
+ *
+ * Keys in this object are names that are rejected in schema declarations b/c they conflict with mongoose functionality. Using these key name will throw an error.
+ *
+ * on, emit, _events, db, get, set, init, isNew, errors, schema, options, modelName, collection, _pres, _posts, toObject
+ *
+ * _NOTE:_ Use of these terms as method names is permitted, but play at your own risk, as they may be existing mongoose document methods you are stomping on.
+ *
+ * var schema = new Schema(..);
+ * schema.methods.init = function () {} // potentially breaking
+ */
+
+Schema.reserved = Object.create(null);
+var reserved = Schema.reserved;
+// Core object
+reserved['prototype'] =
+// EventEmitter
+reserved.emit =
+reserved.on =
+reserved.once =
+reserved.listeners =
+reserved.removeListener =
+// document properties and functions
+reserved.collection =
+reserved.db =
+reserved.errors =
+reserved.init =
+reserved.isModified =
+reserved.isNew =
+reserved.get =
+reserved.modelName =
+reserved.save =
+reserved.schema =
+reserved.set =
+reserved.toObject =
+reserved.validate =
+// hooks.js
+reserved._pres = reserved._posts = 1;
+
+/*!
+ * Document keys to print warnings for
+ */
+
+var warnings = {};
+warnings.increment = '`increment` should not be used as a schema path name ' +
+ 'unless you have disabled versioning.';
+
+/**
+ * Gets/sets schema paths.
+ *
+ * Sets a path (if arity 2)
+ * Gets a path (if arity 1)
+ *
+ * ####Example
+ *
+ * schema.path('name') // returns a SchemaType
+ * schema.path('name', Number) // changes the schemaType of `name` to Number
+ *
+ * @param {String} path
+ * @param {Object} constructor
+ * @api public
+ */
+
+Schema.prototype.path = function(path, obj) {
+ if (obj === undefined) {
+ if (this.paths[path]) {
+ return this.paths[path];
+ }
+ if (this.subpaths[path]) {
+ return this.subpaths[path];
+ }
+ if (this.singleNestedPaths[path]) {
+ return this.singleNestedPaths[path];
+ }
+
+ // subpaths?
+ return /\.\d+\.?.*$/.test(path)
+ ? getPositionalPath(this, path)
+ : undefined;
+ }
+
+ // some path names conflict with document methods
+ if (reserved[path]) {
+ throw new Error('`' + path + '` may not be used as a schema pathname');
+ }
+
+ if (warnings[path]) {
+ console.log('WARN: ' + warnings[path]);
+ }
+
+ // update the tree
+ var subpaths = path.split(/\./),
+ last = subpaths.pop(),
+ branch = this.tree;
+
+ subpaths.forEach(function(sub, i) {
+ if (!branch[sub]) {
+ branch[sub] = {};
+ }
+ if (typeof branch[sub] !== 'object') {
+ var msg = 'Cannot set nested path `' + path + '`. '
+ + 'Parent path `'
+ + subpaths.slice(0, i).concat([sub]).join('.')
+ + '` already set to type ' + branch[sub].name
+ + '.';
+ throw new Error(msg);
+ }
+ branch = branch[sub];
+ });
+
+ branch[last] = utils.clone(obj);
+
+ this.paths[path] = Schema.interpretAsType(path, obj, this.options);
+
+ if (this.paths[path].$isSingleNested) {
+ for (var key in this.paths[path].schema.paths) {
+ this.singleNestedPaths[path + '.' + key] =
+ this.paths[path].schema.paths[key];
+ }
+ for (key in this.paths[path].schema.singleNestedPaths) {
+ this.singleNestedPaths[path + '.' + key] =
+ this.paths[path].schema.singleNestedPaths[key];
+ }
+
+ this.childSchemas.push(this.paths[path].schema);
+ } else if (this.paths[path].$isMongooseDocumentArray) {
+ this.childSchemas.push(this.paths[path].schema);
+ }
+ return this;
+};
+
+/**
+ * Converts type arguments into Mongoose Types.
+ *
+ * @param {String} path
+ * @param {Object} obj constructor
+ * @api private
+ */
+
+Schema.interpretAsType = function(path, obj, options) {
+ if (obj.constructor) {
+ var constructorName = utils.getFunctionName(obj.constructor);
+ if (constructorName !== 'Object') {
+ var oldObj = obj;
+ obj = {};
+ obj[options.typeKey] = oldObj;
+ }
+ }
+
+ // Get the type making sure to allow keys named "type"
+ // and default to mixed if not specified.
+ // { type: { type: String, default: 'freshcut' } }
+ var type = obj[options.typeKey] && (options.typeKey !== 'type' || !obj.type.type)
+ ? obj[options.typeKey]
+ : {};
+
+ if (utils.getFunctionName(type.constructor) === 'Object' || type === 'mixed') {
+ return new MongooseTypes.Mixed(path, obj);
+ }
+
+ if (Array.isArray(type) || Array === type || type === 'array') {
+ // if it was specified through { type } look for `cast`
+ var cast = (Array === type || type === 'array')
+ ? obj.cast
+ : type[0];
+
+ if (cast && cast.instanceOfSchema) {
+ return new MongooseTypes.DocumentArray(path, cast, obj);
+ }
+
+ if (Array.isArray(cast)) {
+ return new MongooseTypes.Array(path, Schema.interpretAsType(path, cast, options), obj);
+ }
+
+ if (typeof cast === 'string') {
+ cast = MongooseTypes[cast.charAt(0).toUpperCase() + cast.substring(1)];
+ } else if (cast && (!cast[options.typeKey] || (options.typeKey === 'type' && cast.type.type))
+ && utils.getFunctionName(cast.constructor) === 'Object') {
+ if (Object.keys(cast).length) {
+ // The `minimize` and `typeKey` options propagate to child schemas
+ // declared inline, like `{ arr: [{ val: { $type: String } }] }`.
+ // See gh-3560
+ var childSchemaOptions = {minimize: options.minimize};
+ if (options.typeKey) {
+ childSchemaOptions.typeKey = options.typeKey;
+ }
+ var childSchema = new Schema(cast, childSchemaOptions);
+ childSchema.$implicitlyCreated = true;
+ return new MongooseTypes.DocumentArray(path, childSchema, obj);
+ } else {
+ // Special case: empty object becomes mixed
+ return new MongooseTypes.Array(path, MongooseTypes.Mixed, obj);
+ }
+ }
+
+ if (cast) {
+ type = cast[options.typeKey] && (options.typeKey !== 'type' || !cast.type.type)
+ ? cast[options.typeKey]
+ : cast;
+
+ name = typeof type === 'string'
+ ? type
+ : type.schemaName || utils.getFunctionName(type);
+
+ if (!(name in MongooseTypes)) {
+ throw new TypeError('Undefined type `' + name + '` at array `' + path +
+ '`');
+ }
+ }
+
+ return new MongooseTypes.Array(path, cast || MongooseTypes.Mixed, obj, options);
+ }
+
+ if (type && type.instanceOfSchema) {
+ return new MongooseTypes.Embedded(type, path, obj);
+ }
+
+ var name;
+ if (Buffer.isBuffer(type)) {
+ name = 'Buffer';
+ } else {
+ name = typeof type === 'string'
+ ? type
+ // If not string, `type` is a function. Outside of IE, function.name
+ // gives you the function name. In IE, you need to compute it
+ : type.schemaName || utils.getFunctionName(type);
+ }
+
+ if (name) {
+ name = name.charAt(0).toUpperCase() + name.substring(1);
+ }
+
+ if (undefined == MongooseTypes[name]) {
+ throw new TypeError('Undefined type `' + name + '` at `' + path +
+ '`\n Did you try nesting Schemas? ' +
+ 'You can only nest using refs or arrays.');
+ }
+
+ return new MongooseTypes[name](path, obj);
+};
+
+/**
+ * Iterates the schemas paths similar to Array#forEach.
+ *
+ * The callback is passed the pathname and schemaType as arguments on each iteration.
+ *
+ * @param {Function} fn callback function
+ * @return {Schema} this
+ * @api public
+ */
+
+Schema.prototype.eachPath = function(fn) {
+ var keys = Object.keys(this.paths),
+ len = keys.length;
+
+ for (var i = 0; i < len; ++i) {
+ fn(keys[i], this.paths[keys[i]]);
+ }
+
+ return this;
+};
+
+/**
+ * Returns an Array of path strings that are required by this schema.
+ *
+ * @api public
+ * @param {Boolean} invalidate refresh the cache
+ * @return {Array}
+ */
+
+Schema.prototype.requiredPaths = function requiredPaths(invalidate) {
+ if (this._requiredpaths && !invalidate) {
+ return this._requiredpaths;
+ }
+
+ var paths = Object.keys(this.paths),
+ i = paths.length,
+ ret = [];
+
+ while (i--) {
+ var path = paths[i];
+ if (this.paths[path].isRequired) {
+ ret.push(path);
+ }
+ }
+ this._requiredpaths = ret;
+ return this._requiredpaths;
+};
+
+/**
+ * Returns indexes from fields and schema-level indexes (cached).
+ *
+ * @api private
+ * @return {Array}
+ */
+
+Schema.prototype.indexedPaths = function indexedPaths() {
+ if (this._indexedpaths) {
+ return this._indexedpaths;
+ }
+ this._indexedpaths = this.indexes();
+ return this._indexedpaths;
+};
+
+/**
+ * Returns the pathType of `path` for this schema.
+ *
+ * Given a path, returns whether it is a real, virtual, nested, or ad-hoc/undefined path.
+ *
+ * @param {String} path
+ * @return {String}
+ * @api public
+ */
+
+Schema.prototype.pathType = function(path) {
+ if (path in this.paths) {
+ return 'real';
+ }
+ if (path in this.virtuals) {
+ return 'virtual';
+ }
+ if (path in this.nested) {
+ return 'nested';
+ }
+ if (path in this.subpaths) {
+ return 'real';
+ }
+ if (path in this.singleNestedPaths) {
+ return 'real';
+ }
+
+ if (/\.\d+\.|\.\d+$/.test(path)) {
+ return getPositionalPathType(this, path);
+ }
+ return 'adhocOrUndefined';
+};
+
+/**
+ * Returns true iff this path is a child of a mixed schema.
+ *
+ * @param {String} path
+ * @return {Boolean}
+ * @api private
+ */
+
+Schema.prototype.hasMixedParent = function(path) {
+ var subpaths = path.split(/\./g);
+ path = '';
+ for (var i = 0; i < subpaths.length; ++i) {
+ path = i > 0 ? path + '.' + subpaths[i] : subpaths[i];
+ if (path in this.paths &&
+ this.paths[path] instanceof MongooseTypes.Mixed) {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+/**
+ * Setup updatedAt and createdAt timestamps to documents if enabled
+ *
+ * @param {Boolean|Object} timestamps timestamps options
+ * @api private
+ */
+Schema.prototype.setupTimestamp = function(timestamps) {
+ if (timestamps) {
+ var createdAt = timestamps.createdAt || 'createdAt';
+ var updatedAt = timestamps.updatedAt || 'updatedAt';
+ var schemaAdditions = {};
+
+ schemaAdditions[updatedAt] = Date;
+
+ if (!this.paths[createdAt]) {
+ schemaAdditions[createdAt] = Date;
+ }
+
+ this.add(schemaAdditions);
+
+ this.pre('save', function(next) {
+ var defaultTimestamp = new Date();
+ var auto_id = this._id && this._id.auto;
+
+ if (!this[createdAt] && this.isSelected(createdAt)) {
+ this[createdAt] = auto_id ? this._id.getTimestamp() : defaultTimestamp;
+ }
+
+ if (this.isNew || this.isModified()) {
+ this[updatedAt] = this.isNew ? this[createdAt] : defaultTimestamp;
+ }
+
+ next();
+ });
+
+ var genUpdates = function(overwrite) {
+ var now = new Date();
+ var updates = {};
+ if (overwrite) {
+ updates[updatedAt] = now;
+ updates[createdAt] = now;
+ return updates;
+ }
+ updates = { $set: {}, $setOnInsert: {} };
+ updates.$set[updatedAt] = now;
+ updates.$setOnInsert[createdAt] = now;
+
+ return updates;
+ };
+
+ this.methods.initializeTimestamps = function() {
+ if (!this[createdAt]) {
+ this[createdAt] = new Date();
+ }
+ if (!this[updatedAt]) {
+ this[updatedAt] = new Date();
+ }
+ return this;
+ };
+
+ this.pre('findOneAndUpdate', function(next) {
+ var overwrite = this.options.overwrite;
+ this.findOneAndUpdate({}, genUpdates(overwrite), { overwrite: overwrite });
+ applyTimestampsToChildren(this);
+ next();
+ });
+
+ this.pre('update', function(next) {
+ var overwrite = this.options.overwrite;
+ this.update({}, genUpdates(overwrite), { overwrite: overwrite });
+ applyTimestampsToChildren(this);
+ next();
+ });
+ }
+};
+
+/*!
+ * ignore
+ */
+
+function applyTimestampsToChildren(query) {
+ var now = new Date();
+ var update = query.getUpdate();
+ var keys = Object.keys(update);
+ var key;
+ var schema = query.model.schema;
+ var len;
+ var createdAt;
+ var updatedAt;
+ var timestamps;
+ var path;
+
+ var hasDollarKey = keys.length && keys[0].charAt(0) === '$';
+
+ if (hasDollarKey) {
+ if (update.$push) {
+ for (key in update.$push) {
+ var $path = schema.path(key);
+ if (update.$push[key] &&
+ $path &&
+ $path.$isMongooseDocumentArray &&
+ $path.schema.options.timestamps) {
+ timestamps = $path.schema.options.timestamps;
+ createdAt = timestamps.createdAt || 'createdAt';
+ updatedAt = timestamps.updatedAt || 'updatedAt';
+ update.$push[key][updatedAt] = now;
+ update.$push[key][createdAt] = now;
+ }
+ }
+ }
+ if (update.$set) {
+ for (key in update.$set) {
+ path = schema.path(key);
+ if (!path) {
+ continue;
+ }
+ if (Array.isArray(update.$set[key]) && path.$isMongooseDocumentArray) {
+ len = update.$set[key].length;
+ timestamps = schema.path(key).schema.options.timestamps;
+ if (timestamps) {
+ createdAt = timestamps.createdAt || 'createdAt';
+ updatedAt = timestamps.updatedAt || 'updatedAt';
+ for (var i = 0; i < len; ++i) {
+ update.$set[key][i][updatedAt] = now;
+ update.$set[key][i][createdAt] = now;
+ }
+ }
+ } else if (update.$set[key] && path.$isSingleNested) {
+ timestamps = schema.path(key).schema.options.timestamps;
+ if (timestamps) {
+ createdAt = timestamps.createdAt || 'createdAt';
+ updatedAt = timestamps.updatedAt || 'updatedAt';
+ update.$set[key][updatedAt] = now;
+ update.$set[key][createdAt] = now;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*!
+ * ignore
+ */
+
+function getPositionalPathType(self, path) {
+ var subpaths = path.split(/\.(\d+)\.|\.(\d+)$/).filter(Boolean);
+ if (subpaths.length < 2) {
+ return self.paths[subpaths[0]];
+ }
+
+ var val = self.path(subpaths[0]);
+ var isNested = false;
+ if (!val) {
+ return val;
+ }
+
+ var last = subpaths.length - 1,
+ subpath,
+ i = 1;
+
+ for (; i < subpaths.length; ++i) {
+ isNested = false;
+ subpath = subpaths[i];
+
+ if (i === last && val && !/\D/.test(subpath)) {
+ if (val.$isMongooseDocumentArray) {
+ var oldVal = val;
+ val = new SchemaType(subpath);
+ val.cast = function(value, doc, init) {
+ return oldVal.cast(value, doc, init)[0];
+ };
+ val.caster = oldVal.caster;
+ val.schema = oldVal.schema;
+ } else if (val instanceof MongooseTypes.Array) {
+ // StringSchema, NumberSchema, etc
+ val = val.caster;
+ } else {
+ val = undefined;
+ }
+ break;
+ }
+
+ // ignore if its just a position segment: path.0.subpath
+ if (!/\D/.test(subpath)) {
+ continue;
+ }
+
+ if (!(val && val.schema)) {
+ val = undefined;
+ break;
+ }
+
+ var type = val.schema.pathType(subpath);
+ isNested = (type === 'nested');
+ val = val.schema.path(subpath);
+ }
+
+ self.subpaths[path] = val;
+ if (val) {
+ return 'real';
+ }
+ if (isNested) {
+ return 'nested';
+ }
+ return 'adhocOrUndefined';
+}
+
+
+/*!
+ * ignore
+ */
+
+function getPositionalPath(self, path) {
+ getPositionalPathType(self, path);
+ return self.subpaths[path];
+}
+
+/**
+ * Adds a method call to the queue.
+ *
+ * @param {String} name name of the document method to call later
+ * @param {Array} args arguments to pass to the method
+ * @api public
+ */
+
+Schema.prototype.queue = function(name, args) {
+ this.callQueue.push([name, args]);
+ return this;
+};
+
+/**
+ * Defines a pre hook for the document.
+ *
+ * ####Example
+ *
+ * var toySchema = new Schema(..);
+ *
+ * toySchema.pre('save', function (next) {
+ * if (!this.created) this.created = new Date;
+ * next();
+ * })
+ *
+ * toySchema.pre('validate', function (next) {
+ * if (this.name !== 'Woody') this.name = 'Woody';
+ * next();
+ * })
+ *
+ * @param {String} method
+ * @param {Function} callback
+ * @see hooks.js https://github.com/bnoguchi/hooks-js/tree/31ec571cef0332e21121ee7157e0cf9728572cc3
+ * @api public
+ */
+
+Schema.prototype.pre = function() {
+ var name = arguments[0];
+ if (IS_KAREEM_HOOK[name]) {
+ this.s.hooks.pre.apply(this.s.hooks, arguments);
+ return this;
+ }
+ return this.queue('pre', arguments);
+};
+
+/**
+ * Defines a post hook for the document
+ *
+ * var schema = new Schema(..);
+ * schema.post('save', function (doc) {
+ * console.log('this fired after a document was saved');
+ * });
+ *
+ * shema.post('find', function(docs) {
+ * console.log('this fired after you run a find query');
+ * });
+ *
+ * var Model = mongoose.model('Model', schema);
+ *
+ * var m = new Model(..);
+ * m.save(function(err) {
+ * console.log('this fires after the `post` hook');
+ * });
+ *
+ * m.find(function(err, docs) {
+ * console.log('this fires after the post find hook');
+ * });
+ *
+ * @param {String} method name of the method to hook
+ * @param {Function} fn callback
+ * @see middleware http://mongoosejs.com/docs/middleware.html
+ * @see hooks.js https://www.npmjs.com/package/hooks-fixed
+ * @see kareem http://npmjs.org/package/kareem
+ * @api public
+ */
+
+Schema.prototype.post = function(method, fn) {
+ if (IS_KAREEM_HOOK[method]) {
+ this.s.hooks.post.apply(this.s.hooks, arguments);
+ return this;
+ }
+ // assuming that all callbacks with arity < 2 are synchronous post hooks
+ if (fn.length < 2) {
+ return this.queue('on', [arguments[0], function(doc) {
+ return fn.call(doc, doc);
+ }]);
+ }
+
+ if (fn.length === 3) {
+ this.s.hooks.post(method + ':error', fn);
+ return this;
+ }
+
+ return this.queue('post', [arguments[0], function(next) {
+ // wrap original function so that the callback goes last,
+ // for compatibility with old code that is using synchronous post hooks
+ var _this = this;
+ var args = Array.prototype.slice.call(arguments, 1);
+ fn.call(this, this, function(err) {
+ return next.apply(_this, [err].concat(args));
+ });
+ }]);
+};
+
+/**
+ * Registers a plugin for this schema.
+ *
+ * @param {Function} plugin callback
+ * @param {Object} [opts]
+ * @see plugins
+ * @api public
+ */
+
+Schema.prototype.plugin = function(fn, opts) {
+ fn(this, opts);
+ return this;
+};
+
+/**
+ * Adds an instance method to documents constructed from Models compiled from this schema.
+ *
+ * ####Example
+ *
+ * var schema = kittySchema = new Schema(..);
+ *
+ * schema.method('meow', function () {
+ * console.log('meeeeeoooooooooooow');
+ * })
+ *
+ * var Kitty = mongoose.model('Kitty', schema);
+ *
+ * var fizz = new Kitty;
+ * fizz.meow(); // meeeeeooooooooooooow
+ *
+ * If a hash of name/fn pairs is passed as the only argument, each name/fn pair will be added as methods.
+ *
+ * schema.method({
+ * purr: function () {}
+ * , scratch: function () {}
+ * });
+ *
+ * // later
+ * fizz.purr();
+ * fizz.scratch();
+ *
+ * @param {String|Object} method name
+ * @param {Function} [fn]
+ * @api public
+ */
+
+Schema.prototype.method = function(name, fn) {
+ if (typeof name !== 'string') {
+ for (var i in name) {
+ this.methods[i] = name[i];
+ }
+ } else {
+ this.methods[name] = fn;
+ }
+ return this;
+};
+
+/**
+ * Adds static "class" methods to Models compiled from this schema.
+ *
+ * ####Example
+ *
+ * var schema = new Schema(..);
+ * schema.static('findByName', function (name, callback) {
+ * return this.find({ name: name }, callback);
+ * });
+ *
+ * var Drink = mongoose.model('Drink', schema);
+ * Drink.findByName('sanpellegrino', function (err, drinks) {
+ * //
+ * });
+ *
+ * If a hash of name/fn pairs is passed as the only argument, each name/fn pair will be added as statics.
+ *
+ * @param {String|Object} name
+ * @param {Function} [fn]
+ * @api public
+ */
+
+Schema.prototype.static = function(name, fn) {
+ if (typeof name !== 'string') {
+ for (var i in name) {
+ this.statics[i] = name[i];
+ }
+ } else {
+ this.statics[name] = fn;
+ }
+ return this;
+};
+
+/**
+ * Defines an index (most likely compound) for this schema.
+ *
+ * ####Example
+ *
+ * schema.index({ first: 1, last: -1 })
+ *
+ * @param {Object} fields
+ * @param {Object} [options] Options to pass to [MongoDB driver's `createIndex()` function](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#createIndex)
+ * @param {String} [options.expires=null] Mongoose-specific syntactic sugar, uses [ms](https://www.npmjs.com/package/ms) to convert `expires` option into seconds for the `expireAfterSeconds` in the above link.
+ * @api public
+ */
+
+Schema.prototype.index = function(fields, options) {
+ options || (options = {});
+
+ if (options.expires) {
+ utils.expires(options);
+ }
+
+ this._indexes.push([fields, options]);
+ return this;
+};
+
+/**
+ * Sets/gets a schema option.
+ *
+ * ####Example
+ *
+ * schema.set('strict'); // 'true' by default
+ * schema.set('strict', false); // Sets 'strict' to false
+ * schema.set('strict'); // 'false'
+ *
+ * @param {String} key option name
+ * @param {Object} [value] if not passed, the current option value is returned
+ * @see Schema ./
+ * @api public
+ */
+
+Schema.prototype.set = function(key, value, _tags) {
+ if (arguments.length === 1) {
+ return this.options[key];
+ }
+
+ switch (key) {
+ case 'read':
+ this.options[key] = readPref(value, _tags);
+ break;
+ case 'safe':
+ this.options[key] = value === false
+ ? {w: 0}
+ : value;
+ break;
+ case 'timestamps':
+ this.setupTimestamp(value);
+ this.options[key] = value;
+ break;
+ default:
+ this.options[key] = value;
+ }
+
+ return this;
+};
+
+/**
+ * Gets a schema option.
+ *
+ * @param {String} key option name
+ * @api public
+ */
+
+Schema.prototype.get = function(key) {
+ return this.options[key];
+};
+
+/**
+ * The allowed index types
+ *
+ * @static indexTypes
+ * @receiver Schema
+ * @api public
+ */
+
+var indexTypes = '2d 2dsphere hashed text'.split(' ');
+
+Object.defineProperty(Schema, 'indexTypes', {
+ get: function() {
+ return indexTypes;
+ },
+ set: function() {
+ throw new Error('Cannot overwrite Schema.indexTypes');
+ }
+});
+
+/**
+ * Compiles indexes from fields and schema-level indexes
+ *
+ * @api public
+ */
+
+Schema.prototype.indexes = function() {
+ 'use strict';
+
+ var indexes = [];
+ var seenPrefix = {};
+
+ var collectIndexes = function(schema, prefix) {
+ if (seenPrefix[prefix]) {
+ return;
+ }
+ seenPrefix[prefix] = true;
+
+ prefix = prefix || '';
+ var key, path, index, field, isObject, options, type;
+ var keys = Object.keys(schema.paths);
+
+ for (var i = 0; i < keys.length; ++i) {
+ key = keys[i];
+ path = schema.paths[key];
+
+ if ((path instanceof MongooseTypes.DocumentArray) || path.$isSingleNested) {
+ collectIndexes(path.schema, key + '.');
+ } else {
+ index = path._index;
+
+ if (index !== false && index !== null && index !== undefined) {
+ field = {};
+ isObject = utils.isObject(index);
+ options = isObject ? index : {};
+ type = typeof index === 'string' ? index :
+ isObject ? index.type :
+ false;
+
+ if (type && ~Schema.indexTypes.indexOf(type)) {
+ field[prefix + key] = type;
+ } else if (options.text) {
+ field[prefix + key] = 'text';
+ delete options.text;
+ } else {
+ field[prefix + key] = 1;
+ }
+
+ delete options.type;
+ if (!('background' in options)) {
+ options.background = true;
+ }
+
+ indexes.push([field, options]);
+ }
+ }
+ }
+
+ if (prefix) {
+ fixSubIndexPaths(schema, prefix);
+ } else {
+ schema._indexes.forEach(function(index) {
+ if (!('background' in index[1])) {
+ index[1].background = true;
+ }
+ });
+ indexes = indexes.concat(schema._indexes);
+ }
+ };
+
+ collectIndexes(this);
+ return indexes;
+
+ /*!
+ * Checks for indexes added to subdocs using Schema.index().
+ * These indexes need their paths prefixed properly.
+ *
+ * schema._indexes = [ [indexObj, options], [indexObj, options] ..]
+ */
+
+ function fixSubIndexPaths(schema, prefix) {
+ var subindexes = schema._indexes,
+ len = subindexes.length,
+ indexObj,
+ newindex,
+ klen,
+ keys,
+ key,
+ i = 0,
+ j;
+
+ for (i = 0; i < len; ++i) {
+ indexObj = subindexes[i][0];
+ keys = Object.keys(indexObj);
+ klen = keys.length;
+ newindex = {};
+
+ // use forward iteration, order matters
+ for (j = 0; j < klen; ++j) {
+ key = keys[j];
+ newindex[prefix + key] = indexObj[key];
+ }
+
+ indexes.push([newindex, subindexes[i][1]]);
+ }
+ }
+};
+
+/**
+ * Creates a virtual type with the given name.
+ *
+ * @param {String} name
+ * @param {Object} [options]
+ * @return {VirtualType}
+ */
+
+Schema.prototype.virtual = function(name, options) {
+ if (options && options.ref) {
+ if (!options.localField) {
+ throw new Error('Reference virtuals require `localField` option');
+ }
+
+ if (!options.foreignField) {
+ throw new Error('Reference virtuals require `foreignField` option');
+ }
+
+ this.pre('init', function(next, obj) {
+ if (name in obj) {
+ if (!this.$$populatedVirtuals) {
+ this.$$populatedVirtuals = {};
+ }
+
+ if (options.justOne) {
+ this.$$populatedVirtuals[name] = Array.isArray(obj[name]) ?
+ obj[name][0] :
+ obj[name];
+ } else {
+ this.$$populatedVirtuals[name] = Array.isArray(obj[name]) ?
+ obj[name] :
+ obj[name] == null ? [] : [obj[name]];
+ }
+
+ delete obj[name];
+ }
+ if (this.ownerDocument) {
+ next();
+ return obj;
+ } else {
+ next();
+ }
+ });
+
+ var virtual = this.virtual(name);
+ virtual.options = options;
+ return virtual.
+ get(function() {
+ if (!this.$$populatedVirtuals) {
+ this.$$populatedVirtuals = {};
+ }
+ if (name in this.$$populatedVirtuals) {
+ return this.$$populatedVirtuals[name];
+ }
+ return null;
+ }).
+ set(function(v) {
+ if (!this.$$populatedVirtuals) {
+ this.$$populatedVirtuals = {};
+ }
+ this.$$populatedVirtuals[name] = v;
+ });
+ }
+
+ var virtuals = this.virtuals;
+ var parts = name.split('.');
+
+ if (this.pathType(name) === 'real') {
+ throw new Error('Virtual path "' + name + '"' +
+ ' conflicts with a real path in the schema');
+ }
+
+ virtuals[name] = parts.reduce(function(mem, part, i) {
+ mem[part] || (mem[part] = (i === parts.length - 1)
+ ? new VirtualType(options, name)
+ : {});
+ return mem[part];
+ }, this.tree);
+
+ return virtuals[name];
+};
+
+/*!
+ * ignore
+ */
+
+Schema.prototype._getVirtual = function(name) {
+ return _getVirtual(this, name);
+};
+
+/*!
+ * ignore
+ */
+
+function _getVirtual(schema, name) {
+ var parts = name.split('.');
+ var cur = '';
+ var nestedSchemaPath = '';
+ for (var i = 0; i < parts.length; ++i) {
+ cur += (cur.length > 0 ? '.' : '') + parts[i];
+ if (schema.virtuals[cur]) {
+ if (i === parts.length - 1) {
+ schema.virtuals[cur].$nestedSchemaPath = nestedSchemaPath;
+ return schema.virtuals[cur];
+ }
+ continue;
+ } else if (schema.paths[cur] && schema.paths[cur].schema) {
+ schema = schema.paths[cur].schema;
+ nestedSchemaPath += (nestedSchemaPath.length > 0 ? '.' : '') + cur;
+ cur = '';
+ } else {
+ return null;
+ }
+ }
+}
+
+/**
+ * Returns the virtual type with the given `name`.
+ *
+ * @param {String} name
+ * @return {VirtualType}
+ */
+
+Schema.prototype.virtualpath = function(name) {
+ return this.virtuals[name];
+};
+
+/**
+ * Removes the given `path` (or [`paths`]).
+ *
+ * @param {String|Array} path
+ *
+ * @api public
+ */
+Schema.prototype.remove = function(path) {
+ if (typeof path === 'string') {
+ path = [path];
+ }
+ if (Array.isArray(path)) {
+ path.forEach(function(name) {
+ if (this.path(name)) {
+ delete this.paths[name];
+
+ var pieces = name.split('.');
+ var last = pieces.pop();
+ var branch = this.tree;
+ for (var i = 0; i < pieces.length; ++i) {
+ branch = branch[pieces[i]];
+ }
+ delete branch[last];
+ }
+ }, this);
+ }
+};
+
+/**
+ * Loads an ES6 class into a schema. Maps setters + getters, static methods, and instance methods to schema virtuals, statics, and methods.
+ *
+ * @param {Function} model
+ */
+Schema.prototype.loadClass = function(model, virtualsOnly) {
+ if (model === Object.prototype || model === Function.prototype) {
+ return this;
+ }
+
+ // Add static methods
+ if (!virtualsOnly) {
+ Object.getOwnPropertyNames(model).forEach(function(name) {
+ if (name.match(/^(length|name|prototype)$/)) {
+ return;
+ }
+ var method = Object.getOwnPropertyDescriptor(model, name);
+ if (typeof method.value === 'function') this.static(name, method.value);
+ }, this);
+ }
+
+ // Add methods and virtuals
+ Object.getOwnPropertyNames(model.prototype).forEach(function(name) {
+ if (name.match(/^(constructor)$/)) {
+ return;
+ }
+ var method = Object.getOwnPropertyDescriptor(model.prototype, name);
+ if (!virtualsOnly) {
+ if (typeof method.value === 'function') {
+ this.method(name, method.value);
+ }
+ }
+ if (typeof method.get === 'function') {
+ this.virtual(name).get(method.get);
+ }
+ if (typeof method.set === 'function') {
+ this.virtual(name).set(method.set);
+ }
+ }, this);
+
+ return (this.loadClass(Object.getPrototypeOf(model)));
+};
+
+/*!
+ * ignore
+ */
+
+Schema.prototype._getSchema = function(path) {
+ var _this = this;
+ var pathschema = _this.path(path);
+ var resultPath = [];
+
+ if (pathschema) {
+ pathschema.$fullPath = path;
+ return pathschema;
+ }
+
+ function search(parts, schema) {
+ var p = parts.length + 1,
+ foundschema,
+ trypath;
+
+ while (p--) {
+ trypath = parts.slice(0, p).join('.');
+ foundschema = schema.path(trypath);
+ if (foundschema) {
+ resultPath.push(trypath);
+
+ if (foundschema.caster) {
+ // array of Mixed?
+ if (foundschema.caster instanceof MongooseTypes.Mixed) {
+ foundschema.caster.$fullPath = resultPath.join('.');
+ return foundschema.caster;
+ }
+
+ // Now that we found the array, we need to check if there
+ // are remaining document paths to look up for casting.
+ // Also we need to handle array.$.path since schema.path
+ // doesn't work for that.
+ // If there is no foundschema.schema we are dealing with
+ // a path like array.$
+ if (p !== parts.length && foundschema.schema) {
+ if (parts[p] === '$') {
+ // comments.$.comments.$.title
+ return search(parts.slice(p + 1), foundschema.schema);
+ }
+ // this is the last path of the selector
+ return search(parts.slice(p), foundschema.schema);
+ }
+ }
+
+ foundschema.$fullPath = resultPath.join('.');
+
+ return foundschema;
+ }
+ }
+ }
+
+ // look for arrays
+ return search(path.split('.'), _this);
+};
+
+/*!
+ * ignore
+ */
+
+Schema.prototype._getPathType = function(path) {
+ var _this = this;
+ var pathschema = _this.path(path);
+
+ if (pathschema) {
+ return 'real';
+ }
+
+ function search(parts, schema) {
+ var p = parts.length + 1,
+ foundschema,
+ trypath;
+
+ while (p--) {
+ trypath = parts.slice(0, p).join('.');
+ foundschema = schema.path(trypath);
+ if (foundschema) {
+ if (foundschema.caster) {
+ // array of Mixed?
+ if (foundschema.caster instanceof MongooseTypes.Mixed) {
+ return { schema: foundschema, pathType: 'mixed' };
+ }
+
+ // Now that we found the array, we need to check if there
+ // are remaining document paths to look up for casting.
+ // Also we need to handle array.$.path since schema.path
+ // doesn't work for that.
+ // If there is no foundschema.schema we are dealing with
+ // a path like array.$
+ if (p !== parts.length && foundschema.schema) {
+ if (parts[p] === '$') {
+ if (p === parts.length - 1) {
+ return { schema: foundschema, pathType: 'nested' };
+ }
+ // comments.$.comments.$.title
+ return search(parts.slice(p + 1), foundschema.schema);
+ }
+ // this is the last path of the selector
+ return search(parts.slice(p), foundschema.schema);
+ }
+ return {
+ schema: foundschema,
+ pathType: foundschema.$isSingleNested ? 'nested' : 'array'
+ };
+ }
+ return { schema: foundschema, pathType: 'real' };
+ } else if (p === parts.length && schema.nested[trypath]) {
+ return { schema: schema, pathType: 'nested' };
+ }
+ }
+ return { schema: foundschema || schema, pathType: 'undefined' };
+ }
+
+ // look for arrays
+ return search(path.split('.'), _this);
+};
+
+
+/*!
+ * Module exports.
+ */
+
+module.exports = exports = Schema;
+
+// require down here because of reference issues
+
+/**
+ * The various built-in Mongoose Schema Types.
+ *
+ * ####Example:
+ *
+ * var mongoose = require('mongoose');
+ * var ObjectId = mongoose.Schema.Types.ObjectId;
+ *
+ * ####Types:
+ *
+ * - [String](#schema-string-js)
+ * - [Number](#schema-number-js)
+ * - [Boolean](#schema-boolean-js) | Bool
+ * - [Array](#schema-array-js)
+ * - [Buffer](#schema-buffer-js)
+ * - [Date](#schema-date-js)
+ * - [ObjectId](#schema-objectid-js) | Oid
+ * - [Mixed](#schema-mixed-js)
+ *
+ * Using this exposed access to the `Mixed` SchemaType, we can use them in our schema.
+ *
+ * var Mixed = mongoose.Schema.Types.Mixed;
+ * new mongoose.Schema({ _user: Mixed })
+ *
+ * @api public
+ */
+
+Schema.Types = MongooseTypes = require('./schema/index');
+
+/*!
+ * ignore
+ */
+
+exports.ObjectId = MongooseTypes.ObjectId;