1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768 |
- /*!
- * 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;
|