/*!
|
|
* Module dependencies.
|
|
*/
|
|
|
|
var EventEmitter = require('events').EventEmitter
|
|
, VirtualType = require('./virtualtype')
|
|
, utils = require('./utils')
|
|
, NamedScope
|
|
, Query
|
|
, Types
|
|
|
|
/**
|
|
* 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 true
|
|
* - [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
|
|
* - [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
|
|
* - [versionKey](/docs/guide.html#versionKey): bool - defaults to "__v"
|
|
*
|
|
* ####Note:
|
|
*
|
|
* _When nesting schemas, (`children` in the example above), always declare the child schema first before passing it into is parent._
|
|
*
|
|
* @param {Object} definition
|
|
* @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.paths = {};
|
|
this.subpaths = {};
|
|
this.virtuals = {};
|
|
this.nested = {};
|
|
this.inherits = {};
|
|
this.callQueue = [];
|
|
this._indexes = [];
|
|
this.methods = {};
|
|
this.statics = {};
|
|
this.tree = {};
|
|
this._requiredpaths = undefined;
|
|
|
|
this.options = this.defaultOptions(options);
|
|
|
|
// build paths
|
|
if (obj) {
|
|
this.add(obj);
|
|
}
|
|
|
|
// ensure the documents get an auto _id unless disabled
|
|
var auto_id = !this.paths['_id'] && (!this.options.noId && this.options._id);
|
|
if (auto_id) {
|
|
this.add({ _id: {type: Schema.ObjectId, auto: true} });
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Returns this documents _id cast to a string.
|
|
*/
|
|
|
|
function idGetter () {
|
|
if (this.$__._id) {
|
|
return this.$__._id;
|
|
}
|
|
|
|
return this.$__._id = null == this._id
|
|
? null
|
|
: String(this._id);
|
|
}
|
|
|
|
/*!
|
|
* Inherit from EventEmitter.
|
|
*/
|
|
|
|
Schema.prototype.__proto__ = EventEmitter.prototype;
|
|
|
|
/**
|
|
* 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 && false === options.safe) {
|
|
options.safe = { w: 0 };
|
|
}
|
|
|
|
options = utils.options({
|
|
strict: true
|
|
, bufferCommands: true
|
|
, capped: false // { size, max, autoIndexId }
|
|
, versionKey: '__v'
|
|
, minimize: true
|
|
, autoIndex: true
|
|
, shardKey: null
|
|
, read: null
|
|
// the following are only applied at construction time
|
|
, noId: false // deprecated, use { _id: false }
|
|
, _id: true
|
|
, noVirtualId: false // deprecated, use { id: false }
|
|
, id: true
|
|
}, options);
|
|
|
|
if (options.read)
|
|
options.read = utils.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 (null == obj[key]) {
|
|
throw new TypeError('Invalid value for schema path `'+ prefix + key +'`');
|
|
}
|
|
|
|
if (utils.isObject(obj[key]) && (!obj[key].constructor || 'Object' == obj[key].constructor.name) && (!obj[key].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 {
|
|
this.path(prefix + key, obj[key]); // mixed type
|
|
}
|
|
} else {
|
|
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, 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;
|
|
reserved.on =
|
|
reserved.db =
|
|
reserved.init =
|
|
reserved.isNew =
|
|
reserved.errors =
|
|
reserved.schema =
|
|
reserved.options =
|
|
reserved.modelName =
|
|
reserved.collection =
|
|
reserved.toObject =
|
|
reserved.emit = // EventEmitter
|
|
reserved._events = // EventEmitter
|
|
reserved._pres = reserved._posts = 1 // hooks.js
|
|
|
|
/**
|
|
* 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];
|
|
|
|
// 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");
|
|
}
|
|
|
|
// update the tree
|
|
var subpaths = path.split(/\./)
|
|
, last = subpaths.pop()
|
|
, branch = this.tree;
|
|
|
|
subpaths.forEach(function(sub, i) {
|
|
if (!branch[sub]) branch[sub] = {};
|
|
if ('object' != typeof branch[sub]) {
|
|
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);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Converts type arguments into Mongoose Types.
|
|
*
|
|
* @param {String} path
|
|
* @param {Object} obj constructor
|
|
* @api private
|
|
*/
|
|
|
|
Schema.interpretAsType = function (path, obj) {
|
|
if (obj.constructor && obj.constructor.name != 'Object')
|
|
obj = { type: obj };
|
|
|
|
// 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.type && !obj.type.type
|
|
? obj.type
|
|
: {};
|
|
|
|
if ('Object' == type.constructor.name || 'mixed' == type) {
|
|
return new Types.Mixed(path, obj);
|
|
}
|
|
|
|
if (Array.isArray(type) || Array == type || 'array' == type) {
|
|
// if it was specified through { type } look for `cast`
|
|
var cast = (Array == type || 'array' == type)
|
|
? obj.cast
|
|
: type[0];
|
|
|
|
if (cast instanceof Schema) {
|
|
return new Types.DocumentArray(path, cast, obj);
|
|
}
|
|
|
|
if ('string' == typeof cast) {
|
|
cast = Types[cast.charAt(0).toUpperCase() + cast.substring(1)];
|
|
} else if (cast && (!cast.type || cast.type.type)
|
|
&& 'Object' == cast.constructor.name
|
|
&& Object.keys(cast).length) {
|
|
return new Types.DocumentArray(path, new Schema(cast), obj);
|
|
}
|
|
|
|
return new Types.Array(path, cast || Types.Mixed, obj);
|
|
}
|
|
|
|
var name = 'string' == typeof type
|
|
? type
|
|
: type.name;
|
|
|
|
if (name) {
|
|
name = name.charAt(0).toUpperCase() + name.substring(1);
|
|
}
|
|
|
|
if (undefined == Types[name]) {
|
|
throw new TypeError('Undefined type at `' + path +
|
|
'`\n Did you try nesting Schemas? ' +
|
|
'You can only nest using refs or arrays.');
|
|
}
|
|
|
|
return new Types[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
|
|
* @return {Array}
|
|
*/
|
|
|
|
Schema.prototype.requiredPaths = function requiredPaths () {
|
|
if (this._requiredpaths) 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);
|
|
}
|
|
|
|
return this._requiredpaths = ret;
|
|
}
|
|
|
|
/**
|
|
* 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 (/\.\d+\.|\.\d+$/.test(path) && getPositionalPath(this, path)) {
|
|
return 'real';
|
|
} else {
|
|
return 'adhocOrUndefined'
|
|
}
|
|
};
|
|
|
|
/*!
|
|
* ignore
|
|
*/
|
|
|
|
function getPositionalPath (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]);
|
|
if (!val) return val;
|
|
|
|
var last = subpaths.length - 1
|
|
, subpath
|
|
, i = 1;
|
|
|
|
for (; i < subpaths.length; ++i) {
|
|
subpath = subpaths[i];
|
|
|
|
if (i === last && val && !val.schema && !/\D/.test(subpath)) {
|
|
if (val instanceof Types.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;
|
|
}
|
|
|
|
val = val.schema.path(subpath);
|
|
}
|
|
|
|
return self.subpaths[path] = val;
|
|
}
|
|
|
|
/**
|
|
* 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 private
|
|
*/
|
|
|
|
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(){
|
|
return this.queue('pre', arguments);
|
|
};
|
|
|
|
/**
|
|
* Defines a post for the document
|
|
*
|
|
* Post hooks fire `on` the event emitted from document instances of Models compiled from this schema.
|
|
*
|
|
* var schema = new Schema(..);
|
|
* schema.post('save', function (doc) {
|
|
* console.log('this fired after a document was saved');
|
|
* });
|
|
*
|
|
* var Model = mongoose.model('Model', schema);
|
|
*
|
|
* var m = new Model(..);
|
|
* m.save(function (err) {
|
|
* console.log('this fires after the `post` hook');
|
|
* });
|
|
*
|
|
* @param {String} method name of the method to hook
|
|
* @param {Function} fn callback
|
|
* @see hooks.js https://github.com/bnoguchi/hooks-js/tree/31ec571cef0332e21121ee7157e0cf9728572cc3
|
|
* @api public
|
|
*/
|
|
|
|
Schema.prototype.post = function(method, fn){
|
|
return this.queue('on', arguments);
|
|
};
|
|
|
|
/**
|
|
* 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 ('string' != typeof name)
|
|
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} name
|
|
* @param {Function} fn
|
|
* @api public
|
|
*/
|
|
|
|
Schema.prototype.static = function(name, fn) {
|
|
if ('string' != typeof name)
|
|
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]
|
|
* @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.
|
|
*
|
|
* @param {String} key option name
|
|
* @param {Object} [value] if not passed, the current option value is returned
|
|
* @api public
|
|
*/
|
|
|
|
Schema.prototype.set = function (key, value, _tags) {
|
|
if (1 === arguments.length) {
|
|
return this.options[key];
|
|
}
|
|
|
|
switch (key) {
|
|
case 'read':
|
|
this.options[key] = utils.readPref(value, _tags)
|
|
break;
|
|
case 'safe':
|
|
this.options[key] = false === value
|
|
? { w: 0 }
|
|
: 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 = []
|
|
, seenSchemas = []
|
|
collectIndexes(this);
|
|
return indexes;
|
|
|
|
function collectIndexes (schema, prefix) {
|
|
if (~seenSchemas.indexOf(schema)) return;
|
|
seenSchemas.push(schema);
|
|
|
|
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 Types.DocumentArray) {
|
|
collectIndexes(path.schema, key + '.');
|
|
} else {
|
|
index = path._index;
|
|
|
|
if (false !== index && null != index) {
|
|
field = {};
|
|
isObject = utils.isObject(index);
|
|
options = isObject ? index : {};
|
|
type = 'string' == typeof index ? index :
|
|
isObject ? index.type :
|
|
false;
|
|
|
|
if (type && ~Schema.indexTypes.indexOf(type)) {
|
|
field[prefix + key] = type;
|
|
} 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);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* 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) {
|
|
var virtuals = this.virtuals;
|
|
var parts = name.split('.');
|
|
return 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);
|
|
};
|
|
|
|
/**
|
|
* Returns the virtual type with the given `name`.
|
|
*
|
|
* @param {String} name
|
|
* @return {VirtualType}
|
|
*/
|
|
|
|
Schema.prototype.virtualpath = function (name) {
|
|
return this.virtuals[name];
|
|
};
|
|
|
|
/**
|
|
* These still haven't been fixed. Once they're working we'll make them public again.
|
|
* @api private
|
|
*/
|
|
|
|
Schema.prototype.namedScope = function (name, fn) {
|
|
var namedScopes = this.namedScopes || (this.namedScopes = new NamedScope)
|
|
, newScope = Object.create(namedScopes)
|
|
, allScopes = namedScopes.scopesByName || (namedScopes.scopesByName = {});
|
|
allScopes[name] = newScope;
|
|
newScope.name = name;
|
|
newScope.block = fn;
|
|
newScope.query = new Query();
|
|
newScope.decorate(namedScopes, {
|
|
block0: function (block) {
|
|
return function () {
|
|
block.call(this.query);
|
|
return this;
|
|
};
|
|
},
|
|
blockN: function (block) {
|
|
return function () {
|
|
block.apply(this.query, arguments);
|
|
return this;
|
|
};
|
|
},
|
|
basic: function (query) {
|
|
return function () {
|
|
this.query.find(query);
|
|
return this;
|
|
};
|
|
}
|
|
});
|
|
return newScope;
|
|
};
|
|
|
|
/*!
|
|
* 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 = require('./schema/index');
|
|
|
|
/*!
|
|
* ignore
|
|
*/
|
|
|
|
Types = Schema.Types;
|
|
NamedScope = require('./namedscope')
|
|
Query = require('./query');
|
|
var ObjectId = exports.ObjectId = Types.ObjectId;
|
|
|