function ES6Promise() { throw new Error('Can\'t use ES6 promise with mpromise style constructor'); }
n/a
function Aggregate() { this._pipeline = []; this._model = undefined; this.options = undefined; if (arguments.length === 1 && util.isArray(arguments[0])) { this.append.apply(this, arguments[0]); } else { this.append.apply(this, arguments); } }
...
* ####Example:
*
* new Aggregate();
* new Aggregate({ $project: { a: 1, b: 1 } });
* new Aggregate({ $project: { a: 1, b: 1 } }, { $skip: 5 });
* new Aggregate([{ $project: { a: 1, b: 1 } }, { $skip: 5 }]);
*
* Returned when calling Model.aggregate().
*
* ####Example:
*
* Model
* .aggregate({ $match: { age: { $gte: 21 }}})
* .unwind('tags')
* .exec(callback)
...
function SchemaArray(key, cast, options, schemaOptions) { var typeKey = 'type'; if (schemaOptions && schemaOptions.typeKey) { typeKey = schemaOptions.typeKey; } if (cast) { var castOptions = {}; if (utils.getFunctionName(cast.constructor) === 'Object') { if (cast[typeKey]) { // support { type: Woot } castOptions = utils.clone(cast); // do not alter user arguments delete castOptions[typeKey]; cast = cast[typeKey]; } else { cast = Mixed; } } // support { type: 'String' } var name = typeof cast === 'string' ? cast : utils.getFunctionName(cast); var caster = name in Types ? Types[name] : cast; this.casterConstructor = caster; if (typeof caster === 'function') { this.caster = new caster(null, castOptions); } else { this.caster = caster; } if (!(this.caster instanceof EmbeddedDoc)) { this.caster.path = key; } } SchemaType.call(this, key, options, 'Array'); var defaultArr; var fn; if (this.defaultValue != null) { defaultArr = this.defaultValue; fn = typeof defaultArr === 'function'; } if (!('defaultValue' in this) || this.defaultValue !== void 0) { this.default(function() { var arr = []; if (fn) { arr = defaultArr(); } else if (defaultArr != null) { arr = defaultArr; } // Leave it up to `cast()` to convert the array return arr; }); } }
n/a
function SchemaBoolean(path, options) { SchemaType.call(this, path, options, 'Boolean'); }
n/a
function Document(obj, schema, fields, skipId, skipInit) { if (!(this instanceof Document)) { return new Document(obj, schema, fields, skipId, skipInit); } if (utils.isObject(schema) && !schema.instanceOfSchema) { schema = new Schema(schema); } // When creating EmbeddedDocument, it already has the schema and he doesn't need the _id schema = this.schema || schema; // Generate ObjectId if it is missing, but it requires a scheme if (!this.schema && schema.options._id) { obj = obj || {}; if (obj._id === undefined) { obj._id = new ObjectId(); } } if (!schema) { throw new MongooseError.MissingSchemaError(); } this.$__setSchema(schema); this.$__ = new InternalCache; this.$__.emitter = new EventEmitter(); this.isNew = true; this.errors = undefined; if (typeof fields === 'boolean') { this.$__.strictMode = fields; fields = undefined; } else { this.$__.strictMode = this.schema.options && this.schema.options.strict; this.$__.selected = fields; } var required = this.schema.requiredPaths(); for (var i = 0; i < required.length; ++i) { this.$__.activePaths.require(required[i]); } this.$__.emitter.setMaxListeners(0); this._doc = this.$__buildDoc(obj, fields, skipId); if (!skipInit && obj) { this.init(obj); } this.$__registerHooksFromSchema(); // apply methods for (var m in schema.methods) { this[m] = schema.methods[m]; } // apply statics for (var s in schema.statics) { this[s] = schema.statics[s]; } }
n/a
function MissingSchemaError() { var msg = 'Schema hasn\'t been registered for document.\n' + 'Use mongoose.Document(name, schema)'; MongooseError.call(this, msg); Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee); this.name = 'MissingSchemaError'; }
n/a
function SchemaBuffer(key, options) { SchemaType.call(this, key, options, 'Buffer'); }
n/a
function Collection(name, conn, opts) { if (opts === void 0) { opts = {}; } if (opts.capped === void 0) { opts.capped = {}; } opts.bufferCommands = undefined === opts.bufferCommands ? true : opts.bufferCommands; if (typeof opts.capped === 'number') { opts.capped = {size: opts.capped}; } this.opts = opts; this.name = name; this.collectionName = name; this.conn = conn; this.queue = []; this.buffer = this.opts.bufferCommands; this.emitter = new EventEmitter(); if (STATES.connected === this.conn.readyState) { this.onOpen(); } }
...
model.prototype.$__setSchema(schema);
var collectionOptions = {
bufferCommands: schema.options.bufferCommands,
capped: schema.options.capped
};
model.prototype.collection = connection.collection(
collectionName
, collectionOptions
);
// apply methods and statics
applyMethods(model, schema);
applyStatics(model, schema);
...
function Connection(base) { this.base = base; this.collections = {}; this.models = {}; this.config = {autoIndex: true}; this.replica = false; this.hosts = null; this.host = null; this.port = null; this.user = null; this.pass = null; this.name = null; this.options = null; this.otherDbs = []; this._readyState = STATES.disconnected; this._closeCalled = false; this._hasOpened = false; }
n/a
function SchemaDate(key, options) { SchemaType.call(this, key, options, 'Date'); }
n/a
function Decimal128(key, options) { SchemaType.call(this, key, options, 'Decimal128'); }
n/a
function DisconnectedError(connectionString) { MongooseError.call(this, 'Ran out of retries trying to reconnect to "' + connectionString + '". Try setting `server.reconnectTries` and ' + '`server.reconnectInterval` to something higher.'); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'DisconnectedError'; }
n/a
function DivergentArrayError(paths) { var msg = 'For your own good, using `document.save()` to update an array ' + 'which was selected using an $elemMatch projection OR ' + 'populated using skip, limit, query conditions, or exclusion of ' + 'the _id field when the operation results in a $pop or $set of ' + 'the entire array is not supported. The following ' + 'path(s) would have been modified unsafely:\n' + ' ' + paths.join('\n ') + '\n' + 'Use Model.update() to update these arrays instead.'; // TODO write up a docs page (FAQ) and link to it MongooseError.call(this, msg); Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee); this.name = 'DivergentArrayError'; }
n/a
function Document(obj, fields, skipId) { this.$__ = new InternalCache; this.$__.emitter = new EventEmitter(); this.isNew = true; this.errors = undefined; var schema = this.schema; if (typeof fields === 'boolean') { this.$__.strictMode = fields; fields = undefined; } else { this.$__.strictMode = schema.options && schema.options.strict; this.$__.selected = fields; } var required = schema.requiredPaths(true); for (var i = 0; i < required.length; ++i) { this.$__.activePaths.require(required[i]); } this.$__.emitter.setMaxListeners(0); this._doc = this.$__buildDoc(obj, fields, skipId); if (obj) { if (obj instanceof Document) { this.isNew = obj.isNew; } // Skip set hooks if (this.$__original_set) { this.$__original_set(obj, undefined, true); } else { this.set(obj, undefined, true); } } if (!schema.options.strict && obj) { var _this = this, keys = Object.keys(this._doc); keys.forEach(function(key) { if (!(key in schema.tree)) { defineKey(key, null, _this); } }); } applyQueue(this); }
n/a
function DocumentArray(key, schema, options) { var EmbeddedDocument = _createConstructor(schema, options); ArrayType.call(this, key, EmbeddedDocument, options); this.schema = schema; this.$isMongooseDocumentArray = true; var fn = this.defaultValue; if (!('defaultValue' in this) || fn !== void 0) { this.default(function() { var arr = fn.call(this); if (!Array.isArray(arr)) { arr = [arr]; } // Leave it up to `cast()` to convert this to a documentarray return arr; }); } }
n/a
function Embedded(schema, path, options) { var _embedded = function SingleNested(value, path, parent) { var _this = this; Subdocument.apply(this, arguments); this.$parent = parent; if (parent) { parent.on('save', function() { _this.emit('save', _this); _this.constructor.emit('save', _this); }); parent.on('isNew', function(val) { _this.isNew = val; _this.emit('isNew', val); _this.constructor.emit('isNew', val); }); } }; _embedded.prototype = Object.create(Subdocument.prototype); _embedded.prototype.$__setSchema(schema); _embedded.prototype.constructor = _embedded; _embedded.schema = schema; _embedded.$isSingleNested = true; _embedded.prototype.$basePath = path; _embedded.prototype.toBSON = function() { return this.toObject({ transform: false, retainKeyOrder: schema.options.retainKeyOrder, virtuals: false, _skipDepopulateTopLevel: true, depopulate: true, flattenDecimals: false }); }; // apply methods for (var i in schema.methods) { _embedded.prototype[i] = schema.methods[i]; } // apply statics for (i in schema.statics) { _embedded[i] = schema.statics[i]; } for (i in EventEmitter.prototype) { _embedded[i] = EventEmitter.prototype[i]; } applyHooks(_embedded, schema); this.caster = _embedded; this.schema = schema; this.$isSingleNested = true; SchemaType.call(this, path, options, 'Embedded'); }
n/a
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
res.statusCode = 204;
return res.end();
}
req.on('end', function() {
server.serve(req, res, function(err) {
if (err) {
console.error(err, req.url);
res.writeHead(err.status, err.headers);
res.end();
}
});
});
req.resume();
}).listen(8088);
...
function MissingSchemaError(name) { var msg = 'Schema hasn\'t been registered for model "' + name + '".\n' + 'Use mongoose.model(name, schema)'; MongooseError.call(this, msg); Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee); this.name = 'MissingSchemaError'; }
n/a
function Mixed(path, options) { if (options && options.default) { var def = options.default; if (Array.isArray(def) && def.length === 0) { // make sure empty array defaults are handled options.default = Array; } else if (!options.shared && utils.isObject(def) && Object.keys(def).length === 0) { // prevent odd "shared" objects between documents options.default = function() { return {}; }; } } SchemaType.call(this, path, options, 'Mixed'); }
n/a
function Model(doc, fields, skipId) { Document.call(this, doc, fields, skipId, true); }
...
});
```
Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
### Accessing a Model
Once we define a model through `mongoose.model('ModelName', mySchema)`, we
can access it through the same function
```js
var myModel = mongoose.model('ModelName');
```
Or just do it all at once
...
function DocumentNotFoundError(query) { MongooseError.call(this, 'No document found for query "' + util.inspect(query) + '"'); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'DocumentNotFoundError'; this.query = query; }
n/a
function SchemaNumber(key, options) { SchemaType.call(this, key, options, 'Number'); }
n/a
function ObjectExpectedError(path, val) { MongooseError.call(this, 'Tried to set nested object field `' + path + '` to primitive value `' + val + '` and strict mode is set to throw.'); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'ObjectExpectedError'; this.path = path; }
n/a
function ObjectId(key, options) { SchemaType.call(this, key, options, 'ObjectID'); }
n/a
function OverwriteModelError(name) { MongooseError.call(this, 'Cannot overwrite `' + name + '` model once compiled.'); Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee); this.name = 'OverwriteModelError'; }
n/a
function Promise(fn) { MPromise.call(this, fn); }
n/a
function Query(conditions, options, model, collection) { // this stuff is for dealing with custom queries created by #toConstructor if (!this._mongooseOptions) { this._mongooseOptions = {}; } // this is the case where we have a CustomQuery, we need to check if we got // options passed in, and if we did, merge them in if (options) { var keys = Object.keys(options); for (var i = 0; i < keys.length; ++i) { var k = keys[i]; this._mongooseOptions[k] = options[k]; } } if (collection) { this.mongooseCollection = collection; } if (model) { this.model = model; this.schema = model.schema; } // this is needed because map reduce returns a model that can be queried, but // all of the queries on said model should be lean if (this.model && this.model._mapreduce) { this.lean(); } // inherit mquery mquery.call(this, this.mongooseCollection, options); if (conditions) { this.find(conditions); } if (this.schema) { var kareemOptions = { useErrorHandlers: true, numCallbackParams: 1 }; this._count = this.model.hooks.createWrapper('count', Query.prototype._count, this, kareemOptions); this._execUpdate = this.model.hooks.createWrapper('update', Query.prototype._execUpdate, this, kareemOptions); this._find = this.model.hooks.createWrapper('find', Query.prototype._find, this, kareemOptions); this._findOne = this.model.hooks.createWrapper('findOne', Query.prototype._findOne, this, kareemOptions); this._findOneAndRemove = this.model.hooks.createWrapper('findOneAndRemove', Query.prototype._findOneAndRemove, this, kareemOptions); this._findOneAndUpdate = this.model.hooks.createWrapper('findOneAndUpdate', Query.prototype._findOneAndUpdate, this, kareemOptions); this._replaceOne = this.model.hooks.createWrapper('replaceOne', Query.prototype._replaceOne, this, kareemOptions); this._updateMany = this.model.hooks.createWrapper('updateMany', Query.prototype._updateMany, this, kareemOptions); this._updateOne = this.model.hooks.createWrapper('updateOne', Query.prototype._updateOne, this, kareemOptions); } }
n/a
function QueryCursor(query, options) { Readable.call(this, { objectMode: true }); this.cursor = null; this.query = query; this._transforms = options.transform ? [options.transform] : []; var _this = this; var model = query.model; model.hooks.execPre('find', query, function() { model.collection.find(query._conditions, options, function(err, cursor) { if (_this._error) { cursor.close(function() {}); _this.listeners('error').length > 0 && _this.emit('error', _this._error); } if (err) { return _this.emit('error', err); } _this.cursor = cursor; _this.emit('cursor', cursor); }); }); }
n/a
function QueryStream(query, options) { Stream.call(this); this.query = query; this.readable = true; this.paused = false; this._cursor = null; this._destroyed = null; this._fields = null; this._buffer = null; this._inline = T_INIT; this._running = false; this._transform = options && typeof options.transform === 'function' ? options.transform : K; // give time to hook up events var _this = this; process.nextTick(function() { _this._init(); }); }
n/a
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.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); } }
n/a
function SchemaType(path, options, instance) { this.path = path; this.instance = instance; this.validators = []; this.setters = []; this.getters = []; this.options = options; this._index = null; this.selected; for (var i in options) { if (this[i] && typeof this[i] === 'function') { // { unique: true, index: true } if (i === 'index' && this._index) { continue; } var opts = Array.isArray(options[i]) ? options[i] : [options[i]]; this[i].apply(this, opts); } } }
n/a
function StateMachine() { }
n/a
function StrictModeError(path, msg) { msg = msg || 'Field `' + path + '` is not in schema and strict ' + 'mode is set to throw.'; MongooseError.call(this, msg); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'StrictModeError'; this.path = path; }
n/a
function SchemaString(key, options) { this.enumValues = []; this.regExp = null; SchemaType.call(this, key, options, 'String'); }
n/a
function Subdocument(value, fields) { this.$isSingleNested = true; Document.call(this, value, fields); }
n/a
function ValidationError(instance) { this.errors = {}; if (instance && instance.constructor.name === 'model') { MongooseError.call(this, instance.constructor.modelName + ' validation failed'); } else { MongooseError.call(this, 'Validation failed'); } if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'ValidationError'; if (instance) { instance.errors = this.errors; } }
n/a
function ValidatorError(properties) { var msg = properties.message; if (!msg) { msg = MongooseError.messages.general.default; } var message = this.formatMessage(msg, properties); MongooseError.call(this, message); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.properties = properties; this.name = 'ValidatorError'; this.kind = properties.type; this.path = properties.path; this.value = properties.value; this.reason = properties.reason; }
n/a
function VersionError(doc) { MongooseError.call(this, 'No matching document found for id "' + doc._id + '"'); this.name = 'VersionError'; }
n/a
function VirtualType(options, name) { this.path = name; this.getters = []; this.setters = []; this.options = options || {}; }
n/a
function ES6Promise() { throw new Error('Can\'t use ES6 promise with mpromise style constructor'); }
n/a
use = function (Promise) { ES6Promise.ES6 = Promise; }
...
*/
Promise.set = function(lib) {
if (lib === MPromise) {
return Promise.reset();
}
Promise._promise = require('./ES6Promise');
Promise._promise.use(lib);
require('mquery').Promise = Promise._promise.ES6;
};
/**
* Resets to using mpromise
*
* @api private
...
function Aggregate() { this._pipeline = []; this._model = undefined; this.options = undefined; if (arguments.length === 1 && util.isArray(arguments[0])) { this.append.apply(this, arguments[0]); } else { this.append.apply(this, arguments); } }
...
* ####Example:
*
* new Aggregate();
* new Aggregate({ $project: { a: 1, b: 1 } });
* new Aggregate({ $project: { a: 1, b: 1 } }, { $skip: 5 });
* new Aggregate([{ $project: { a: 1, b: 1 } }, { $skip: 5 }]);
*
* Returned when calling Model.aggregate().
*
* ####Example:
*
* Model
* .aggregate({ $match: { age: { $gte: 21 }}})
* .unwind('tags')
* .exec(callback)
...
addCursorFlag = function (flag, value) { if (!this.options) { this.options = {}; } this.options[flag] = value; return this; }
...
};
/**
* Adds a [cursor flag](http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html#addCursorFlag)
*
* ####Example:
*
* Model.aggregate(..).addCursorFlag('noCursorTimeout', true).exec();
*
* @param {String} flag
* @param {Boolean} value
* @see mongodb http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html#addCursorFlag
*/
Aggregate.prototype.addCursorFlag = function(flag, value) {
...
allowDiskUse = function (value) { if (!this.options) { this.options = {}; } this.options.allowDiskUse = value; return this; }
...
};
/**
* Sets the allowDiskUse option for the aggregation query (ignored for < 2.6.0)
*
* ####Example:
*
* Model.aggregate(..).allowDiskUse(true).exec(callback)
*
* @param {Boolean} value Should tell server it can use hard drive to store data during aggregation.
* @param {Array} [tags] optional tags for this query
* @see mongodb http://docs.mongodb.org/manual/reference/command/aggregate/
*/
Aggregate.prototype.allowDiskUse = function(value) {
...
append = function () { var args = (arguments.length === 1 && util.isArray(arguments[0])) ? arguments[0] : utils.args(arguments); if (!args.every(isOperator)) { throw new Error('Arguments must be aggregate pipeline operators'); } this._pipeline = this._pipeline.concat(args); return this; }
...
};
/**
* Appends new operators to this aggregate pipeline
*
* ####Examples:
*
* aggregate.append({ $project: { field: 1 }}, { $limit: 2 });
*
* // or pass an array
* var pipeline = [{ $match: { daw: 'Logic Audio X' }} ];
* aggregate.append(pipeline);
*
* @param {Object} ops operator(s) to append
* @return {Aggregate}
...
collation = function (collation) { if (!this.options) { this.options = {}; } this.options.collation = collation; return this; }
...
};
/**
* Adds a collation
*
* ####Example:
*
* Model.aggregate(..).collation({ locale: 'en_US', strength: 1 }).exec
();
*
* @param {Object} collation options
* @param {Boolean} value
* @see mongodb http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#aggregate
*/
Aggregate.prototype.collation = function(collation) {
...
cursor = function (options) { if (!this.options) { this.options = {}; } this.options.cursor = options || {}; return this; }
...
/**
* Sets the cursor option option for the aggregation query (ignored for < 2.6.0).
* Note the different syntax below: .exec() returns a cursor object, and no callback
* is necessary.
*
* ####Example:
*
* var cursor = Model.aggregate(..).cursor({ batchSize: 1000 }).exec();
* cursor.each(function(error, doc) {
* // use doc
* });
*
* @param {Object} options set the cursor batch size
* @see mongodb http://mongodb.github.io/node-mongodb-native/2.0/api/AggregationCursor.html
*/
...
exec = function (callback) { if (!this._model) { throw new Error('Aggregate not bound to any Model'); } var _this = this; var Promise = PromiseProvider.get(); var options = utils.clone(this.options); if (options && options.cursor) { if (options.cursor.async) { delete options.cursor.async; return new Promise.ES6(function(resolve) { if (!_this._model.collection.buffer) { process.nextTick(function() { var cursor = _this._model.collection. aggregate(_this._pipeline, options || {}); decorateCursor(cursor); resolve(cursor); callback && callback(null, cursor); }); return; } _this._model.collection.emitter.once('queue', function() { var cursor = _this._model.collection. aggregate(_this._pipeline, options || {}); decorateCursor(cursor); resolve(cursor); callback && callback(null, cursor); }); }); } var cursor = this._model.collection. aggregate(this._pipeline, this.options || {}); decorateCursor(cursor); return cursor; } return new Promise.ES6(function(resolve, reject) { if (!_this._pipeline.length) { var err = new Error('Aggregate has empty pipeline'); if (callback) { callback(err); } reject(err); return; } prepareDiscriminatorPipeline(_this); _this._model .collection .aggregate(_this._pipeline, _this.options || {}, function(error, result) { if (error) { if (callback) { callback(error); } reject(error); return; } if (callback) { callback(null, result); } resolve(result); }); }); }
...
function getVersion() {
var hist = fs.readFileSync('./History.md', 'utf8').replace(/\r/g, '\n').split('\n');
for (var i = 0; i < hist.length; ++i) {
var line = (hist[i] || '').trim();
if (!line) {
continue;
}
var match = /^\s*([^\s]+)\s/.exec(line);
if (match && match[1]) {
return match[1];
}
}
throw new Error('no match found');
}
...
explain = function (callback) { var _this = this; var Promise = PromiseProvider.get(); return new Promise.ES6(function(resolve, reject) { if (!_this._pipeline.length) { var err = new Error('Aggregate has empty pipeline'); if (callback) { callback(err); } reject(err); return; } prepareDiscriminatorPipeline(_this); _this._model .collection .aggregate(_this._pipeline, _this.options || {}) .explain(function(error, result) { if (error) { if (callback) { callback(error); } reject(error); return; } if (callback) { callback(null, result); } resolve(result); }); }); }
...
};
/**
* Execute the aggregation with explain
*
* ####Example:
*
* Model.aggregate(..).explain(callback)
*
* @param {Function} callback
* @return {Promise}
*/
Aggregate.prototype.explain = function(callback) {
var _this = this;
...
facet = function (options) { return this.append({$facet: options}); }
...
};
/**
* Combines multiple aggregation pipelines.
*
* ####Example:
* Model.aggregate(...)
* .facet({
* books: [{ groupBy: '$author' }],
* price: [{ $bucketAuto: { groupBy: '$price', buckets: 2 } }]
* })
* .exec();
*
* // Output: { books: [...], price: [{...}, {...}] }
*
...
graphLookup = function (options) { var cloneOptions = {}; if (options) { if (!utils.isObject(options)) { throw new TypeError('Invalid graphLookup() argument. Must be an object.'); } utils.mergeClone(cloneOptions, options); var startWith = cloneOptions.startWith; if (startWith && typeof startWith === 'string') { cloneOptions.startWith = cloneOptions.startWith.charAt(0) === '$' ? cloneOptions.startWith : '$' + cloneOptions.startWith; } } return this.append({ $graphLookup: cloneOptions }); }
...
/**
* Appends new custom $graphLookup operator(s) to this aggregate pipeline, performing a recursive search on a collection.
*
* Note that graphLookup can only consume at most 100MB of memory, and does not allow disk use even if `{ allowDiskUse: true }` is
specified.
*
* #### Examples:
* // Suppose we have a collection of courses, where a document might look like `{ _id: 0, name: 'Calculus', prerequisite
: 'Trigonometry'}` and `{ _id: 0, name: 'Trigonometry', prerequisite: 'Algebra' }`
* aggregate.graphLookup({ from: 'courses', startWith: '$prerequisite
', connectFromField: 'prerequisite', connectToField: 'name', as: 'prerequisites', maxDepth: 3
}) // this will recursively search the 'courses' collection up to 3 prerequisites
*
* @see $graphLookup https://docs.mongodb.com/manual/reference/operator/aggregation/graphLookup/#pipe._S_graphLookup
* @param {Object} options to $graphLookup as described in the above link
* @return {Aggregate}
* @api public
*/
...
group = function (arg) { var op = {}; op['$' + $operator] = arg; return this.append(op); }
...
};
/**
* Appends a new custom $group operator to this aggregate pipeline.
*
* ####Examples:
*
* aggregate.group({ _id: "$department" });
*
* @see $group http://docs.mongodb.org/manual/reference/aggregation/group/
* @method group
* @memberOf Aggregate
* @param {Object} arg $group operator contents
* @return {Aggregate}
* @api public
...
limit = function (arg) { var op = {}; op['$' + $operator] = arg; return this.append(op); }
...
*/
/**
* Appends a new $limit operator to this aggregate pipeline.
*
* ####Examples:
*
* aggregate.limit(10);
*
* @see $limit http://docs.mongodb.org/manual/reference/aggregation/limit/
* @method limit
* @memberOf Aggregate
* @param {Number} num maximum number of records to pass to the next stage
* @return {Aggregate}
* @api public
...
lookup = function (options) { return this.append({$lookup: options}); }
...
};
/**
* Appends new custom $lookup operator(s) to this aggregate pipeline.
*
* ####Examples:
*
* aggregate.lookup({ from: 'users', localField: 'userId', foreignField
: '_id', as: 'users' });
*
* @see $lookup https://docs.mongodb.org/manual/reference/operator/aggregation/lookup/#pipe._S_lookup
* @param {Object} options to $lookup as described in the above link
* @return {Aggregate}
* @api public
*/
...
match = function (arg) { var op = {}; op['$' + $operator] = arg; return this.append(op); }
...
*/
/**
* Appends a new custom $match operator to this aggregate pipeline.
*
* ####Examples:
*
* aggregate.match({ department: { $in: [ "sales", "engineering"
; ] } });
*
* @see $match http://docs.mongodb.org/manual/reference/aggregation/match/
* @method match
* @memberOf Aggregate
* @param {Object} arg $match operator contents
* @return {Aggregate}
* @api public
...
model = function (model) { this._model = model; return this; }
...
});
```
Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
### Accessing a Model
Once we define a model through `mongoose.model('ModelName', mySchema)`, we
can access it through the same function
```js
var myModel = mongoose.model('ModelName');
```
Or just do it all at once
...
near = function (arg) { var op = {}; op.$geoNear = arg; return this.append(op); }
...
*
* ####NOTE:
*
* **MUST** be used as the first operator in the pipeline.
*
* ####Examples:
*
* aggregate.near({
* near: [40.724, -73.997],
* distanceField: "dist.calculated", // required
* maxDistance: 0.008,
* query: { type: "public" },
* includeLocs: "dist.location",
* uniqueDocs: true,
* num: 5
...
out = function (arg) { var op = {}; op['$' + $operator] = arg; return this.append(op); }
n/a
project = function (arg) { var fields = {}; if (typeof arg === 'object' && !util.isArray(arg)) { Object.keys(arg).forEach(function(field) { fields[field] = arg[field]; }); } else if (arguments.length === 1 && typeof arg === 'string') { arg.split(/\s+/).forEach(function(field) { if (!field) { return; } var include = field[0] === '-' ? 0 : 1; if (include === 0) { field = field.substring(1); } fields[field] = include; }); } else { throw new Error('Invalid project() argument. Must be string or object'); } return this.append({$project: fields}); }
...
* Appends a new $project operator to this aggregate pipeline.
*
* Mongoose query [selection syntax](#query_Query-select) is also supported.
*
* ####Examples:
*
* // include a, include b, exclude _id
* aggregate.project("a b -_id");
*
* // or you may use object notation, useful when
* // you have keys already prefixed with a "-"
* aggregate.project({a: 1, b: 1, _id: 0});
*
* // reshaping documents
* aggregate.project({
...
read = function (pref, tags) { if (!this.options) { this.options = {}; } read.call(this, pref, tags); return this; }
...
};
/**
* Sets the readPreference option for the aggregation query.
*
* ####Example:
*
* Model.aggregate(..).read('primaryPreferred').exec(callback)
*
* @param {String} pref one of the listed preference options or their aliases
* @param {Array} [tags] optional tags for this query
* @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
* @see driver http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences
*/
...
sample = function (size) { return this.append({$sample: {size: size}}); }
...
};
/**
* Appepnds new custom $sample operator(s) to this aggregate pipeline.
*
* ####Examples:
*
* aggregate.sample(3); // Add a pipeline that picks 3 random documents
*
* @see $sample https://docs.mongodb.org/manual/reference/operator/aggregation/sample/#pipe._S_sample
* @param {Number} size number of random documents to pick
* @return {Aggregate}
* @api public
*/
...
skip = function (arg) { var op = {}; op['$' + $operator] = arg; return this.append(op); }
...
*/
/**
* Appends a new $skip operator to this aggregate pipeline.
*
* ####Examples:
*
* aggregate.skip(10);
*
* @see $skip http://docs.mongodb.org/manual/reference/aggregation/skip/
* @method skip
* @memberOf Aggregate
* @param {Number} num number of records to skip before next stage
* @return {Aggregate}
* @api public
...
sort = function (arg) { // TODO refactor to reuse the query builder logic var sort = {}; if (arg.constructor.name === 'Object') { var desc = ['desc', 'descending', -1]; Object.keys(arg).forEach(function(field) { sort[field] = desc.indexOf(arg[field]) === -1 ? 1 : -1; }); } else if (arguments.length === 1 && typeof arg === 'string') { arg.split(/\s+/).forEach(function(field) { if (!field) { return; } var ascend = field[0] === '-' ? -1 : 1; if (ascend === -1) { field = field.substring(1); } sort[field] = ascend; }); } else { throw new TypeError('Invalid sort() argument. Must be a string or object.'); } return this.append({$sort: sort}); }
...
* If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`.
*
* If a string is passed, it must be a space delimited list of path names. The sort order of each path is ascending unless the path
name is prefixed with `-` which will be treated as descending.
*
* ####Examples:
*
* // these are equivalent
* aggregate.sort({ field: 'asc', test: -1 });
* aggregate.sort('field -test');
*
* @see $sort http://docs.mongodb.org/manual/reference/aggregation/sort/
* @param {Object|String} arg
* @return {Aggregate} this
* @api public
*/
...
then = function (resolve, reject) { return this.exec().then(resolve, reject); }
...
*
* ####Example:
*
* aggregate.exec(callback);
*
* // Because a promise is returned, the `callback` is optional.
* var promise = aggregate.exec();
* promise.then(..);
*
* @see Promise #promise_Promise
* @param {Function} [callback]
* @return {Promise}
* @api public
*/
...
unwind = function () { var args = utils.args(arguments); var res = []; for (var i = 0; i < args.length; ++i) { var arg = args[i]; if (arg && typeof arg === 'object') { res.push({ $unwind: arg }); } else if (typeof arg === 'string') { res.push({ $unwind: (arg && arg.charAt(0) === '$') ? arg : '$' + arg }); } else { throw new Error('Invalid arg "' + arg + '" to unwind(), ' + 'must be string or object'); } } return this.append.apply(this, res); }
...
*
* Returned when calling Model.aggregate().
*
* ####Example:
*
* Model
* .aggregate({ $match: { age: { $gte: 21 }}})
* .unwind('tags')
* .exec(callback)
*
* ####Note:
*
* - The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned).
* - Requires MongoDB >= 2.1
* - Mongoose does **not** cast pipeline stages. `new Aggregate({ $match: { _id: '00000000000000000000000a' } });` will
not work unless `_id` is a string in the database. Use `new Aggregate({ $match: { _id: mongoose.Types.ObjectId('00000000000000000000000a
') } });` instead.
...
function SchemaArray(key, cast, options, schemaOptions) { var typeKey = 'type'; if (schemaOptions && schemaOptions.typeKey) { typeKey = schemaOptions.typeKey; } if (cast) { var castOptions = {}; if (utils.getFunctionName(cast.constructor) === 'Object') { if (cast[typeKey]) { // support { type: Woot } castOptions = utils.clone(cast); // do not alter user arguments delete castOptions[typeKey]; cast = cast[typeKey]; } else { cast = Mixed; } } // support { type: 'String' } var name = typeof cast === 'string' ? cast : utils.getFunctionName(cast); var caster = name in Types ? Types[name] : cast; this.casterConstructor = caster; if (typeof caster === 'function') { this.caster = new caster(null, castOptions); } else { this.caster = caster; } if (!(this.caster instanceof EmbeddedDoc)) { this.caster.path = key; } } SchemaType.call(this, key, options, 'Array'); var defaultArr; var fn; if (this.defaultValue != null) { defaultArr = this.defaultValue; fn = typeof defaultArr === 'function'; } if (!('defaultValue' in this) || this.defaultValue !== void 0) { this.default(function() { var arr = []; if (fn) { arr = defaultArr(); } else if (defaultArr != null) { arr = defaultArr; } // Leave it up to `cast()` to convert the array return arr; }); } }
n/a
applyGetters = function (value, scope) { if (this.caster.options && this.caster.options.ref) { // means the object id was populated return value; } return SchemaType.prototype.applyGetters.call(this, value, scope); }
...
if (adhoc) {
obj = adhoc.cast(obj);
}
// Check if this path is populated - don't apply getters if it is,
// because otherwise its a nested object. See gh-3357
if (schema && !this.populated(path)) {
obj = schema.applyGetters(obj, this);
}
return obj;
};
/**
* Returns the schematype for the given `path`.
...
cast = function (value, doc, init) { if (Array.isArray(value)) { if (!value.length && doc) { var indexes = doc.schema.indexedPaths(); for (var i = 0, l = indexes.length; i < l; ++i) { var pathIndex = indexes[i][0][this.path]; if (pathIndex === '2dsphere' || pathIndex === '2d') { return; } } } if (!(value && value.isMongooseArray)) { value = new MongooseArray(value, this.path, doc); } else if (value && value.isMongooseArray) { // We need to create a new array, otherwise change tracking will // update the old doc (gh-4449) value = new MongooseArray(value, this.path, doc); } if (this.caster) { try { for (i = 0, l = value.length; i < l; i++) { value[i] = this.caster.cast(value[i], doc, init); } } catch (e) { // rethrow throw new CastError('[' + e.kind + ']', util.inspect(value), this.path, e); } } return value; } // gh-2442: if we're loading this from the db and its not an array, mark // the whole array as modified. if (!!doc && !!init) { doc.markModified(this.path); } return this.cast([value], doc, init); }
...
doc[i] = obj[i];
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
...
castForQuery = function ($conditional, value) { var handler, val; if (arguments.length === 2) { handler = this.$conditionalHandlers[$conditional]; if (!handler) { throw new Error('Can\'t use ' + $conditional + ' with Array.'); } val = handler.call(this, value); } else { val = $conditional; var Constructor = this.casterConstructor; if (val && Constructor.discriminators && Constructor.schema.options.discriminatorKey && typeof val[Constructor.schema.options.discriminatorKey] === 'string' && Constructor.discriminators[val[Constructor.schema.options.discriminatorKey]]) { Constructor = Constructor.discriminators[val[Constructor.schema.options.discriminatorKey]]; } var proto = this.casterConstructor.prototype; var method = proto && (proto.castForQuery || proto.cast); if (!method && Constructor.castForQuery) { method = Constructor.castForQuery; } var caster = this.caster; if (Array.isArray(val)) { val = val.map(function(v) { if (utils.isObject(v) && v.$elemMatch) { return v; } if (method) { v = method.call(caster, v); return v; } if (v != null) { v = new Constructor(v); return v; } return v; }); } else if (method) { val = method.call(caster, val); } else if (val != null) { val = new Constructor(val); } } return val; }
...
}
if (geo) {
var numbertype = new Types.Number('__QueryCasting__');
var value = val[geo];
if (val.$maxDistance != null) {
val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
}
if (val.$minDistance != null) {
val.$minDistance = numbertype.castForQuery(val.$minDistance);
}
if (geo === '$within') {
var withinType = value.$center
...
checkRequired = function (value) { return !!(value && value.length); }
...
// in here, `this` refers to the validating document.
// no validation when this path wasn't selected in the query.
if ('isSelected' in this && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
return true;
}
return ((typeof required === 'function') && !required.apply(this)) ||
_this.checkRequired(v, this);
};
this.originalRequiredValue = required;
if (typeof required === 'string') {
message = required;
required = undefined;
}
...
function SchemaArray(key, cast, options, schemaOptions) { var typeKey = 'type'; if (schemaOptions && schemaOptions.typeKey) { typeKey = schemaOptions.typeKey; } if (cast) { var castOptions = {}; if (utils.getFunctionName(cast.constructor) === 'Object') { if (cast[typeKey]) { // support { type: Woot } castOptions = utils.clone(cast); // do not alter user arguments delete castOptions[typeKey]; cast = cast[typeKey]; } else { cast = Mixed; } } // support { type: 'String' } var name = typeof cast === 'string' ? cast : utils.getFunctionName(cast); var caster = name in Types ? Types[name] : cast; this.casterConstructor = caster; if (typeof caster === 'function') { this.caster = new caster(null, castOptions); } else { this.caster = caster; } if (!(this.caster instanceof EmbeddedDoc)) { this.caster.path = key; } } SchemaType.call(this, key, options, 'Array'); var defaultArr; var fn; if (this.defaultValue != null) { defaultArr = this.defaultValue; fn = typeof defaultArr === 'function'; } if (!('defaultValue' in this) || this.defaultValue !== void 0) { this.default(function() { var arr = []; if (fn) { arr = defaultArr(); } else if (defaultArr != null) { arr = defaultArr; } // Leave it up to `cast()` to convert the array return arr; }); } }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function SchemaBoolean(path, options) { SchemaType.call(this, path, options, 'Boolean'); }
n/a
cast = function (value) { if (value === null) { return value; } if (value === '0') { return false; } if (value === 'true') { return true; } if (value === 'false') { return false; } return !!value; }
...
doc[i] = obj[i];
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
...
castForQuery = function ($conditional, val) { var handler; if (arguments.length === 2) { handler = SchemaBoolean.$conditionalHandlers[$conditional]; if (handler) { return handler.call(this, val); } return this.cast(val); } return this.cast($conditional); }
...
}
if (geo) {
var numbertype = new Types.Number('__QueryCasting__');
var value = val[geo];
if (val.$maxDistance != null) {
val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
}
if (val.$minDistance != null) {
val.$minDistance = numbertype.castForQuery(val.$minDistance);
}
if (geo === '$within') {
var withinType = value.$center
...
checkRequired = function (value) { return value === true || value === false; }
...
// in here, `this` refers to the validating document.
// no validation when this path wasn't selected in the query.
if ('isSelected' in this && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
return true;
}
return ((typeof required === 'function') && !required.apply(this)) ||
_this.checkRequired(v, this);
};
this.originalRequiredValue = required;
if (typeof required === 'string') {
message = required;
required = undefined;
}
...
function SchemaBoolean(path, options) { SchemaType.call(this, path, options, 'Boolean'); }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function Document(obj, fields, skipId) { this.$__ = new InternalCache; this.$__.emitter = new EventEmitter(); this.isNew = true; this.errors = undefined; var schema = this.schema; if (typeof fields === 'boolean') { this.$__.strictMode = fields; fields = undefined; } else { this.$__.strictMode = schema.options && schema.options.strict; this.$__.selected = fields; } var required = schema.requiredPaths(true); for (var i = 0; i < required.length; ++i) { this.$__.activePaths.require(required[i]); } this.$__.emitter.setMaxListeners(0); this._doc = this.$__buildDoc(obj, fields, skipId); if (obj) { if (obj instanceof Document) { this.isNew = obj.isNew; } // Skip set hooks if (this.$__original_set) { this.$__original_set(obj, undefined, true); } else { this.set(obj, undefined, true); } } if (!schema.options.strict && obj) { var _this = this, keys = Object.keys(this._doc); keys.forEach(function(key) { if (!(key in schema.tree)) { defineKey(key, null, _this); } }); } applyQueue(this); }
...
* MissingSchema Error constructor.
*
* @inherits MongooseError
*/
function MissingSchemaError() {
var msg = 'Schema hasn\'t been registered for document.\n'
+ 'Use mongoose.Document(name, schema)';
MongooseError.call(this, msg);
Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee);
this.name = 'MissingSchemaError';
}
/*!
* Inherits from MongooseError.
...
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
n/a
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.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); } }
...
* - [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');
/*!
...
function SchemaType(path, options, instance) { this.path = path; this.instance = instance; this.validators = []; this.setters = []; this.getters = []; this.options = options; this._index = null; this.selected; for (var i in options) { if (this[i] && typeof this[i] === 'function') { // { unique: true, index: true } if (i === 'index' && this._index) { continue; } var opts = Array.isArray(options[i]) ? options[i] : [options[i]]; this[i].apply(this, opts); } } }
n/a
function VirtualType(options, name) { this.path = name; this.getters = []; this.setters = []; this.options = options || {}; }
n/a
function Document(obj, schema, fields, skipId, skipInit) { if (!(this instanceof Document)) { return new Document(obj, schema, fields, skipId, skipInit); } if (utils.isObject(schema) && !schema.instanceOfSchema) { schema = new Schema(schema); } // When creating EmbeddedDocument, it already has the schema and he doesn't need the _id schema = this.schema || schema; // Generate ObjectId if it is missing, but it requires a scheme if (!this.schema && schema.options._id) { obj = obj || {}; if (obj._id === undefined) { obj._id = new ObjectId(); } } if (!schema) { throw new MongooseError.MissingSchemaError(); } this.$__setSchema(schema); this.$__ = new InternalCache; this.$__.emitter = new EventEmitter(); this.isNew = true; this.errors = undefined; if (typeof fields === 'boolean') { this.$__.strictMode = fields; fields = undefined; } else { this.$__.strictMode = this.schema.options && this.schema.options.strict; this.$__.selected = fields; } var required = this.schema.requiredPaths(); for (var i = 0; i < required.length; ++i) { this.$__.activePaths.require(required[i]); } this.$__.emitter.setMaxListeners(0); this._doc = this.$__buildDoc(obj, fields, skipId); if (!skipInit && obj) { this.init(obj); } this.$__registerHooksFromSchema(); // apply methods for (var m in schema.methods) { this[m] = schema.methods[m]; } // apply statics for (var s in schema.statics) { this[s] = schema.statics[s]; } }
n/a
function ValidationError(instance) { this.errors = {}; if (instance && instance.constructor.name === 'model') { MongooseError.call(this, instance.constructor.modelName + ' validation failed'); } else { MongooseError.call(this, 'Validation failed'); } if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'ValidationError'; if (instance) { instance.errors = this.errors; } }
n/a
addListener = function () { return Document.$emitter[emitterFn].apply(Document.$emitter, arguments); }
n/a
emit = function () { return Document.$emitter[emitterFn].apply(Document.$emitter, arguments); }
...
You can intercept method arguments via middleware.
For example, this would allow you to broadcast changes about your Documents every time someone `set`s a path in your Document to
a new value:
```js
schema.pre('set', function (next, path, val, typel) {
// `this` is the current Document
this.emit('set', path, val);
// Pass control to the next pre
next();
});
```
Moreover, you can mutate the incoming `method` arguments so that subsequent middleware see different values for those arguments.
To do so, just pass the new values to `next`:
...
listeners = function () { return Document.$emitter[emitterFn].apply(Document.$emitter, arguments); }
...
doc[pair[0]].apply(doc, pair[1]);
}
}
}
Document.prototype.$__handleReject = function handleReject(err) {
// emit on the Model if listening
if (this.listeners('error').length) {
this.emit('error', err);
} else if (this.constructor.listeners && this.constructor.listeners('error').length) {
this.constructor.emit('error', err);
} else if (this.listeners && this.listeners('error').length) {
this.emit('error', err);
}
};
...
on = function () { return Document.$emitter[emitterFn].apply(Document.$emitter, arguments); }
...
require('http').createServer(function(req, res) {
if (req.url === '/favicon.ico') {
req.destroy();
res.statusCode = 204;
return res.end();
}
req.on('end', function() {
server.serve(req, res, function(err) {
if (err) {
console.error(err, req.url);
res.writeHead(err.status, err.headers);
res.end();
}
});
...
once = function () { return Document.$emitter[emitterFn].apply(Document.$emitter, arguments); }
...
aggregate(_this._pipeline, options || {});
decorateCursor(cursor);
resolve(cursor);
callback && callback(null, cursor);
});
return;
}
_this._model.collection.emitter.once('queue', function() {
var cursor = _this._model.collection.
aggregate(_this._pipeline, options || {});
decorateCursor(cursor);
resolve(cursor);
callback && callback(null, cursor);
});
});
...
removeAllListeners = function () { return Document.$emitter[emitterFn].apply(Document.$emitter, arguments); }
n/a
removeListener = function () { return Document.$emitter[emitterFn].apply(Document.$emitter, arguments); }
...
}
if (!(value && value.isMongooseDocumentArray) &&
(!options || !options.skipDocumentArrayCast)) {
value = new MongooseDocumentArray(value, this.path, doc);
if (prev && prev._handlers) {
for (var key in prev._handlers) {
doc.removeListener(key, prev._handlers[key]);
}
}
} else if (value && value.isMongooseDocumentArray) {
// We need to create a new array, otherwise change tracking will
// update the old doc (gh-4449)
value = new MongooseDocumentArray(value, this.path, doc);
}
...
setMaxListeners = function () { return Document.$emitter[emitterFn].apply(Document.$emitter, arguments); }
...
}
var required = this.schema.requiredPaths();
for (var i = 0; i < required.length; ++i) {
this.$__.activePaths.require(required[i]);
}
this.$__.emitter.setMaxListeners(0);
this._doc = this.$__buildDoc(obj, fields, skipId);
if (!skipInit && obj) {
this.init(obj);
}
this.$__registerHooksFromSchema();
...
function Document(obj, schema, fields, skipId, skipInit) { if (!(this instanceof Document)) { return new Document(obj, schema, fields, skipId, skipInit); } if (utils.isObject(schema) && !schema.instanceOfSchema) { schema = new Schema(schema); } // When creating EmbeddedDocument, it already has the schema and he doesn't need the _id schema = this.schema || schema; // Generate ObjectId if it is missing, but it requires a scheme if (!this.schema && schema.options._id) { obj = obj || {}; if (obj._id === undefined) { obj._id = new ObjectId(); } } if (!schema) { throw new MongooseError.MissingSchemaError(); } this.$__setSchema(schema); this.$__ = new InternalCache; this.$__.emitter = new EventEmitter(); this.isNew = true; this.errors = undefined; if (typeof fields === 'boolean') { this.$__.strictMode = fields; fields = undefined; } else { this.$__.strictMode = this.schema.options && this.schema.options.strict; this.$__.selected = fields; } var required = this.schema.requiredPaths(); for (var i = 0; i < required.length; ++i) { this.$__.activePaths.require(required[i]); } this.$__.emitter.setMaxListeners(0); this._doc = this.$__buildDoc(obj, fields, skipId); if (!skipInit && obj) { this.init(obj); } this.$__registerHooksFromSchema(); // apply methods for (var m in schema.methods) { this[m] = schema.methods[m]; } // apply statics for (var s in schema.statics) { this[s] = schema.statics[s]; } }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function MissingSchemaError() { var msg = 'Schema hasn\'t been registered for document.\n' + 'Use mongoose.Document(name, schema)'; MongooseError.call(this, msg); Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee); this.name = 'MissingSchemaError'; }
n/a
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function SchemaBuffer(key, options) { SchemaType.call(this, key, options, 'Buffer'); }
n/a
cast = function (value, doc, init) { var ret; if (SchemaType._isRef(this, value, doc, init)) { // wait! we may need to cast this to a document if (value === null || value === undefined) { return value; } // lazy load Document || (Document = require('./../document')); if (value instanceof Document) { value.$__.wasPopulated = true; return value; } // setting a populated path if (Buffer.isBuffer(value)) { return value; } else if (!utils.isObject(value)) { throw new CastError('buffer', value, this.path); } // Handle the case where user directly sets a populated // path to a plain object; cast to the Model used in // the population query. var path = doc.$__fullPath(this.path); var owner = doc.ownerDocument ? doc.ownerDocument() : doc; var pop = owner.populated(path, true); ret = new pop.options.model(value); ret.$__.wasPopulated = true; return ret; } // documents if (value && value._id) { value = value._id; } if (value && value.isMongooseBuffer) { return value; } if (Buffer.isBuffer(value)) { if (!value || !value.isMongooseBuffer) { value = new MongooseBuffer(value, [this.path, doc]); } return value; } else if (value instanceof Binary) { ret = new MongooseBuffer(value.value(true), [this.path, doc]); if (typeof value.sub_type !== 'number') { throw new CastError('buffer', value, this.path); } ret._subtype = value.sub_type; return ret; } if (value === null) { return value; } var type = typeof value; if (type === 'string' || type === 'number' || Array.isArray(value)) { if (type === 'number') { value = [value]; } ret = new MongooseBuffer(value, [this.path, doc]); return ret; } throw new CastError('buffer', value, this.path); }
...
doc[i] = obj[i];
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
...
castForQuery = function ($conditional, val) { var handler; if (arguments.length === 2) { handler = this.$conditionalHandlers[$conditional]; if (!handler) { throw new Error('Can\'t use ' + $conditional + ' with Buffer.'); } return handler.call(this, val); } val = $conditional; var casted = this.cast(val); return casted ? casted.toObject({ transform: false, virtuals: false }) : casted; }
...
}
if (geo) {
var numbertype = new Types.Number('__QueryCasting__');
var value = val[geo];
if (val.$maxDistance != null) {
val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
}
if (val.$minDistance != null) {
val.$minDistance = numbertype.castForQuery(val.$minDistance);
}
if (geo === '$within') {
var withinType = value.$center
...
checkRequired = function (value, doc) { if (SchemaType._isRef(this, value, doc, true)) { return !!value; } return !!(value && value.length); }
...
// in here, `this` refers to the validating document.
// no validation when this path wasn't selected in the query.
if ('isSelected' in this && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
return true;
}
return ((typeof required === 'function') && !required.apply(this)) ||
_this.checkRequired(v, this);
};
this.originalRequiredValue = required;
if (typeof required === 'string') {
message = required;
required = undefined;
}
...
function SchemaBuffer(key, options) { SchemaType.call(this, key, options, 'Buffer'); }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function Collection(name, conn, opts) { if (opts === void 0) { opts = {}; } if (opts.capped === void 0) { opts.capped = {}; } opts.bufferCommands = undefined === opts.bufferCommands ? true : opts.bufferCommands; if (typeof opts.capped === 'number') { opts.capped = {size: opts.capped}; } this.opts = opts; this.name = name; this.collectionName = name; this.conn = conn; this.queue = []; this.buffer = this.opts.bufferCommands; this.emitter = new EventEmitter(); if (STATES.connected === this.conn.readyState) { this.onOpen(); } }
...
model.prototype.$__setSchema(schema);
var collectionOptions = {
bufferCommands: schema.options.bufferCommands,
capped: schema.options.capped
};
model.prototype.collection = connection.collection(
collectionName
, collectionOptions
);
// apply methods and statics
applyMethods(model, schema);
applyStatics(model, schema);
...
addQueue = function (name, args) { this.queue.push([name, args]); return this; }
...
create();
}));
};
setImmediate(function() {
// If buffering is off, do this manually.
if (options._automatic && !model.collection.collection) {
model.collection.addQueue(create, []);
} else {
create();
}
});
}
function _handleSafe(options) {
...
doQueue = function () { for (var i = 0, l = this.queue.length; i < l; i++) { if (typeof this.queue[i][0] === 'function') { this.queue[i][0].apply(this, this.queue[i][1]); } else { this[this.queue[i][0]].apply(this, this.queue[i][1]); } } this.queue = []; var _this = this; process.nextTick(function() { _this.emitter.emit('queue'); }); return this; }
...
* Called when the database connects
*
* @api private
*/
Collection.prototype.onOpen = function() {
this.buffer = false;
this.doQueue();
};
/**
* Called when the database disconnects
*
* @api private
*/
...
ensureIndex = function () { throw new Error('Collection#ensureIndex unimplemented by driver'); }
...
var indexFields = index[0];
var options = index[1];
_handleSafe(options);
indexSingleStart(indexFields, options);
model.collection.ensureIndex(indexFields, options, utils.tick(function(err, name) {
indexSingleDone(err, indexFields, options, name);
if (err) {
return done(err);
}
create();
}));
};
...
find = function () { throw new Error('Collection#find unimplemented by driver'); }
...
//
});
```
Or we can find documents from the same collection
```js
MyModel.find({}, function (err, docs) {
// docs.forEach
});
```
You can also `findOne`, `findById`, `update`, etc. For more details check out [the docs](http://mongoosejs.com/docs/queries.html
).
**Important!** If you opened a separate connection using `mongoose.createConnection()` but attempt to access the model through `
mongoose.model('ModelName')` it will not work as expected since it is not hooked up to an active db connection. In this
case access your model through the connection you created:
...
findAndModify = function () { throw new Error('Collection#findAndModify unimplemented by driver'); }
...
};
if (opts.runValidators && doValidate) {
var _callback = function(error) {
if (error) {
return callback(error);
}
_this._collection.findAndModify(castedQuery, castedDoc, opts, utils.tick(function(error
, res) {
return cb(error, res ? res.value : res, res);
}));
};
try {
doValidate(_callback);
} catch (error) {
...
findOne = function () { throw new Error('Collection#findOne unimplemented by driver'); }
...
};
/**
* Checks if `path` was selected in the source query which initialized this document.
*
* ####Example
*
* Thing.findOne().select('name').exec(function (err, doc) {
* doc.isSelected('name') // true
* doc.isSelected('age') // false
* })
*
* @param {String} path
* @return {Boolean}
* @api public
...
getIndexes = function () { throw new Error('Collection#getIndexes unimplemented by driver'); }
n/a
insert = function () { throw new Error('Collection#insert unimplemented by driver'); }
...
setTimeout(function() {
callback(new Error('document must have an _id before saving'));
}, 0);
return;
}
this.$__version(true, obj);
this.collection.insert(obj, options.safe, function(err, ret) {
if (err) {
_this.isNew = true;
_this.emit('isNew', true);
_this.constructor.emit('isNew', true);
callback(err);
return;
...
mapReduce = function () { throw new Error('Collection#mapReduce unimplemented by driver'); }
...
* `o` is an object specifying all mapReduce options as well as the map and reduce functions. All options are delegated to the driver
implementation. See [node-mongodb-native mapReduce() documentation](http://mongodb.github.io/node-mongodb-native/api-generated/
collection.html#mapreduce) for more detail about options.
*
* ####Example:
*
* var o = {};
* o.map = function () { emit(this.name, 1) }
* o.reduce = function (k, vals) { return vals.length }
* User.mapReduce(o, function (err, results) {
* console.log(results)
* })
*
* ####Other options:
*
* - `query` {Object} query filter object.
* - `sort` {Object} sort input objects using this key
...
onClose = function () { if (this.opts.bufferCommands) { this.buffer = true; } }
...
case 1: // connected
case 4: // unauthorized
this.readyState = STATES.disconnecting;
this.doClose(function(err) {
if (err) {
_this.error(err, callback);
} else {
_this.onClose();
callback && callback();
}
});
break;
case 2: // connecting
this.once('open', function() {
...
onOpen = function () { this.buffer = false; this.doQueue(); }
...
this.collectionName = name;
this.conn = conn;
this.queue = [];
this.buffer = this.opts.bufferCommands;
this.emitter = new EventEmitter();
if (STATES.connected === this.conn.readyState) {
this.onOpen();
}
}
/**
* The collection name
*
* @api public
...
save = function () { throw new Error('Collection#save unimplemented by driver'); }
...
Then Mongoose will create the model for your __tickets__ collection, not your __ticket__ collection.
Once we have our model, we can then instantiate it, and save it:
```js
var instance = new MyModel();
instance.my.key = 'hello';
instance.save(function (err) {
//
});
```
Or we can find documents from the same collection
```js
...
update = function () { throw new Error('Collection#update unimplemented by driver'); }
...
}
/**
* Sends an update command with this document `_id` as the query selector.
*
* ####Example:
*
* weirdCar.update({$inc: {wheels:1}}, { w: 1 }, callback);
*
* ####Valid options:
*
* - same as in [Model.update](#model_Model.update)
*
* @see Model.update #model_Model.update
* @param {Object} doc
...
function flatten(update, path, options) { var keys; if (update && utils.isMongooseObject(update) && !Buffer.isBuffer(update)) { keys = Object.keys(update.toObject({ transform: false, virtuals: false })); } else { keys = Object.keys(update || {}); } var numKeys = keys.length; var result = {}; path = path ? path + '.' : ''; for (var i = 0; i < numKeys; ++i) { var key = keys[i]; var val = update[key]; result[path + key] = val; if (shouldFlatten(val)) { if (options && options.skipArrays && Array.isArray(val)) { continue; } var flat = flatten(val, path + key); for (var k in flat) { result[k] = flat[k]; } if (Array.isArray(val)) { result[path + key] = val; } } } return result; }
...
if (mod.options.match) {
match = utils.object.shallowCopy(mod.options.match);
} else {
match = {};
}
var ids = utils.array.flatten(mod.ids, flatten);
ids = utils.array.unique(ids);
if (ids.length === 0 || ids.every(utils.isNullOrUndefined)) {
--_remaining;
continue;
}
...
function modifiedPaths(update, path, result) { var keys = Object.keys(update || {}); var numKeys = keys.length; result = result || {}; path = path ? path + '.' : ''; for (var i = 0; i < numKeys; ++i) { var key = keys[i]; var val = update[key]; result[path + key] = true; if (utils.isMongooseObject(val) && !Buffer.isBuffer(val)) { val = val.toObject({ transform: false, virtuals: false }); } if (shouldFlatten(val)) { modifiedPaths(val, path + key, result); } } return result; }
...
*/
Document.prototype.isModified = function(paths) {
if (paths) {
if (!Array.isArray(paths)) {
paths = paths.split(' ');
}
var modified = this.modifiedPaths();
var directModifiedPaths = Object.keys(this.$__.activePaths.states.modify);
var isModifiedChild = paths.some(function(path) {
return !!~modified.indexOf(path);
});
return isModifiedChild || paths.some(function(path) {
return directModifiedPaths.some(function(mod) {
return mod === path || path.indexOf(mod + '.') === 0;
...
function Connection(base) { this.base = base; this.collections = {}; this.models = {}; this.config = {autoIndex: true}; this.replica = false; this.hosts = null; this.host = null; this.port = null; this.user = null; this.pass = null; this.name = null; this.options = null; this.otherDbs = []; this._readyState = STATES.disconnected; this._closeCalled = false; this._hasOpened = false; }
n/a
_close = function (callback) { var _this = this; this._closeCalled = true; switch (this.readyState) { case 0: // disconnected callback && callback(); break; case 1: // connected case 4: // unauthorized this.readyState = STATES.disconnecting; this.doClose(function(err) { if (err) { _this.error(err, callback); } else { _this.onClose(); callback && callback(); } }); break; case 2: // connecting this.once('open', function() { _this.close(callback); }); break; case 3: // disconnecting if (!callback) { break; } this.once('close', function() { callback(); }); break; } return this; }
...
* @api public
*/
Connection.prototype.close = function(callback) {
var _this = this;
var Promise = PromiseProvider.get();
return new Promise.ES6(function(resolve, reject) {
_this._close(function(error) {
callback && callback(error);
if (error) {
reject(error);
return;
}
resolve();
});
...
_open = function (emit, callback) { this.readyState = STATES.connecting; this._closeCalled = false; var _this = this; var method = this.replica ? 'doOpenSet' : 'doOpen'; // open connection this[method](function(err) { if (err) { _this.readyState = STATES.disconnected; if (_this._hasOpened) { if (callback) { callback(err); } } else { _this.error(err, emit && callback); } return; } _this.onOpen(callback); }); }
...
this.name = database;
this.host = host;
this.port = port;
var _this = this;
var promise = new Promise.ES6(function(resolve, reject) {
_this._open(true, function(error) {
callback && callback(error);
if (error) {
// Error can be on same tick re: christkv/mongodb-core#157
setImmediate(function() {
reject(error);
if (!callback && !promise.$hasHandler) {
_this.emit('error', error);
...
authMechanismDoesNotRequirePassword = function () { if (this.options && this.options.auth) { return authMechanismsWhichDontRequirePassword.indexOf(this.options.auth.authMechanism) >= 0; } return true; }
...
* username and password are both provided than authentication is needed, but in some cases a
* password is not required.
* @api private
* @return {Boolean} true if the connection should be authenticated after it is opened, otherwise false.
*/
Connection.prototype.shouldAuthenticate = function() {
return (this.user !== null && this.user !== void 0) &&
((this.pass !== null || this.pass !== void 0) || this.authMechanismDoesNotRequirePassword
());
};
/**
* @brief Returns a boolean value that specifies if the current authentication mechanism needs a
* password to authenticate according to the auth objects passed into the open/openSet methods.
* @api private
* @return {Boolean} true if the authentication mechanism specified in the options object requires
...
close = function (callback) { var _this = this; var Promise = PromiseProvider.get(); return new Promise.ES6(function(resolve, reject) { _this._close(function(error) { callback && callback(error); if (error) { reject(error); return; } resolve(); }); }); }
...
* For practical reasons, a Connection equals a Db.
*
* @param {Mongoose} base a mongoose instance
* @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
* @event `connecting`: Emitted when `connection.{open,openSet}()` is executed on this connection.
* @event `connected`: Emitted when this connection successfully connects to the db. May be emitted _multiple_ times in `reconnected
` scenarios.
* @event `open`: Emitted after we `connected` and `onOpen` is executed on all of this connections models.
* @event `disconnecting`: Emitted when `connection.close()` was executed.
* @event `disconnected`: Emitted after getting disconnected from the db.
* @event `close`: Emitted after we `disconnected` and `onClose` executed on all of this connections models.
* @event `reconnected`: Emitted after we `connected` and subsequently `disconnected`, followed by successfully another successfull
connection.
* @event `error`: Emitted when an error occurs on this connection.
* @event `fullsetup`: Emitted in a replica-set scenario, when primary and at least one seconaries specified in the connection string
are connected.
* @event `all`: Emitted in a replica-set scenario, when all nodes specified in the connection string are connected.
* @api public
...
collection = function (name, options) { if (!(name in this.collections)) { this.collections[name] = new Collection(name, this, options); } return this.collections[name]; }
...
model.prototype.$__setSchema(schema);
var collectionOptions = {
bufferCommands: schema.options.bufferCommands,
capped: schema.options.capped
};
model.prototype.collection = connection.collection(
collectionName
, collectionOptions
);
// apply methods and statics
applyMethods(model, schema);
applyStatics(model, schema);
...
dropDatabase = function (callback) { var Promise = PromiseProvider.get(); var _this = this; var promise = new Promise.ES6(function(resolve, reject) { if (_this.readyState !== STATES.connected) { _this.on('open', function() { _this.db.dropDatabase(function(error) { if (error) { reject(error); } else { resolve(); } }); }); } else { _this.db.dropDatabase(function(error) { if (error) { reject(error); } else { resolve(); } }); } }); if (callback) { promise.then(function() { callback(); }, callback); } return promise; }
...
Connection.prototype.dropDatabase = function(callback) {
var Promise = PromiseProvider.get();
var _this = this;
var promise = new Promise.ES6(function(resolve, reject) {
if (_this.readyState !== STATES.connected) {
_this.on('open', function() {
_this.db.dropDatabase(function(error) {
if (error) {
reject(error);
} else {
resolve();
}
});
});
...
error = function (err, callback) { if (callback) { return callback(err); } this.emit('error', err); }
...
res.statusCode = 204;
return res.end();
}
req.on('end', function() {
server.serve(req, res, function(err) {
if (err) {
console.error(err, req.url);
res.writeHead(err.status, err.headers);
res.end();
}
});
});
req.resume();
}).listen(8088);
...
model = function (name, schema, collection) { // collection name discovery if (typeof schema === 'string') { collection = schema; schema = false; } if (utils.isObject(schema) && !schema.instanceOfSchema) { schema = new Schema(schema); } if (schema && !schema.instanceOfSchema) { throw new Error('The 2nd parameter to `mongoose.model()` should be a ' + 'schema or a POJO'); } if (this.models[name] && !collection) { // model exists but we are not subclassing with custom collection if (schema && schema.instanceOfSchema && schema !== this.models[name].schema) { throw new MongooseError.OverwriteModelError(name); } return this.models[name]; } var opts = {cache: false, connection: this}; var model; if (schema && schema.instanceOfSchema) { // compile a model model = this.base.model(name, schema, collection, opts); // only the first model with this name is cached to allow // for one-offs with custom collection names etc. if (!this.models[name]) { this.models[name] = model; } model.init(); return model; } if (this.models[name] && collection) { // subclassing current model with alternate collection model = this.models[name]; schema = model.prototype.schema; var sub = model.__subclass(this, schema, collection); // do not cache the sub model return sub; } // lookup model in mongoose module model = this.base.models[name]; if (!model) { throw new MongooseError.MissingSchemaError(name); } if (this === model.prototype.db && (!collection || collection === model.collection.name)) { // model already uses this connection. // only the first model with this name is cached to allow // for one-offs with custom collection names etc. if (!this.models[name]) { this.models[name] = model; } return model; } this.models[name] = model.__subclass(this, schema, collection); return this.models[name]; }
...
});
```
Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
### Accessing a Model
Once we define a model through `mongoose.model('ModelName', mySchema)`, we
can access it through the same function
```js
var myModel = mongoose.model('ModelName');
```
Or just do it all at once
...
modelNames = function () { return Object.keys(this.models); }
n/a
onClose = function () { this.readyState = STATES.disconnected; // avoid having the collection subscribe to our event emitter // to prevent 0.3 warning for (var i in this.collections) { if (utils.object.hasOwnProperty(this.collections, i)) { this.collections[i].onClose(); } } this.emit('close'); }
...
case 1: // connected
case 4: // unauthorized
this.readyState = STATES.disconnecting;
this.doClose(function(err) {
if (err) {
_this.error(err, callback);
} else {
_this.onClose();
callback && callback();
}
});
break;
case 2: // connecting
this.once('open', function() {
...
onOpen = function (callback) { var _this = this; function open(err, isAuth) { if (err) { _this.readyState = isAuth ? STATES.unauthorized : STATES.disconnected; _this.error(err, callback); return; } _this.readyState = STATES.connected; // avoid having the collection subscribe to our event emitter // to prevent 0.3 warning for (var i in _this.collections) { if (utils.object.hasOwnProperty(_this.collections, i)) { _this.collections[i].onOpen(); } } callback && callback(); _this.emit('open'); } // re-authenticate if we're not already connected #3871 if (this._readyState !== STATES.connected && this.shouldAuthenticate()) { _this.db.authenticate(_this.user, _this.pass, _this.options.auth, function(err) { open(err, true); }); } else { open(); } }
...
this.collectionName = name;
this.conn = conn;
this.queue = [];
this.buffer = this.opts.bufferCommands;
this.emitter = new EventEmitter();
if (STATES.connected === this.conn.readyState) {
this.onOpen();
}
}
/**
* The collection name
*
* @api public
...
open = function (host, database, port, options, callback) { var parsed; var Promise = PromiseProvider.get(); var err; if (typeof database === 'string') { switch (arguments.length) { case 2: port = 27017; break; case 3: switch (typeof port) { case 'function': callback = port; port = 27017; break; case 'object': options = port; port = 27017; break; } break; case 4: if (typeof options === 'function') { callback = options; options = {}; } } } else { switch (typeof database) { case 'function': callback = database; database = undefined; break; case 'object': options = database; database = undefined; callback = port; break; } if (!rgxProtocol.test(host)) { host = 'mongodb://' + host; } try { parsed = muri(host); } catch (err) { this.error(err, callback); return new Promise.ES6(function(resolve, reject) { reject(err); }); } database = parsed.db; host = parsed.hosts[0].host || parsed.hosts[0].ipc; port = parsed.hosts[0].port || 27017; } this.options = this.parseOptions(options, parsed && parsed.options); // make sure we can open if (STATES.disconnected !== this.readyState) { err = new Error('Trying to open unclosed connection.'); err.state = this.readyState; this.error(err, callback); return new Promise.ES6(function(resolve, reject) { reject(err); }); } if (!host) { err = new Error('Missing hostname.'); this.error(err, callback); return new Promise.ES6(function(resolve, reject) { reject(err); }); } if (!database) { err = new Error('Missing database name.'); this.error(err, callback); return new Promise.ES6(function(resolve, reject) { reject(err); }); } // authentication if (this.optionsProvideAuthenticationData(options)) { this.user = options.user; this.pass = options.pass; } else if (parsed && parsed.auth) { this.user = parsed.auth.user; this.pass = parsed.auth.pass; // Check hostname for user/pass } else if (/@/.test(host) && /:/.test(host.split('@')[0])) { host = host.split('@'); if (host.length > 2) { err = new Error('Username and password must be URI encoded if they ' + 'contain "@", see http://bit.ly/2nRYRyq'); this.error(err, callback); return new Promise.ES6(function(resolve, reject) { reject(err); }); } var auth = host.shift().split(':'); if (auth.length > 2) { err = new Error('Username and password must be URI encoded if they ' + 'contain ":", see http://bit.ly/2nRYRyq'); this.error(err, callback); return new Promise.ES6(function(resolve, reject) { reject(err); }); } host = host.pop(); this.user = auth[0]; this.pass = auth[1]; } else { this.user = this.pass = undefined; } // global configuration options if (options && options.config) { this.config.autoIndex = options.config.autoIndex !== false; } this.name = database; this.host = host; this.port = port; var _this = this; var promise = new Promise.ES6(function(resolve, reject) { _this._open(true, function(error) { callback && callback(error); if (error) { // Error can be on same tick re: christkv/mongodb-core#157 setImmediate(function() { reject(error); if (!callback && !promise.$hasHandler) { _this.emit('error', error); } }); return; } resolve(); }); }); return promise; }
...
*
* _Options passed take precedence over options included in connection strings._
*
* ####Notes:
*
* _If connecting to multiple mongos servers, set the `mongos` option to true._
*
* conn.open('mongodb://mongosA:27501,mongosB:27501', { mongos: true },
cb);
*
* Mongoose forces the db option `forceServerObjectId` false and cannot be overridden.
* Mongoose defaults the server `auto_reconnect` options to true which can be overridden.
* See the node-mongodb-native driver instance for options that it understands.
*
* _Options passed take precedence over options included in connection strings._
*
...
openSet = function (uris, database, options, callback) { if (!rgxProtocol.test(uris)) { uris = 'mongodb://' + uris; } var Promise = PromiseProvider.get(); switch (arguments.length) { case 3: switch (typeof database) { case 'string': this.name = database; break; case 'object': callback = options; options = database; database = null; break; } if (typeof options === 'function') { callback = options; options = {}; } break; case 2: switch (typeof database) { case 'string': this.name = database; break; case 'function': callback = database; database = null; break; case 'object': options = database; database = null; break; } } if (typeof database === 'string') { this.name = database; } var parsed; try { parsed = muri(uris); } catch (err) { this.error(err, callback); return new Promise.ES6(function(resolve, reject) { reject(err); }); } if (!this.name) { this.name = parsed.db; } this.hosts = parsed.hosts; this.options = this.parseOptions(options, parsed && parsed.options); this.replica = true; if (!this.name) { var err = new Error('No database name provided for replica set'); this.error(err, callback); return new Promise.ES6(function(resolve, reject) { reject(err); }); } // authentication if (this.optionsProvideAuthenticationData(options)) { this.user = options.user; this.pass = options.pass; } else if (parsed && parsed.auth) { this.user = parsed.auth.user; this.pass = parsed.auth.pass; } else { this.user = this.pass = undefined; } // global configuration options if (options && options.config) { this.config.autoIndex = options.config.autoIndex !== false; } var _this = this; var emitted = false; var promise = new Promise.ES6(function(resolve, reject) { _this._open(true, function(error) { callback && callback(error); if (error) { reject(error); if (!callback && !promise.$hasHandler && !emitted) { emitted = true; _this.emit('error', error); } return; } resolve(); }); }); return promise; }
...
/**
* Opens the connection to a replica set.
*
* ####Example:
*
* var db = mongoose.createConnection();
* db.openSet("mongodb://user:pwd@localhost:27020,localhost:27021,localhost:27012
/mydb");
*
* The database name and/or auth need only be included in one URI.
* The `options` is a hash which is passed to the internal driver connection object.
*
* Valid `options`
*
* db - passed to the connection db instance
...
optionsProvideAuthenticationData = function (options) { return (options) && (options.user) && ((options.pass) || this.authMechanismDoesNotRequirePassword()); }
...
this.error(err, callback);
return new Promise.ES6(function(resolve, reject) {
reject(err);
});
}
// authentication
if (this.optionsProvideAuthenticationData(options)) {
this.user = options.user;
this.pass = options.pass;
} else if (parsed && parsed.auth) {
this.user = parsed.auth.user;
this.pass = parsed.auth.pass;
// Check hostname for user/pass
...
shouldAuthenticate = function () { return (this.user !== null && this.user !== void 0) && ((this.pass !== null || this.pass !== void 0) || this.authMechanismDoesNotRequirePassword()); }
...
}
callback && callback();
_this.emit('open');
}
// re-authenticate if we're not already connected #3871
if (this._readyState !== STATES.connected && this.shouldAuthenticate()) {
_this.db.authenticate(_this.user, _this.pass, _this.options.auth, function(err) {
open(err, true);
});
} else {
open();
}
};
...
function SchemaDate(key, options) { SchemaType.call(this, key, options, 'Date'); }
n/a
cast = function (value) { // If null or undefined if (value === null || value === void 0 || value === '') { return null; } if (value instanceof Date) { if (isNaN(value.valueOf())) { throw new CastError('date', value, this.path); } return value; } var date; if (typeof value === 'boolean') { throw new CastError('date', value, this.path); } if (value instanceof Number || typeof value === 'number' || String(value) == Number(value)) { // support for timestamps date = new Date(Number(value)); } else if (value.valueOf) { // support for moment.js date = new Date(value.valueOf()); } if (!isNaN(date.valueOf())) { return date; } throw new CastError('date', value, this.path); }
...
doc[i] = obj[i];
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
...
castForQuery = function ($conditional, val) { var handler; if (arguments.length !== 2) { return this.cast($conditional); } handler = this.$conditionalHandlers[$conditional]; if (!handler) { throw new Error('Can\'t use ' + $conditional + ' with Date.'); } return handler.call(this, val); }
...
}
if (geo) {
var numbertype = new Types.Number('__QueryCasting__');
var value = val[geo];
if (val.$maxDistance != null) {
val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
}
if (val.$minDistance != null) {
val.$minDistance = numbertype.castForQuery(val.$minDistance);
}
if (geo === '$within') {
var withinType = value.$center
...
checkRequired = function (value) { return value instanceof Date; }
...
// in here, `this` refers to the validating document.
// no validation when this path wasn't selected in the query.
if ('isSelected' in this && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
return true;
}
return ((typeof required === 'function') && !required.apply(this)) ||
_this.checkRequired(v, this);
};
this.originalRequiredValue = required;
if (typeof required === 'string') {
message = required;
required = undefined;
}
...
function SchemaDate(key, options) { SchemaType.call(this, key, options, 'Date'); }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
expires = function (when) { if (!this._index || this._index.constructor.name !== 'Object') { this._index = {}; } this._index.expires = when; utils.expires(this._index); return this; }
...
* @api public
*/
Schema.prototype.index = function(fields, options) {
options || (options = {});
if (options.expires) {
utils.expires(options);
}
this._indexes.push([fields, options]);
return this;
};
/**
...
max = function (value, message) { if (this.maxValidator) { this.validators = this.validators.filter(function(v) { return v.validator !== this.maxValidator; }, this); } if (value) { var msg = message || MongooseError.messages.Date.max; msg = msg.replace(/{MAX}/, (value === Date.now ? 'Date.now()' : this.cast(value).toString())); var _this = this; this.validators.push({ validator: this.maxValidator = function(val) { var max = (value === Date.now ? value() : _this.cast(value)); return val === null || val.valueOf() <= max.valueOf(); }, message: msg, type: 'max', max: value }); } return this; }
n/a
min = function (value, message) { if (this.minValidator) { this.validators = this.validators.filter(function(v) { return v.validator !== this.minValidator; }, this); } if (value) { var msg = message || MongooseError.messages.Date.min; msg = msg.replace(/{MIN}/, (value === Date.now ? 'Date.now()' : this.cast(value).toString())); var _this = this; this.validators.push({ validator: this.minValidator = function(val) { var min = (value === Date.now ? value() : _this.cast(value)); return val === null || val.valueOf() >= min.valueOf(); }, message: msg, type: 'min', min: value }); } return this; }
n/a
function Decimal128(key, options) { SchemaType.call(this, key, options, 'Decimal128'); }
n/a
cast = function (value, doc, init) { if (SchemaType._isRef(this, value, doc, init)) { // wait! we may need to cast this to a document if (value === null || value === undefined) { return value; } // lazy load Document || (Document = require('./../document')); if (value instanceof Document) { value.$__.wasPopulated = true; return value; } // setting a populated path if (value instanceof Decimal128Type) { return value; } else if (Buffer.isBuffer(value) || !utils.isObject(value)) { throw new CastError('Decimal128', value, this.path); } // Handle the case where user directly sets a populated // path to a plain object; cast to the Model used in // the population query. var path = doc.$__fullPath(this.path); var owner = doc.ownerDocument ? doc.ownerDocument() : doc; var pop = owner.populated(path, true); var ret = value; if (!doc.$__.populated || !doc.$__.populated[path] || !doc.$__.populated[path].options || !doc.$__.populated[path].options.options || !doc.$__.populated[path].options.options.lean) { ret = new pop.options.model(value); ret.$__.wasPopulated = true; } return ret; } if (value === null || value === undefined) { return value; } if (value instanceof Decimal128Type) { return value; } if (typeof value === 'string') { return Decimal128Type.fromString(value); } if (Buffer.isBuffer(value)) { return new Decimal128Type(value); } throw new CastError('Decimal128', value, this.path); }
...
doc[i] = obj[i];
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
...
castForQuery = function ($conditional, val) { var handler; if (arguments.length === 2) { handler = this.$conditionalHandlers[$conditional]; if (!handler) { throw new Error('Can\'t use ' + $conditional + ' with ObjectId.'); } return handler.call(this, val); } return this.cast($conditional); }
...
}
if (geo) {
var numbertype = new Types.Number('__QueryCasting__');
var value = val[geo];
if (val.$maxDistance != null) {
val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
}
if (val.$minDistance != null) {
val.$minDistance = numbertype.castForQuery(val.$minDistance);
}
if (geo === '$within') {
var withinType = value.$center
...
function checkRequired(value, doc) { if (SchemaType._isRef(this, value, doc, true)) { return !!value; } return value instanceof Decimal128Type; }
...
// in here, `this` refers to the validating document.
// no validation when this path wasn't selected in the query.
if ('isSelected' in this && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
return true;
}
return ((typeof required === 'function') && !required.apply(this)) ||
_this.checkRequired(v, this);
};
this.originalRequiredValue = required;
if (typeof required === 'string') {
message = required;
required = undefined;
}
...
function Decimal128(key, options) { SchemaType.call(this, key, options, 'Decimal128'); }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function DisconnectedError(connectionString) { MongooseError.call(this, 'Ran out of retries trying to reconnect to "' + connectionString + '". Try setting `server.reconnectTries` and ' + '`server.reconnectInterval` to something higher.'); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'DisconnectedError'; }
n/a
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function DivergentArrayError(paths) { var msg = 'For your own good, using `document.save()` to update an array ' + 'which was selected using an $elemMatch projection OR ' + 'populated using skip, limit, query conditions, or exclusion of ' + 'the _id field when the operation results in a $pop or $set of ' + 'the entire array is not supported. The following ' + 'path(s) would have been modified unsafely:\n' + ' ' + paths.join('\n ') + '\n' + 'Use Model.update() to update these arrays instead.'; // TODO write up a docs page (FAQ) and link to it MongooseError.call(this, msg); Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee); this.name = 'DivergentArrayError'; }
n/a
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function Document(obj, fields, skipId) { this.$__ = new InternalCache; this.$__.emitter = new EventEmitter(); this.isNew = true; this.errors = undefined; var schema = this.schema; if (typeof fields === 'boolean') { this.$__.strictMode = fields; fields = undefined; } else { this.$__.strictMode = schema.options && schema.options.strict; this.$__.selected = fields; } var required = schema.requiredPaths(true); for (var i = 0; i < required.length; ++i) { this.$__.activePaths.require(required[i]); } this.$__.emitter.setMaxListeners(0); this._doc = this.$__buildDoc(obj, fields, skipId); if (obj) { if (obj instanceof Document) { this.isNew = obj.isNew; } // Skip set hooks if (this.$__original_set) { this.$__original_set(obj, undefined, true); } else { this.set(obj, undefined, true); } } if (!schema.options.strict && obj) { var _this = this, keys = Object.keys(this._doc); keys.forEach(function(key) { if (!(key in schema.tree)) { defineKey(key, null, _this); } }); } applyQueue(this); }
n/a
function ValidationError(instance) { this.errors = {}; if (instance && instance.constructor.name === 'model') { MongooseError.call(this, instance.constructor.modelName + ' validation failed'); } else { MongooseError.call(this, 'Validation failed'); } if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'ValidationError'; if (instance) { instance.errors = this.errors; } }
n/a
_lazySetupHooks = function (proto, methodName, errorCb) { if ('undefined' === typeof proto[methodName].numAsyncPres) { this.$hook(methodName, proto[methodName], errorCb); } }
n/a
removePost = function (name, fnToRemove) { var proto = this.prototype || this , posts = proto._posts || (proto._posts || {}); if (!posts[name]) return this; if (arguments.length === 1) { // Remove all post callbacks for hook `name` posts[name].length = 0; } else { posts[name] = posts[name].filter( function (currFn) { return currFn !== fnToRemove; }); } return this; }
n/a
removePre = function (name, fnToRemove) { var proto = this.prototype || this , pres = proto._pres || (proto._pres || {}); if (!pres[name]) return this; if (arguments.length === 1) { // Remove all pre callbacks for hook `name` pres[name].length = 0; } else { pres[name] = pres[name].filter( function (currFn) { return currFn !== fnToRemove; }); } return this; }
n/a
_lazySetupHooks = function (proto, methodName, errorCb) { if ('undefined' === typeof proto[methodName].numAsyncPres) { this.$hook(methodName, proto[methodName], errorCb); } }
n/a
addListener = function () { return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
n/a
depopulate = function (path) { var populatedIds = this.populated(path); if (!populatedIds) { return; } delete this.$__.populated[path]; this.set(path, populatedIds); return this; }
...
/**
* Takes a populated field and returns it to its unpopulated state.
*
* ####Example:
*
* Model.findOne().populate('author').exec(function (err, doc) {
* console.log(doc.author.name); // Dr.Seuss
* console.log(doc.depopulate('author'));
* console.log(doc.author); // '5144cf8050f071d979c118a7'
* })
*
* If the path was not populated, this is a no-op.
*
* @param {String} path
* @return {Document} this
...
emit = function () { return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
...
You can intercept method arguments via middleware.
For example, this would allow you to broadcast changes about your Documents every time someone `set`s a path in your Document to
a new value:
```js
schema.pre('set', function (next, path, val, typel) {
// `this` is the current Document
this.emit('set', path, val);
// Pass control to the next pre
next();
});
```
Moreover, you can mutate the incoming `method` arguments so that subsequent middleware see different values for those arguments.
To do so, just pass the new values to `next`:
...
equals = function (doc) { if (!doc) { return false; } var tid = this.get('_id'); var docid = doc.get ? doc.get('_id') : doc; if (!tid && !docid) { return deepEqual(this, doc); } return tid && tid.equals ? tid.equals(docid) : tid === docid; }
...
var tid = this.get('_id');
var docid = doc.get ? doc.get('_id') : doc;
if (!tid && !docid) {
return deepEqual(this, doc);
}
return tid && tid.equals
? tid.equals(docid)
: tid === docid;
};
/**
* Populates document references, executing the `callback` when complete.
* If you want to use promises instead, use this function with
* [`execPopulate()`](#document_Document-execPopulate)
...
execPopulate = function () { var Promise = PromiseProvider.get(); var _this = this; return new Promise.ES6(function(resolve, reject) { _this.populate(function(error, res) { if (error) { reject(error); } else { resolve(res); } }); }); }
...
*
* // summary
* doc.populate(path) // not executed
* doc.populate(options); // not executed
* doc.populate(path, callback) // executed
* doc.populate(options, callback); // executed
* doc.populate(callback); // executed
* doc.populate(options).execPopulate() // executed, returns promise
*
*
* ####NOTE:
*
* Population does not occur unless a `callback` is passed *or* you explicitly
* call `execPopulate()`.
* Passing the same path a second time will overwrite the previous path options.
...
get = function (path, type) { var adhoc; if (type) { adhoc = Schema.interpretAsType(path, type, this.schema.options); } var schema = this.$__path(path) || this.schema.virtualpath(path), pieces = path.split('.'), obj = this._doc; for (var i = 0, l = pieces.length; i < l; i++) { obj = obj === null || obj === void 0 ? undefined : obj[pieces[i]]; } if (adhoc) { obj = adhoc.cast(obj); } // Check if this path is populated - don't apply getters if it is, // because otherwise its a nested object. See gh-3357 if (schema && !this.populated(path)) { obj = schema.applyGetters(obj, this); } return obj; }
...
// a setter
Comment.path('name').set(function (v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function (next) {
notify(this.get('email'));
next();
});
```
Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
### Accessing a Model
...
getValue = function (path) { return utils.getValue(path, this._doc); }
...
// If doc._id is not null or undefined
if (doc._id !== null && doc._id !== undefined &&
opts && opts.populated && opts.populated.length) {
var id = String(doc._id);
for (var i = 0; i < opts.populated.length; ++i) {
var item = opts.populated[i];
if (item.isVirtual) {
this.populated(item.path, utils.getValue(item.path, doc), item);
} else {
this.populated(item.path, item._docs[id], item);
}
}
}
init(this, doc, this._doc);
...
init = function (doc, opts, fn) { // do not prefix this method with $__ since its // used by public hooks if (typeof opts === 'function') { fn = opts; opts = null; } this.isNew = false; // handle docs with populated paths // If doc._id is not null or undefined if (doc._id !== null && doc._id !== undefined && opts && opts.populated && opts.populated.length) { var id = String(doc._id); for (var i = 0; i < opts.populated.length; ++i) { var item = opts.populated[i]; if (item.isVirtual) { this.populated(item.path, utils.getValue(item.path, doc), item); } else { this.populated(item.path, item._docs[id], item); } } } init(this, doc, this._doc); this.$__storeShard(); this.emit('init', this); this.constructor.emit('init', this); if (fn) { fn(null); } return this; }
...
this.$__.activePaths.require(required[i]);
}
this.$__.emitter.setMaxListeners(0);
this._doc = this.$__buildDoc(obj, fields, skipId);
if (!skipInit && obj) {
this.init(obj);
}
this.$__registerHooksFromSchema();
// apply methods
for (var m in schema.methods) {
this[m] = schema.methods[m];
...
inspect = function (options) { var isPOJO = options && utils.getFunctionName(options.constructor) === 'Object'; var opts; if (isPOJO) { opts = options; opts.minimize = false; opts.retainKeyOrder = true; } return this.toObject(opts); }
...
* Helper for console.log
*
* @api public
* @method toString
*/
Document.prototype.toString = function() {
return inspect(this.inspect());
};
/**
* Returns true if the Document stores the same data as doc.
*
* Documents are considered equal when they have matching `_id`s, unless neither
* document has an `_id`, in which case this function falls back to using
...
invalidate = function (path, err, val, kind) { if (!this.$__.validationError) { this.$__.validationError = new ValidationError(this); } if (this.$__.validationError.errors[path]) { return; } if (!err || typeof err === 'string') { err = new ValidatorError({ path: path, message: err, type: kind || 'user defined', value: val }); } if (this.$__.validationError === err) { return this.$__.validationError; } this.$__.validationError.errors[path] = err; return this.$__.validationError; }
...
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
}
} else {
...
isDirectModified = function (path) { return (path in this.$__.activePaths.states.modify); }
...
// Else mongodb throws: "LEFT_SUBFIELD only supports Object"
if (parts.length <= 1) {
pathToMark = path;
} else {
for (i = 0; i < parts.length; ++i) {
subpath = parts.slice(0, i + 1).join('.');
if (this.isDirectModified(subpath) // earlier prefixes that are already
// marked as dirty have precedence
|| this.get(subpath) === null) {
pathToMark = subpath;
break;
}
}
...
function isDirectSelected(path) { if (this.$__.selected) { if (path === '_id') { return this.$__.selected._id !== 0; } var paths = Object.keys(this.$__.selected); var i = paths.length; var inclusive = null; var cur; if (i === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } while (i--) { cur = paths[i]; if (cur === '_id') { continue; } if (this.$__.selected[cur] && this.$__.selected[cur].$meta) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } return !inclusive; } return true; }
...
/**
* Checks if `path` was explicitly selected. If no projection, always returns
* true.
*
* ####Example
*
* Thing.findOne().select('nested.name').exec(function (err, doc) {
* doc.isDirectSelected('nested.name') // true
* doc.isDirectSelected('nested.otherName') // false
* doc.isDirectSelected('nested') // false
* })
*
* @param {String} path
* @return {Boolean}
* @api public
...
isInit = function (path) { return (path in this.$__.activePaths.states.init); }
n/a
isModified = function (paths) { if (paths) { if (!Array.isArray(paths)) { paths = paths.split(' '); } var modified = this.modifiedPaths(); var directModifiedPaths = Object.keys(this.$__.activePaths.states.modify); var isModifiedChild = paths.some(function(path) { return !!~modified.indexOf(path); }); return isModifiedChild || paths.some(function(path) { return directModifiedPaths.some(function(mod) { return mod === path || path.indexOf(mod + '.') === 0; }); }); } return this.$__.activePaths.some('modify'); }
...
}));
}
} else {
doc[i] = obj[i];
}
}
// mark as hydrated
if (!self.isModified(path)) {
self.$__.activePaths.init(path);
}
}
}
}
/**
...
function isSelected(path) { if (this.$__.selected) { if (path === '_id') { return this.$__.selected._id !== 0; } var paths = Object.keys(this.$__.selected); var i = paths.length; var inclusive = null; var cur; if (i === 1 && paths[0] === '_id') { // only _id was selected. return this.$__.selected._id === 0; } while (i--) { cur = paths[i]; if (cur === '_id') { continue; } if (this.$__.selected[cur] && this.$__.selected[cur].$meta) { continue; } inclusive = !!this.$__.selected[cur]; break; } if (inclusive === null) { return true; } if (path in this.$__.selected) { return inclusive; } i = paths.length; var pathDot = path + '.'; while (i--) { cur = paths[i]; if (cur === '_id') { continue; } if (cur.indexOf(pathDot) === 0) { return inclusive || cur !== pathDot; } if (pathDot.indexOf(cur + '.') === 0) { return inclusive; } } return !inclusive; } return true; }
...
i = keys[index];
path = prefix + i;
schema = self.schema.path(path);
// Should still work if not a model-level discriminator, but should not be
// necessary. This is *only* to catch the case where we queried using the
// base model and the discriminated model has a projection
if (self.schema.$isRootDiscriminator && !self.isSelected(path)) {
return;
}
if (!schema && utils.isObject(obj[i]) &&
(!obj[i].constructor || utils.getFunctionName(obj[i].constructor) === 'Object')) {
// assume nested object
if (!doc[i]) {
...
listeners = function () { return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
...
doc[pair[0]].apply(doc, pair[1]);
}
}
}
Document.prototype.$__handleReject = function handleReject(err) {
// emit on the Model if listening
if (this.listeners('error').length) {
this.emit('error', err);
} else if (this.constructor.listeners && this.constructor.listeners('error').length) {
this.constructor.emit('error', err);
} else if (this.listeners && this.listeners('error').length) {
this.emit('error', err);
}
};
...
markModified = function (path) { this.$__.activePaths.modify(path); }
...
if (!merge) {
this.setValue(path, null);
cleanModifiedSubpaths(this, path);
}
if (Object.keys(val).length === 0) {
this.setValue(path, {});
this.markModified(path);
cleanModifiedSubpaths(this, path);
} else {
this.set(val, path, constructing);
}
return this;
}
this.invalidate(path, new MongooseError.CastError('Object', val, path));
...
modifiedPaths = function () { var directModifiedPaths = Object.keys(this.$__.activePaths.states.modify); return directModifiedPaths.reduce(function(list, path) { var parts = path.split('.'); return list.concat(parts.reduce(function(chains, part, i) { return chains.concat(parts.slice(0, i).concat(part).join('.')); }, []).filter(function(chain) { return (list.indexOf(chain) === -1); })); }, []); }
...
*/
Document.prototype.isModified = function(paths) {
if (paths) {
if (!Array.isArray(paths)) {
paths = paths.split(' ');
}
var modified = this.modifiedPaths();
var directModifiedPaths = Object.keys(this.$__.activePaths.states.modify);
var isModifiedChild = paths.some(function(path) {
return !!~modified.indexOf(path);
});
return isModifiedChild || paths.some(function(path) {
return directModifiedPaths.some(function(mod) {
return mod === path || path.indexOf(mod + '.') === 0;
...
on = function () { return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
...
require('http').createServer(function(req, res) {
if (req.url === '/favicon.ico') {
req.destroy();
res.statusCode = 204;
return res.end();
}
req.on('end', function() {
server.serve(req, res, function(err) {
if (err) {
console.error(err, req.url);
res.writeHead(err.status, err.headers);
res.end();
}
});
...
once = function () { return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
...
aggregate(_this._pipeline, options || {});
decorateCursor(cursor);
resolve(cursor);
callback && callback(null, cursor);
});
return;
}
_this._model.collection.emitter.once('queue', function() {
var cursor = _this._model.collection.
aggregate(_this._pipeline, options || {});
decorateCursor(cursor);
resolve(cursor);
callback && callback(null, cursor);
});
});
...
function populate() { if (arguments.length === 0) { return this; } var pop = this.$__.populate || (this.$__.populate = {}); var args = utils.args(arguments); var fn; if (typeof args[args.length - 1] === 'function') { fn = args.pop(); } // allow `doc.populate(callback)` if (args.length) { // use hash to remove duplicate paths var res = utils.populate.apply(null, args); for (var i = 0; i < res.length; ++i) { pop[res[i].path] = res[i]; } } if (fn) { var paths = utils.object.vals(pop); this.$__.populate = undefined; paths.__noPromise = true; this.constructor.populate(this, paths, fn); } return this; }
...
* Populates document references, executing the `callback` when complete.
* If you want to use promises instead, use this function with
* [`execPopulate()`](#document_Document-execPopulate)
*
* ####Example:
*
* doc
* .populate('company')
* .populate({
* path: 'notes',
* match: /airline/,
* select: 'text',
* model: 'modelName'
* options: opts
* }, function (err, user) {
...
populated = function (path, val, options) { // val and options are internal if (val === null || val === void 0) { if (!this.$__.populated) { return undefined; } var v = this.$__.populated[path]; if (v) { return v.value; } return undefined; } // internal if (val === true) { if (!this.$__.populated) { return undefined; } return this.$__.populated[path]; } this.$__.populated || (this.$__.populated = {}); this.$__.populated[path] = {value: val, options: options}; return val; }
...
// If doc._id is not null or undefined
if (doc._id !== null && doc._id !== undefined &&
opts && opts.populated && opts.populated.length) {
var id = String(doc._id);
for (var i = 0; i < opts.populated.length; ++i) {
var item = opts.populated[i];
if (item.isVirtual) {
this.populated(item.path, utils.getValue(item.path, doc), item);
} else {
this.populated(item.path, item._docs[id], item);
}
}
}
init(this, doc, this._doc);
...
removeAllListeners = function () { return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
n/a
removeListener = function () { return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
...
}
if (!(value && value.isMongooseDocumentArray) &&
(!options || !options.skipDocumentArrayCast)) {
value = new MongooseDocumentArray(value, this.path, doc);
if (prev && prev._handlers) {
for (var key in prev._handlers) {
doc.removeListener(key, prev._handlers[key]);
}
}
} else if (value && value.isMongooseDocumentArray) {
// We need to create a new array, otherwise change tracking will
// update the old doc (gh-4449)
value = new MongooseDocumentArray(value, this.path, doc);
}
...
removePost = function (name, fnToRemove) { var proto = this.prototype || this , posts = proto._posts || (proto._posts || {}); if (!posts[name]) return this; if (arguments.length === 1) { // Remove all post callbacks for hook `name` posts[name].length = 0; } else { posts[name] = posts[name].filter( function (currFn) { return currFn !== fnToRemove; }); } return this; }
n/a
removePre = function (name, fnToRemove) { var proto = this.prototype || this , pres = proto._pres || (proto._pres || {}); if (!pres[name]) return this; if (arguments.length === 1) { // Remove all pre callbacks for hook `name` pres[name].length = 0; } else { pres[name] = pres[name].filter( function (currFn) { return currFn !== fnToRemove; }); } return this; }
n/a
set = function (path, val, type, options) { if (type && utils.getFunctionName(type.constructor) === 'Object') { options = type; type = undefined; } var merge = options && options.merge, adhoc = type && type !== true, constructing = type === true, adhocs; var strict = options && 'strict' in options ? options.strict : this.$__.strictMode; if (adhoc) { adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); adhocs[path] = Schema.interpretAsType(path, type, this.schema.options); } if (typeof path !== 'string') { // new Document({ key: val }) if (path === null || path === void 0) { var _ = path; path = val; val = _; } else { var prefix = val ? val + '.' : ''; if (path instanceof Document) { if (path.$__isNested) { path = path.toObject(); } else { path = path._doc; } } var keys = Object.keys(path); var len = keys.length; var i = 0; var pathtype; var key; if (len === 0 && !this.schema.options.minimize) { if (val) { this.set(val, {}); } return this; } if (this.schema.options.retainKeyOrder) { while (i < len) { _handleIndex.call(this, i++); } } else { while (len--) { _handleIndex.call(this, len); } } return this; } } function _handleIndex(i) { key = keys[i]; var pathName = prefix + key; pathtype = this.schema.pathType(pathName); if (path[key] !== null && path[key] !== void 0 // need to know if plain object - no Buffer, ObjectId, ref, etc && utils.isObject(path[key]) && (!path[key].constructor || utils.getFunctionName(path[key].constructor) === 'Object') && pathtype !== 'virtual' && pathtype !== 'real' && !(this.$__path(pathName) instanceof MixedSchema) && !(this.schema.paths[pathName] && this.schema.paths[pathName].options && this.schema.paths[pathName].options.ref)) { this.set(path[key], prefix + key, constructing); } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) if (constructing && path[key] === void 0 && this.get(key) !== void 0) { return; } if (pathtype === 'real' || pathtype === 'virtual') { // Check for setting single embedded schema to document (gh-3535) var p = path[key]; if (this.schema.paths[pathName] && this.schema.paths[pathName].$isSingleNested && path[key] instanceof Document) { p = p.toObject({ virtuals: false, transform: false }); } this.set(prefix + key, p, constructing); } else if (pathtype === 'nested' && path[key] instanceof Document) { this.set(prefix + key, path[key].toObject({transform: false}), constructing); } else if (strict === 'throw') { if (pathtype === 'nested') { throw new ObjectExpectedError(key, path[key]); } else { throw new StrictModeError(key); } } } else if (path[key] !== void 0) { this.set(prefix + key, path[key], constructing); } } var pathType = this.schema.pathType(path); if (pathType === 'nested' && val) { if (utils.isObject(val) && (!val.constructor || utils.getFunctionName(val.constructor) === 'Object')) { if (!merge) { this.setValue(path, null); cleanModifiedSubpaths(this, path); } if (Object.keys(val).length === 0) { this.setValue(path, {}); this.markModified(path); cleanModifiedSubpaths(this, path); } else { this.set(val, path, constructing); } return this; } this.invalidate(path, new MongooseError.CastError('Object', val, path)); return this; } var schema; var parts = path.split('.'); if (pathType === 'adhocOrUndefined' && strict) { // check for roots that are Mixed types ...
...
age: { type: Number, min: 18, index: true },
bio: { type: String, match: /[a-z]/ },
date: { type: Date, default: Date.now },
buff: Buffer
});
// a setter
Comment.path('name').set(function (v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function (next) {
notify(this.get('email'));
next();
...
setMaxListeners = function () { return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); }
...
}
var required = this.schema.requiredPaths();
for (var i = 0; i < required.length; ++i) {
this.$__.activePaths.require(required[i]);
}
this.$__.emitter.setMaxListeners(0);
this._doc = this.$__buildDoc(obj, fields, skipId);
if (!skipInit && obj) {
this.init(obj);
}
this.$__registerHooksFromSchema();
...
setValue = function (path, val) { utils.setValue(path, val, this._doc); return this; }
...
}
var pathType = this.schema.pathType(path);
if (pathType === 'nested' && val) {
if (utils.isObject(val) &&
(!val.constructor || utils.getFunctionName(val.constructor) === 'Object')) {
if (!merge) {
this.setValue(path, null);
cleanModifiedSubpaths(this, path);
}
if (Object.keys(val).length === 0) {
this.setValue(path, {});
this.markModified(path);
cleanModifiedSubpaths(this, path);
...
toBSON = function () { return this.toObject({ transform: false, virtuals: false, _skipDepopulateTopLevel: true, depopulate: true, flattenDecimals: false }); }
n/a
toJSON = function (options) { return this.$toObject(options, true); }
...
if (Array.isArray(obj)) {
return cloneArray(obj, options);
}
if (isMongooseObject(obj)) {
if (options && options.json && typeof obj.toJSON === 'function') {
return obj.toJSON(options);
}
return obj.toObject(options);
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
...
toObject = function (options) { return this.$toObject(options); }
...
if (value.$maxDistance != null) {
value.$maxDistance = numbertype.castForQuery(value.$maxDistance);
}
if (value.$minDistance != null) {
value.$minDistance = numbertype.castForQuery(value.$minDistance);
}
if (utils.isMongooseObject(value.$geometry)) {
value.$geometry = value.$geometry.toObject({
transform: false,
virtuals: false
});
}
value = value.$geometry.coordinates;
} else if (geo === '$geoWithin') {
if (value.$geometry) {
...
toString = function () { return inspect(this.inspect()); }
...
```
Moreover, you can mutate the incoming `method` arguments so that subsequent middleware see different values for those arguments.
To do so, just pass the new values to `next`:
```js
.pre(method, function firstPre (next, methodArg1, methodArg2) {
// Mutate methodArg1
next("altered-" + methodArg1.toString(), methodArg2);
});
// pre declaration is chainable
.pre(method, function secondPre (next, methodArg1, methodArg2) {
console.log(methodArg1);
// => 'altered-originalValOfMethodArg1'
...
unmarkModified = function (path) { this.$__.activePaths.init(path); }
...
/**
* Clears the modified state on the specified path.
*
* ####Example:
*
* doc.foo = 'bar';
* doc.unmarkModified('foo');
* doc.save() // changes to foo will not be persisted
*
* @param {String} path the path to unmark modified
* @api public
*/
Document.prototype.unmarkModified = function(path) {
...
function update() { var args = utils.args(arguments); args.unshift({_id: this._id}); return this.constructor.update.apply(this.constructor, args); }
...
}
/**
* Sends an update command with this document `_id` as the query selector.
*
* ####Example:
*
* weirdCar.update({$inc: {wheels:1}}, { w: 1 }, callback);
*
* ####Valid options:
*
* - same as in [Model.update](#model_Model.update)
*
* @see Model.update #model_Model.update
* @param {Object} doc
...
validate = function (options, callback) { if (typeof options === 'function') { callback = options; options = null; } this.$__validate(callback || function() {}); }
...
*
* ####Note:
*
* This method is called `pre` save and if a validation rule is violated, [save](#model_Model-save) is aborted and the error is returned
to your `callback`.
*
* ####Example:
*
* doc.validate(function (err) {
* if (err) handleError(err);
* else // validation passed
* });
*
* @param {Object} optional options internal options
* @param {Function} callback optional callback called after validation completes, passing an error if one occurred
* @return {Promise} Promise
...
validateSync = function (pathsToValidate) { var _this = this; if (typeof pathsToValidate === 'string') { pathsToValidate = pathsToValidate.split(' '); } // only validate required fields when necessary var paths = _getPathsToValidate(this); if (pathsToValidate && pathsToValidate.length) { var tmp = []; for (var i = 0; i < paths.length; ++i) { if (pathsToValidate.indexOf(paths[i]) !== -1) { tmp.push(paths[i]); } } paths = tmp; } var validating = {}; paths.forEach(function(path) { if (validating[path]) { return; } validating[path] = true; var p = _this.schema.path(path); if (!p) { return; } if (!_this.$isValid(path)) { return; } var val = _this.getValue(path); var err = p.doValidateSync(val, _this); if (err) { _this.invalidate(path, err, undefined, true); } }); var err = _this.$__.validationError; _this.$__.validationError = undefined; _this.emit('validate', _this); _this.constructor.emit('validate', _this); if (err) { for (var key in err.errors) { // Make sure cast errors persist if (err.errors[key] instanceof MongooseError.CastError) { _this.invalidate(key, err.errors[key]); } } } return err; }
...
*
* ####Note:
*
* This method is useful if you need synchronous validation.
*
* ####Example:
*
* var err = doc.validateSync();
* if ( err ){
* handleError( err );
* } else {
* // validation passed
* }
*
* @param {Array|string} pathsToValidate only validate the given paths
...
function DocumentArray(key, schema, options) { var EmbeddedDocument = _createConstructor(schema, options); ArrayType.call(this, key, EmbeddedDocument, options); this.schema = schema; this.$isMongooseDocumentArray = true; var fn = this.defaultValue; if (!('defaultValue' in this) || fn !== void 0) { this.default(function() { var arr = fn.call(this); if (!Array.isArray(arr)) { arr = [arr]; } // Leave it up to `cast()` to convert this to a documentarray return arr; }); } }
n/a
cast = function (value, doc, init, prev, options) { var selected, subdoc, i; if (!Array.isArray(value)) { // gh-2442 mark whole array as modified if we're initializing a doc from // the db and the path isn't an array in the document if (!!doc && init) { doc.markModified(this.path); } return this.cast([value], doc, init, prev); } if (!(value && value.isMongooseDocumentArray) && (!options || !options.skipDocumentArrayCast)) { value = new MongooseDocumentArray(value, this.path, doc); if (prev && prev._handlers) { for (var key in prev._handlers) { doc.removeListener(key, prev._handlers[key]); } } } else if (value && value.isMongooseDocumentArray) { // We need to create a new array, otherwise change tracking will // update the old doc (gh-4449) value = new MongooseDocumentArray(value, this.path, doc); } i = value.length; while (i--) { if (!value[i]) { continue; } var Constructor = this.casterConstructor; if (Constructor.discriminators && typeof value[i][Constructor.schema.options.discriminatorKey] === 'string' && Constructor.discriminators[value[i][Constructor.schema.options.discriminatorKey]]) { Constructor = Constructor.discriminators[value[i][Constructor.schema.options.discriminatorKey]]; } // Check if the document has a different schema (re gh-3701) if ((value[i] instanceof Subdocument) && value[i].schema !== Constructor.schema) { value[i] = value[i].toObject({ transform: false, virtuals: false }); } if (!(value[i] instanceof Subdocument) && value[i]) { if (init) { if (doc) { selected || (selected = scopePaths(this, doc.$__.selected, init)); } else { selected = true; } subdoc = new Constructor(null, value, true, selected, i); value[i] = subdoc.init(value[i]); } else { if (prev && (subdoc = prev.id(value[i]._id))) { subdoc = prev.id(value[i]._id); } if (prev && subdoc) { // handle resetting doc with existing id but differing data // doc.array = [{ doc: 'val' }] subdoc.set(value[i]); // if set() is hooked it will have no return value // see gh-746 value[i] = subdoc; } else { try { subdoc = new Constructor(value[i], value, undefined, undefined, i); // if set() is hooked it will have no return value // see gh-746 value[i] = subdoc; } catch (error) { var valueInErrorMessage = util.inspect(value[i]); throw new CastError('embedded', valueInErrorMessage, value._path, error); } } } } } return value; }
...
doc[i] = obj[i];
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
...
function DocumentArray(key, schema, options) { var EmbeddedDocument = _createConstructor(schema, options); ArrayType.call(this, key, EmbeddedDocument, options); this.schema = schema; this.$isMongooseDocumentArray = true; var fn = this.defaultValue; if (!('defaultValue' in this) || fn !== void 0) { this.default(function() { var arr = fn.call(this); if (!Array.isArray(arr)) { arr = [arr]; } // Leave it up to `cast()` to convert this to a documentarray return arr; }); } }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
discriminator = function (name, schema) { if (typeof name === 'function') { name = utils.getFunctionName(name); } schema = discriminator(this.casterConstructor, name, schema); var EmbeddedDocument = _createConstructor(schema); EmbeddedDocument.baseCasterConstructor = this.casterConstructor; try { Object.defineProperty(EmbeddedDocument, 'name', { value: name }); } catch (error) { // Ignore error, only happens on old versions of node } this.casterConstructor.discriminators[name] = EmbeddedDocument; return this.casterConstructor.discriminators[name]; }
...
* }
* util.inherits(BaseSchema, Schema);
*
* var PersonSchema = new BaseSchema();
* var BossSchema = new BaseSchema({ department: String });
*
* var Person = mongoose.model('Person', PersonSchema);
* var Boss = Person.discriminator('Boss', BossSchema);
*
* @param {String} name discriminator model name
* @param {Schema} schema discriminator model schema
* @api public
*/
Model.discriminator = function(name, schema) {
...
doValidate = function (array, fn, scope, options) { var _this = this; SchemaType.prototype.doValidate.call(this, array, function(err) { if (err) { return fn(err); } var count = array && array.length; var error; if (!count) { return fn(); } if (options && options.updateValidator) { return fn(); } // handle sparse arrays, do not use array.forEach which does not // iterate over sparse elements yet reports array.length including // them :( function callback(err) { if (err) { error = err; } --count || fn(error); } for (var i = 0, len = count; i < len; ++i) { // sidestep sparse entries var doc = array[i]; if (!doc) { --count || fn(error); continue; } // If you set the array index directly, the doc might not yet be // a full fledged mongoose subdoc, so make it into one. if (!(doc instanceof Subdocument)) { doc = array[i] = new _this.casterConstructor(doc, array, undefined, undefined, i); } // HACK: use $__original_validate to avoid promises so bluebird doesn't // complain if (doc.$__original_validate) { doc.$__original_validate({__noPromise: true}, callback); } else { doc.validate({__noPromise: true}, callback); } } }, scope); }
...
// If user marked as invalid or there was a cast error, don't validate
if (!_this.$isValid(path)) {
--total || complete();
return;
}
var val = _this.getValue(path);
p.doValidate(val, function(err) {
if (err) {
_this.invalidate(path, err, undefined, true);
}
--total || complete();
}, _this);
});
};
...
doValidateSync = function (array, scope) { var schemaTypeError = SchemaType.prototype.doValidateSync.call(this, array, scope); if (schemaTypeError) { return schemaTypeError; } var count = array && array.length, resultError = null; if (!count) { return; } // handle sparse arrays, do not use array.forEach which does not // iterate over sparse elements yet reports array.length including // them :( for (var i = 0, len = count; i < len; ++i) { // only first error if (resultError) { break; } // sidestep sparse entries var doc = array[i]; if (!doc) { continue; } var subdocValidateError = doc.validateSync(); if (subdocValidateError) { resultError = subdocValidateError; } } return resultError; }
...
return;
}
if (!_this.$isValid(path)) {
return;
}
var val = _this.getValue(path);
var err = p.doValidateSync(val, _this);
if (err) {
_this.invalidate(path, err, undefined, true);
}
});
var err = _this.$__.validationError;
_this.$__.validationError = undefined;
...
function Embedded(schema, path, options) { var _embedded = function SingleNested(value, path, parent) { var _this = this; Subdocument.apply(this, arguments); this.$parent = parent; if (parent) { parent.on('save', function() { _this.emit('save', _this); _this.constructor.emit('save', _this); }); parent.on('isNew', function(val) { _this.isNew = val; _this.emit('isNew', val); _this.constructor.emit('isNew', val); }); } }; _embedded.prototype = Object.create(Subdocument.prototype); _embedded.prototype.$__setSchema(schema); _embedded.prototype.constructor = _embedded; _embedded.schema = schema; _embedded.$isSingleNested = true; _embedded.prototype.$basePath = path; _embedded.prototype.toBSON = function() { return this.toObject({ transform: false, retainKeyOrder: schema.options.retainKeyOrder, virtuals: false, _skipDepopulateTopLevel: true, depopulate: true, flattenDecimals: false }); }; // apply methods for (var i in schema.methods) { _embedded.prototype[i] = schema.methods[i]; } // apply statics for (i in schema.statics) { _embedded[i] = schema.statics[i]; } for (i in EventEmitter.prototype) { _embedded[i] = EventEmitter.prototype[i]; } applyHooks(_embedded, schema); this.caster = _embedded; this.schema = schema; this.$isSingleNested = true; SchemaType.call(this, path, options, 'Embedded'); }
n/a
cast = function (val, doc, init) { if (val && val.$isSingleNested) { return val; } var subdoc = new this.caster(void 0, doc ? doc.$__.selected : void 0, doc); if (init) { subdoc.init(val); } else { subdoc.set(val, undefined, true); } return subdoc; }
...
doc[i] = obj[i];
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
...
castForQuery = function ($conditional, val) { var handler; if (arguments.length === 2) { handler = this.$conditionalHandlers[$conditional]; if (!handler) { throw new Error('Can\'t use ' + $conditional); } return handler.call(this, val); } val = $conditional; if (val == null) { return val; } return new this.caster(val); }
...
}
if (geo) {
var numbertype = new Types.Number('__QueryCasting__');
var value = val[geo];
if (val.$maxDistance != null) {
val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
}
if (val.$minDistance != null) {
val.$minDistance = numbertype.castForQuery(val.$minDistance);
}
if (geo === '$within') {
var withinType = value.$center
...
doValidate = function (value, fn, scope) { var Constructor = this.caster; SchemaType.prototype.doValidate.call(this, value, function(error) { if (error) { return fn(error); } if (!value) { return fn(null); } if (!(value instanceof Constructor)) { value = new Constructor(value); } value.validate(fn, {__noPromise: true}); }, scope); }
...
// If user marked as invalid or there was a cast error, don't validate
if (!_this.$isValid(path)) {
--total || complete();
return;
}
var val = _this.getValue(path);
p.doValidate(val, function(err) {
if (err) {
_this.invalidate(path, err, undefined, true);
}
--total || complete();
}, _this);
});
};
...
doValidateSync = function (value, scope) { var schemaTypeError = SchemaType.prototype.doValidateSync.call(this, value, scope); if (schemaTypeError) { return schemaTypeError; } if (!value) { return; } return value.validateSync(); }
...
return;
}
if (!_this.$isValid(path)) {
return;
}
var val = _this.getValue(path);
var err = p.doValidateSync(val, _this);
if (err) {
_this.invalidate(path, err, undefined, true);
}
});
var err = _this.$__.validationError;
_this.$__.validationError = undefined;
...
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
res.statusCode = 204;
return res.end();
}
req.on('end', function() {
server.serve(req, res, function(err) {
if (err) {
console.error(err, req.url);
res.writeHead(err.status, err.headers);
res.end();
}
});
});
req.resume();
}).listen(8088);
...
function CastError(type, value, path, reason) { var stringValue = util.inspect(value); stringValue = stringValue.replace(/^'/, '"').replace(/'$/, '"'); if (stringValue.charAt(0) !== '"') { stringValue = '"' + stringValue + '"'; } MongooseError.call(this, 'Cast to ' + type + ' failed for value ' + stringValue + ' at path "' + path + '"'); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.stringValue = stringValue; this.name = 'CastError'; this.kind = type; this.value = value; this.path = path; this.reason = reason; }
...
this.markModified(path);
cleanModifiedSubpaths(this, path);
} else {
this.set(val, path, constructing);
}
return this;
}
this.invalidate(path, new MongooseError.CastError('Object', val, path));
return this;
}
var schema;
var parts = path.split('.');
if (pathType === 'adhocOrUndefined' && strict) {
...
function DivergentArrayError(paths) { var msg = 'For your own good, using `document.save()` to update an array ' + 'which was selected using an $elemMatch projection OR ' + 'populated using skip, limit, query conditions, or exclusion of ' + 'the _id field when the operation results in a $pop or $set of ' + 'the entire array is not supported. The following ' + 'path(s) would have been modified unsafely:\n' + ' ' + paths.join('\n ') + '\n' + 'Use Model.update() to update these arrays instead.'; // TODO write up a docs page (FAQ) and link to it MongooseError.call(this, msg); Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee); this.name = 'DivergentArrayError'; }
n/a
function DocumentNotFoundError(query) { MongooseError.call(this, 'No document found for query "' + util.inspect(query) + '"'); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'DocumentNotFoundError'; this.query = query; }
n/a
function MissingSchemaError(name) { var msg = 'Schema hasn\'t been registered for model "' + name + '".\n' + 'Use mongoose.model(name, schema)'; MongooseError.call(this, msg); Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee); this.name = 'MissingSchemaError'; }
...
if (obj._id === undefined) {
obj._id = new ObjectId();
}
}
if (!schema) {
throw new MongooseError.MissingSchemaError();
}
this.$__setSchema(schema);
this.$__ = new InternalCache;
this.$__.emitter = new EventEmitter();
this.isNew = true;
...
function OverwriteModelError(name) { MongooseError.call(this, 'Cannot overwrite `' + name + '` model once compiled.'); Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee); this.name = 'OverwriteModelError'; }
...
throw new Error('The 2nd parameter to `mongoose.model()` should be a ' +
'schema or a POJO');
}
if (this.models[name] && !collection) {
// model exists but we are not subclassing with custom collection
if (schema && schema.instanceOfSchema && schema !== this.models[name].schema) {
throw new MongooseError.OverwriteModelError(name);
}
return this.models[name];
}
var opts = {cache: false, connection: this};
var model;
...
function ValidationError(instance) { this.errors = {}; if (instance && instance.constructor.name === 'model') { MongooseError.call(this, instance.constructor.modelName + ' validation failed'); } else { MongooseError.call(this, 'Validation failed'); } if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'ValidationError'; if (instance) { instance.errors = this.errors; } }
n/a
function ValidatorError(properties) { var msg = properties.message; if (!msg) { msg = MongooseError.messages.general.default; } var message = this.formatMessage(msg, properties); MongooseError.call(this, message); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.properties = properties; this.name = 'ValidatorError'; this.kind = properties.type; this.path = properties.path; this.value = properties.value; this.reason = properties.reason; }
n/a
function VersionError(doc) { MongooseError.call(this, 'No matching document found for id "' + doc._id + '"'); this.name = 'VersionError'; }
n/a
function Error() { [native code] }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function MissingSchemaError(name) { var msg = 'Schema hasn\'t been registered for model "' + name + '".\n' + 'Use mongoose.model(name, schema)'; MongooseError.call(this, msg); Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee); this.name = 'MissingSchemaError'; }
n/a
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function Mixed(path, options) { if (options && options.default) { var def = options.default; if (Array.isArray(def) && def.length === 0) { // make sure empty array defaults are handled options.default = Array; } else if (!options.shared && utils.isObject(def) && Object.keys(def).length === 0) { // prevent odd "shared" objects between documents options.default = function() { return {}; }; } } SchemaType.call(this, path, options, 'Mixed'); }
n/a
cast = function (val) { return val; }
...
doc[i] = obj[i];
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
...
castForQuery = function ($cond, val) { if (arguments.length === 2) { return val; } return $cond; }
...
}
if (geo) {
var numbertype = new Types.Number('__QueryCasting__');
var value = val[geo];
if (val.$maxDistance != null) {
val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
}
if (val.$minDistance != null) {
val.$minDistance = numbertype.castForQuery(val.$minDistance);
}
if (geo === '$within') {
var withinType = value.$center
...
function Mixed(path, options) { if (options && options.default) { var def = options.default; if (Array.isArray(def) && def.length === 0) { // make sure empty array defaults are handled options.default = Array; } else if (!options.shared && utils.isObject(def) && Object.keys(def).length === 0) { // prevent odd "shared" objects between documents options.default = function() { return {}; }; } } SchemaType.call(this, path, options, 'Mixed'); }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function Model(doc, fields, skipId) { Document.call(this, doc, fields, skipId, true); }
...
});
```
Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
### Accessing a Model
Once we define a model through `mongoose.model('ModelName', mySchema)`, we
can access it through the same function
```js
var myModel = mongoose.model('ModelName');
```
Or just do it all at once
...
function subclass(conn, schema, collection) { // subclass model using this connection and collection name var _this = this; var Model = function Model(doc, fields, skipId) { if (!(this instanceof Model)) { return new Model(doc, fields, skipId); } _this.call(this, doc, fields, skipId); }; Model.__proto__ = _this; Model.prototype.__proto__ = _this.prototype; Model.db = Model.prototype.db = conn; var s = schema && typeof schema !== 'string' ? schema : _this.prototype.schema; var options = s.options || {}; if (!collection) { collection = _this.prototype.schema.get('collection') || utils.toCollectionName(_this.modelName, options); } var collectionOptions = { bufferCommands: s ? options.bufferCommands : true, capped: s && options.capped }; Model.prototype.collection = conn.collection(collection, collectionOptions); Model.collection = Model.prototype.collection; Model.init(); return Model; }
...
return model;
}
if (this.models[name] && collection) {
// subclassing current model with alternate collection
model = this.models[name];
schema = model.prototype.schema;
var sub = model.__subclass(this, schema, collection);
// do not cache the sub model
return sub;
}
// lookup model in mongoose module
model = this.base.models[name];
...
function _getSchema(path) { return this.schema._getSchema(path); }
...
modelNameFromQuery = options.model && options.model.modelName || options.model,
schema, refPath, Model, currentOptions, modelNames, modelName, discriminatorKey, modelForFindSchema;
var originalModel = options.model;
var isVirtual = false;
var isRefPathArray = false;
schema = model._getSchema(options.path);
var isUnderneathDocArray = schema && schema.$isUnderneathDocArray;
if (isUnderneathDocArray &&
options &&
options.options &&
options.options.sort) {
return new Error('Cannot populate with `sort` on path ' + options.path +
' because it is a subproperty of a document array');
...
function addListener(type, listener) { return _addListener(this, type, listener, false); }
n/a
function aggregate() { var args = [].slice.call(arguments), aggregate, callback; if (typeof args[args.length - 1] === 'function') { callback = args.pop(); } if (args.length === 1 && util.isArray(args[0])) { aggregate = new Aggregate(args[0]); } else { aggregate = new Aggregate(args); } aggregate.model(this); if (typeof callback === 'undefined') { return aggregate; } if (callback) { callback = this.$wrapCallback(callback); } aggregate.exec(callback); }
...
* ####Example:
*
* new Aggregate();
* new Aggregate({ $project: { a: 1, b: 1 } });
* new Aggregate({ $project: { a: 1, b: 1 } }, { $skip: 5 });
* new Aggregate([{ $project: { a: 1, b: 1 } }, { $skip: 5 }]);
*
* Returned when calling Model.aggregate().
*
* ####Example:
*
* Model
* .aggregate({ $match: { age: { $gte: 21 }}})
* .unwind('tags')
* .exec(callback)
...
bulkWrite = function (ops, options, callback) { var Promise = PromiseProvider.get(); var _this = this; if (typeof options === 'function') { callback = options; options = null; } if (callback) { callback = this.$wrapCallback(callback); } options = options || {}; var validations = ops.map(function(op) { if (op['insertOne']) { return function(callback) { op['insertOne']['document'] = new _this(op['insertOne']['document']); op['insertOne']['document'].validate({ __noPromise: true }, function(error) { if (error) { return callback(error); } callback(null); }); }; } else if (op['updateOne']) { return function(callback) { try { op['updateOne']['filter'] = cast(_this.schema, op['updateOne']['filter']); op['updateOne']['update'] = castUpdate(_this.schema, op['updateOne']['update'], _this.schema.options.strict); } catch (error) { return callback(error); } callback(null); }; } else if (op['updateMany']) { return function(callback) { try { op['updateMany']['filter'] = cast(_this.schema, op['updateMany']['filter']); op['updateMany']['update'] = castUpdate(_this.schema, op['updateMany']['filter'], { strict: _this.schema.options.strict, overwrite: false }); } catch (error) { return callback(error); } callback(null); }; } else if (op['replaceOne']) { return function(callback) { try { op['replaceOne']['filter'] = cast(_this.schema, op['replaceOne']['filter']); } catch (error) { return callback(error); } // set `skipId`, otherwise we get "_id field cannot be changed" op['replaceOne']['replacement'] = new _this(op['replaceOne']['replacement'], null, true); op['replaceOne']['replacement'].validate({ __noPromise: true }, function(error) { if (error) { return callback(error); } callback(null); }); }; } else if (op['deleteOne']) { return function(callback) { try { op['deleteOne']['filter'] = cast(_this.schema, op['deleteOne']['filter']); } catch (error) { return callback(error); } callback(null); }; } else if (op['deleteMany']) { return function(callback) { try { op['deleteMany']['filter'] = cast(_this.schema, op['deleteMany']['filter']); } catch (error) { return callback(error); } callback(null); }; } else { return function(callback) { callback(new Error('Invalid op passed to `bulkWrite()`')); }; } }); var promise = new Promise.ES6(function(resolve, reject) { parallel(validations, function(error) { if (error) { callback && callback(error); return reject(error); } _this.collection.bulkWrite(ops, options, function(error, res) { if (error) { callback && callback(error); return reject(error); } callback && callback(null, res); resolve(res); }); }); }); return promise; }
...
*
* Mongoose will perform casting on all operations you provide.
*
* This function does **not** trigger any middleware.
*
* ####Example:
*
* Character.bulkWrite([
* {
* insertOne: {
* document: {
* name: 'Eddard Stark',
* title: 'Warden of the North'
* }
* }
...
function compile(name, schema, collectionName, connection, base) { var versioningEnabled = schema.options.versionKey !== false; if (versioningEnabled && !schema.paths[schema.options.versionKey]) { // add versioning to top level documents only var o = {}; o[schema.options.versionKey] = Number; schema.add(o); } var model; if (typeof name === 'function' && name.prototype instanceof Model) { model = name; name = model.name; schema.loadClass(model, true); model.prototype.$isMongooseModelPrototype = true; } else { // generate new class model = function model(doc, fields, skipId) { if (!(this instanceof model)) { return new model(doc, fields, skipId); } Model.call(this, doc, fields, skipId); }; } model.hooks = schema.s.hooks.clone(); model.base = base; model.modelName = name; if (!(model.prototype instanceof Model)) { model.__proto__ = Model; model.prototype.__proto__ = Model.prototype; } model.model = Model.prototype.model; model.db = model.prototype.db = connection; model.discriminators = model.prototype.discriminators = undefined; model.prototype.$__setSchema(schema); var collectionOptions = { bufferCommands: schema.options.bufferCommands, capped: schema.options.capped }; model.prototype.collection = connection.collection( collectionName , collectionOptions ); // apply methods and statics applyMethods(model, schema); applyStatics(model, schema); applyHooks(model, schema); model.schema = model.prototype.schema; model.collection = model.prototype.collection; // Create custom query constructor model.Query = function() { Query.apply(this, arguments); this.options.retainKeyOrder = model.schema.options.retainKeyOrder; }; model.Query.prototype = Object.create(Query.prototype); model.Query.base = Query.base; applyQueryMethods(model, schema.query); var kareemOptions = { useErrorHandlers: true }; model.$__insertMany = model.hooks.createWrapper('insertMany', model.insertMany, model, kareemOptions); model.insertMany = function(arr, options, callback) { var Promise = PromiseProvider.get(); if (typeof options === 'function') { callback = options; options = null; } return new Promise.ES6(function(resolve, reject) { model.$__insertMany(arr, options, function(error, result) { if (error) { callback && callback(error); return reject(error); } callback && callback(null, result); resolve(result); }); }); }; return model; }
...
callback && callback(err);
reject(err);
return;
}
if (ret.findOne && ret.mapReduce) {
// returned a collection, convert to Model
var model = Model.compile(
'_mapreduce_' + ret.collectionName
, Model.mapReduce.schema
, ret.collectionName
, _this.db
, _this.base);
model._mapreduce = true;
...
function count(conditions, callback) { if (typeof conditions === 'function') { callback = conditions; conditions = {}; } // get the mongodb collection object var mq = new this.Query({}, {}, this, this.collection); if (callback) { callback = this.$wrapCallback(callback); } return mq.count(conditions, callback); }
...
};
/**
* Counts number of matching documents in a database collection.
*
* ####Example:
*
* Adventure.count({ type: 'jungle' }, function (err, count) {
* if (err) ..
* console.log('there are %d jungle adventures', count);
* });
*
* @param {Object} conditions
* @param {Function} [callback]
* @return {Query}
...
function create(doc, callback) { var args; var cb; var discriminatorKey = this.schema.options.discriminatorKey; if (Array.isArray(doc)) { args = doc; cb = callback; } else { var last = arguments[arguments.length - 1]; // Handle falsy callbacks re: #5061 if (typeof last === 'function' || !last) { cb = last; args = utils.args(arguments, 0, arguments.length - 1); } else { args = utils.args(arguments); } } var Promise = PromiseProvider.get(); var _this = this; if (cb) { cb = this.$wrapCallback(cb); } var promise = new Promise.ES6(function(resolve, reject) { if (args.length === 0) { setImmediate(function() { cb && cb(null); resolve(null); }); return; } var toExecute = []; args.forEach(function(doc) { toExecute.push(function(callback) { var Model = _this.discriminators && doc[discriminatorKey] ? _this.discriminators[doc[discriminatorKey]] : _this; var toSave = doc instanceof Model ? doc : new Model(doc); var callbackWrapper = function(error, doc) { if (error) { return callback(error); } callback(null, doc); }; // Hack to avoid getting a promise because of // $__registerHooksFromSchema if (toSave.$__original_save) { toSave.$__original_save({ __noPromise: true }, callbackWrapper); } else { toSave.save({ __noPromise: true }, callbackWrapper); } }); }); parallel(toExecute, function(error, savedDocs) { if (error) { if (cb) { cb(error); } else { reject(error); } return; } if (doc instanceof Array) { resolve(savedDocs); cb && cb.call(_this, null, savedDocs); } else { resolve.apply(promise, savedDocs); if (cb) { savedDocs.unshift(null); cb.apply(_this, savedDocs); } } }); }); return promise; }
...
}
}
/*!
* Inherit from the NodeJS document
*/
Document.prototype = Object.create(NodeJSDocument.prototype);
Document.prototype.constructor = Document;
/*!
* Browser doc exposes the event emitter API
*/
Document.$emitter = new EventEmitter();
...
function deleteMany(conditions, callback) { if (typeof conditions === 'function') { callback = conditions; conditions = {}; } // get the mongodb collection object var mq = new this.Query(conditions, {}, this, this.collection); if (callback) { callback = this.$wrapCallback(callback); } return mq.deleteMany(callback); }
...
/**
* Deletes the first document that matches `conditions` from the collection.
* Behaves like `remove()`, but deletes all documents that match `conditions`
* regardless of the `justOne` option.
*
* ####Example:
*
* Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }, function (err) {});
*
* ####Note:
*
* Like `Model.remove()`, this function does **not** trigger `pre('remove')` or `post('remove')` hooks.
*
* @param {Object} conditions
* @param {Function} [callback]
...
function deleteOne(conditions, callback) { if (typeof conditions === 'function') { callback = conditions; conditions = {}; } // get the mongodb collection object var mq = new this.Query(conditions, {}, this, this.collection); if (callback) { callback = this.$wrapCallback(callback); } return mq.deleteOne(callback); }
...
/**
* Deletes the first document that matches `conditions` from the collection.
* Behaves like `remove()`, but deletes at most one document regardless of the
* `justOne` option.
*
* ####Example:
*
* Character.deleteOne({ name: 'Eddard Stark' }, function (err) {});
*
* ####Note:
*
* Like `Model.remove()`, this function does **not** trigger `pre('remove')` or `post('remove')` hooks.
*
* @param {Object} conditions
* @param {Function} [callback]
...
discriminator = function (name, schema) { if (typeof name === 'function') { name = utils.getFunctionName(name); } schema = discriminator(this, name, schema); if (this.db.models[name]) { throw new OverwriteModelError(name); } schema.$isRootDiscriminator = true; this.discriminators[name] = this.db.model(name, schema, this.collection.name); var d = this.discriminators[name]; d.prototype.__proto__ = this.prototype; Object.defineProperty(d, 'baseModelName', { value: this.modelName, configurable: true, writable: false }); // apply methods and statics applyMethods(d, schema); applyStatics(d, schema); return d; }
...
* }
* util.inherits(BaseSchema, Schema);
*
* var PersonSchema = new BaseSchema();
* var BossSchema = new BaseSchema({ department: String });
*
* var Person = mongoose.model('Person', PersonSchema);
* var Boss = Person.discriminator('Boss', BossSchema);
*
* @param {String} name discriminator model name
* @param {Schema} schema discriminator model schema
* @api public
*/
Model.discriminator = function(name, schema) {
...
function distinct(field, conditions, callback) { // get the mongodb collection object var mq = new this.Query({}, {}, this, this.collection); if (typeof conditions === 'function') { callback = conditions; conditions = {}; } if (callback) { callback = this.$wrapCallback(callback); } return mq.distinct(field, conditions, callback); }
...
/**
* Creates a Query for a `distinct` operation.
*
* Passing a `callback` immediately executes the query.
*
* ####Example
*
* Link.distinct('url', { clicks: {$gt: 100}}, function (err, result) {
* if (err) return handleError(err);
*
* assert(Array.isArray(result));
* console.log('unique urls with more than 100 clicks', result);
* })
*
* var query = Link.distinct('url');
...
function emit(type) { var er, handler, len, args, i, events, domain; var needDomainExit = false; var doError = (type === 'error'); events = this._events; if (events) doError = (doError && events.error == null); else if (!doError) return false; domain = this.domain; // If there is no 'error' event listener then throw. if (doError) { er = arguments[1]; if (domain) { if (!er) er = new Error('Uncaught, unspecified "error" event'); er.domainEmitter = this; er.domain = domain; er.domainThrown = false; domain.emit('error', er); } else if (er instanceof Error) { throw er; // Unhandled 'error' event } else { // At least give some kind of context to the user var err = new Error('Uncaught, unspecified "error" event. (' + er + ')'); err.context = er; throw err; } return false; } handler = events[type]; if (!handler) return false; if (domain && this !== process) { domain.enter(); needDomainExit = true; } var isFn = typeof handler === 'function'; len = arguments.length; switch (len) { // fast cases case 1: emitNone(handler, isFn, this); break; case 2: emitOne(handler, isFn, this, arguments[1]); break; case 3: emitTwo(handler, isFn, this, arguments[1], arguments[2]); break; case 4: emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]); break; // slower default: args = new Array(len - 1); for (i = 1; i < len; i++) args[i - 1] = arguments[i]; emitMany(handler, isFn, this, args); } if (needDomainExit) domain.exit(); return true; }
...
You can intercept method arguments via middleware.
For example, this would allow you to broadcast changes about your Documents every time someone `set`s a path in your Document to
a new value:
```js
schema.pre('set', function (next, path, val, typel) {
// `this` is the current Document
this.emit('set', path, val);
// Pass control to the next pre
next();
});
```
Moreover, you can mutate the incoming `method` arguments so that subsequent middleware see different values for those arguments.
To do so, just pass the new values to `next`:
...
function ensureIndexes(options, callback) { if (typeof options === 'function') { callback = options; options = null; } if (options && options.__noPromise) { _ensureIndexes(this, options, callback); return; } if (callback) { callback = this.$wrapCallback(callback); } var _this = this; var Promise = PromiseProvider.get(); return new Promise.ES6(function(resolve, reject) { _ensureIndexes(_this, options || {}, function(error) { if (error) { callback && callback(error); reject(error); } callback && callback(); resolve(); }); }); }
...
*
* @api private
*/
Model.init = function init() {
if ((this.schema.options.autoIndex) ||
(this.schema.options.autoIndex === null && this.db.config.autoIndex)) {
this.ensureIndexes({ __noPromise: true, _automatic: true });
}
this.schema.emit('init', this);
};
/**
* Sends `ensureIndex` commands to mongo for each index declared in the schema.
...
function eventNames() { return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; }
n/a
function find(conditions, projection, options, callback) { if (typeof conditions === 'function') { callback = conditions; conditions = {}; projection = null; options = null; } else if (typeof projection === 'function') { callback = projection; projection = null; options = null; } else if (typeof options === 'function') { callback = options; options = null; } var mq = new this.Query({}, {}, this, this.collection); mq.select(projection); mq.setOptions(options); if (this.schema.discriminatorMapping && mq.selectedInclusively()) { mq.select(this.schema.options.discriminatorKey); } if (callback) { callback = this.$wrapCallback(callback); } return mq.find(conditions, callback); }
...
//
});
```
Or we can find documents from the same collection
```js
MyModel.find({}, function (err, docs) {
// docs.forEach
});
```
You can also `findOne`, `findById`, `update`, etc. For more details check out [the docs](http://mongoosejs.com/docs/queries.html
).
**Important!** If you opened a separate connection using `mongoose.createConnection()` but attempt to access the model through `
mongoose.model('ModelName')` it will not work as expected since it is not hooked up to an active db connection. In this
case access your model through the connection you created:
...
function findById(id, projection, options, callback) { if (typeof id === 'undefined') { id = null; } if (callback) { callback = this.$wrapCallback(callback); } return this.findOne({_id: id}, projection, options, callback); }
...
if (!err) console.log('Success!');
});
```
The same goes for removing them:
```js
BlogPost.findById(myId, function (err, post) {
if (!err) {
post.comments[0].remove();
post.save(function (err) {
// do something
});
}
});
...
findByIdAndRemove = function (id, options, callback) { if (arguments.length === 1 && typeof id === 'function') { var msg = 'Model.findByIdAndRemove(): First argument must not be a function.\n\n' + ' ' + this.modelName + '.findByIdAndRemove(id, callback)\n' + ' ' + this.modelName + '.findByIdAndRemove(id)\n' + ' ' + this.modelName + '.findByIdAndRemove()\n'; throw new TypeError(msg); } if (callback) { callback = this.$wrapCallback(callback); } return this.findOneAndRemove({_id: id}, options, callback); }
...
* - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
* - `select`: sets the document fields to return
* - `passRawResult`: if true, passes the [raw result from the MongoDB driver as the third callback parameter](http://mongodb.github
.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
* - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
*
* ####Examples:
*
* A.findByIdAndRemove(id, options, callback) // executes
* A.findByIdAndRemove(id, options) // return Query
* A.findByIdAndRemove(id, callback) // executes
* A.findByIdAndRemove(id) // returns Query
* A.findByIdAndRemove() // returns Query
*
* @param {Object|Number|String} id value of `_id` to query by
* @param {Object} [options]
...
findByIdAndUpdate = function (id, update, options, callback) { if (callback) { callback = this.$wrapCallback(callback); } if (arguments.length === 1) { if (typeof id === 'function') { var msg = 'Model.findByIdAndUpdate(): First argument must not be a function.\n\n' + ' ' + this.modelName + '.findByIdAndUpdate(id, callback)\n' + ' ' + this.modelName + '.findByIdAndUpdate(id)\n' + ' ' + this.modelName + '.findByIdAndUpdate()\n'; throw new TypeError(msg); } return this.findOneAndUpdate({_id: id}, undefined); } // if a model is passed in instead of an id if (id instanceof Document) { id = id._id; } return this.findOneAndUpdate.call(this, {_id: id}, update, options, callback); }
...
* - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
* - `select`: sets the document fields to return
* - `passRawResult`: if true, passes the [raw result from the MongoDB driver as the third callback parameter](http://mongodb.github
.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
* - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
*
* ####Examples:
*
* A.findByIdAndUpdate(id, update, options, callback) // executes
* A.findByIdAndUpdate(id, update, options) // returns Query
* A.findByIdAndUpdate(id, update, callback) // executes
* A.findByIdAndUpdate(id, update) // returns Query
* A.findByIdAndUpdate() // returns Query
*
* ####Note:
*
...
function findOne(conditions, projection, options, callback) { if (typeof options === 'function') { callback = options; options = null; } else if (typeof projection === 'function') { callback = projection; projection = null; options = null; } else if (typeof conditions === 'function') { callback = conditions; conditions = {}; projection = null; options = null; } // get the mongodb collection object var mq = new this.Query({}, {}, this, this.collection); mq.select(projection); mq.setOptions(options); if (this.schema.discriminatorMapping && mq.selectedInclusively()) { mq.select(this.schema.options.discriminatorKey); } if (callback) { callback = this.$wrapCallback(callback); } return mq.findOne(conditions, callback); }
...
};
/**
* Checks if `path` was selected in the source query which initialized this document.
*
* ####Example
*
* Thing.findOne().select('name').exec(function (err, doc) {
* doc.isSelected('name') // true
* doc.isSelected('age') // false
* })
*
* @param {String} path
* @return {Boolean}
* @api public
...
findOneAndRemove = function (conditions, options, callback) { if (arguments.length === 1 && typeof conditions === 'function') { var msg = 'Model.findOneAndRemove(): First argument must not be a function.\n\n' + ' ' + this.modelName + '.findOneAndRemove(conditions, callback)\n' + ' ' + this.modelName + '.findOneAndRemove(conditions)\n' + ' ' + this.modelName + '.findOneAndRemove()\n'; throw new TypeError(msg); } if (typeof options === 'function') { callback = options; options = undefined; } if (callback) { callback = this.$wrapCallback(callback); } var fields; if (options) { fields = options.select; options.select = undefined; } var mq = new this.Query({}, {}, this, this.collection); mq.select(fields); return mq.findOneAndRemove(conditions, options, callback); }
...
* - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
* - `select`: sets the document fields to return
* - `passRawResult`: if true, passes the [raw result from the MongoDB driver as the third callback parameter](http://mongodb.github
.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
* - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
*
* ####Examples:
*
* A.findOneAndRemove(conditions, options, callback) // executes
* A.findOneAndRemove(conditions, options) // return Query
* A.findOneAndRemove(conditions, callback) // executes
* A.findOneAndRemove(conditions) // returns Query
* A.findOneAndRemove() // returns Query
*
* Values are cast to their appropriate types when using the findAndModify helpers.
* However, the below are never executed.
...
findOneAndUpdate = function (conditions, update, options, callback) { if (typeof options === 'function') { callback = options; options = null; } else if (arguments.length === 1) { if (typeof conditions === 'function') { var msg = 'Model.findOneAndUpdate(): First argument must not be a function.\n\n' + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options, callback)\n' + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options)\n' + ' ' + this.modelName + '.findOneAndUpdate(conditions, update)\n' + ' ' + this.modelName + '.findOneAndUpdate(update)\n' + ' ' + this.modelName + '.findOneAndUpdate()\n'; throw new TypeError(msg); } update = conditions; conditions = undefined; } if (callback) { callback = this.$wrapCallback(callback); } var fields; if (options && options.fields) { fields = options.fields; } update = utils.clone(update, {depopulate: 1, _isNested: true}); if (this.schema.options.versionKey && options && options.upsert) { if (!update.$setOnInsert) { update.$setOnInsert = {}; } update.$setOnInsert[this.schema.options.versionKey] = 0; } var mq = new this.Query({}, {}, this, this.collection); mq.select(fields); return mq.findOneAndUpdate(conditions, update, options, callback); }
...
*
* Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if
any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned.
*
* ####Options:
*
* - `new`: bool - if true, return the modified document rather than the original. defaults to false (changed in 4.0)
* - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
* - `fields`: {Object|String} - Field selection. Equivalent to `.select(fields).findOneAndUpdate
()`
* - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
* - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
* - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators
validate the update operation against the model's schema.
* - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults
.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because
it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
* - `passRawResult`: if true, passes the [raw result from the MongoDB driver as the third callback parameter](http://mongodb.github
.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
* - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
*
...
geoNear = function (near, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } if (callback) { callback = this.$wrapCallback(callback); } var _this = this; var Promise = PromiseProvider.get(); if (!near) { return new Promise.ES6(function(resolve, reject) { var error = new Error('Must pass a near option to geoNear'); reject(error); callback && callback(error); }); } var x, y; return new Promise.ES6(function(resolve, reject) { var handler = function(err, res) { if (err) { reject(err); callback && callback(err); return; } if (options.lean) { resolve(res.results, res.stats); callback && callback(null, res.results, res.stats); return; } var count = res.results.length; // if there are no results, fulfill the promise now if (count === 0) { resolve(res.results, res.stats); callback && callback(null, res.results, res.stats); return; } var errSeen = false; function init(err) { if (err && !errSeen) { errSeen = true; reject(err); callback && callback(err); return; } if (--count <= 0) { resolve(res.results, res.stats); callback && callback(null, res.results, res.stats); } } for (var i = 0; i < res.results.length; i++) { var temp = res.results[i].obj; res.results[i].obj = new _this(); res.results[i].obj.init(temp, init); } }; if (Array.isArray(near)) { if (near.length !== 2) { var error = new Error('If using legacy coordinates, must be an array ' + 'of size 2 for geoNear'); reject(error); callback && callback(error); return; } x = near[0]; y = near[1]; _this.collection.geoNear(x, y, options, handler); } else { if (near.type !== 'Point' || !Array.isArray(near.coordinates)) { error = new Error('Must pass either a legacy coordinate array or ' + 'GeoJSON Point to geoNear'); reject(error); callback && callback(error); return; } _this.collection.geoNear(near, options, handler); } }); }
...
* ####Options:
* - `lean` {Boolean} return the raw object
* - All options supported by the driver are also supported
*
* ####Example:
*
* // Legacy point
* Model.geoNear([1,3], { maxDistance : 5, spherical : true }, function(err, results
, stats) {
* console.log(results);
* });
*
* // geoJson
* var point = { type : "Point", coordinates : [9,9] };
* Model.geoNear(point, { maxDistance : 5, spherical : true }, function(err, results, stats) {
* console.log(results);
...
geoSearch = function (conditions, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } if (callback) { callback = this.$wrapCallback(callback); } var _this = this; var Promise = PromiseProvider.get(); return new Promise.ES6(function(resolve, reject) { var error; if (conditions === undefined || !utils.isObject(conditions)) { error = new Error('Must pass conditions to geoSearch'); } else if (!options.near) { error = new Error('Must specify the near option in geoSearch'); } else if (!Array.isArray(options.near)) { error = new Error('near option must be an array [x, y]'); } if (error) { callback && callback(error); reject(error); return; } // send the conditions in the options object options.search = conditions; _this.collection.geoHaystackSearch(options.near[0], options.near[1], options, function(err, res) { // have to deal with driver problem. Should be fixed in a soon-ish release // (7/8/2013) if (err) { callback && callback(err); reject(err); return; } var count = res.results.length; if (options.lean || count === 0) { callback && callback(null, res.results, res.stats); resolve(res.results, res.stats); return; } var errSeen = false; function init(err) { if (err && !errSeen) { callback && callback(err); reject(err); return; } if (!--count && !errSeen) { callback && callback(null, res.results, res.stats); resolve(res.results, res.stats); } } for (var i = 0; i < res.results.length; i++) { var temp = res.results[i]; res.results[i] = new _this(); res.results[i].init(temp, {}, init); } }); }); }
...
/**
* Implements `$geoSearch` functionality for Mongoose
*
* ####Example:
*
* var options = { near: [10, 10], maxDistance: 5 };
* Locations.geoSearch({ type : "house" }, options, function(err, res) {
* console.log(res);
* });
*
* ####Options:
* - `near` {Array} x,y point to search for
* - `maxDistance` {Number} the maximum distance from the point near that a result can be
* - `limit` {Number} The maximum number of results to return
...
function getMaxListeners() { return $getMaxListeners(this); }
n/a
hydrate = function (obj) { var model = require('./queryhelpers').createModel(this, obj); model.init(obj); return model; }
...
/**
* Shortcut for creating a new Document from existing raw data, pre-saved in the DB.
* The document returned has no paths marked as modified initially.
*
* ####Example:
*
* // hydrate previous data into a Mongoose document
* var mongooseCandy = Candy.hydrate({ _id: '54108337212ffb6d459f854c',
type: 'jelly bean' });
*
* @param {Object} obj
* @return {Document}
* @api public
*/
Model.hydrate = function(obj) {
...
function init() { if ((this.schema.options.autoIndex) || (this.schema.options.autoIndex === null && this.db.config.autoIndex)) { this.ensureIndexes({ __noPromise: true, _automatic: true }); } this.schema.emit('init', this); }
...
this.$__.activePaths.require(required[i]);
}
this.$__.emitter.setMaxListeners(0);
this._doc = this.$__buildDoc(obj, fields, skipId);
if (!skipInit && obj) {
this.init(obj);
}
this.$__registerHooksFromSchema();
// apply methods
for (var m in schema.methods) {
this[m] = schema.methods[m];
...
insertMany = function (arr, options, callback) { var _this = this; if (typeof options === 'function') { callback = options; options = null; } if (callback) { callback = this.$wrapCallback(callback); } if (!Array.isArray(arr)) { arr = [arr]; } var toExecute = []; arr.forEach(function(doc) { toExecute.push(function(callback) { doc = new _this(doc); doc.validate({ __noPromise: true }, function(error) { if (error) { // Option `ordered` signals that insert should be continued after reaching // a failing insert. Therefore we delegate "null", meaning the validation // failed. It's up to the next function to filter out all failed models if (options != null && typeof options === 'object' && options['ordered'] === false) { return callback(null, null); } return callback(error); } callback(null, doc); }); }); }); parallel(toExecute, function(error, docs) { if (error) { callback && callback(error); return; } // We filter all failed pre-validations by removing nulls var docAttributes = docs.filter(function(doc) { return doc != null; }); // Quickly escape while there aren't any valid docAttributes if (docAttributes.length < 1) { callback && callback(null, []); return; } var docObjects = docAttributes.map(function(doc) { if (doc.schema.options.versionKey) { doc[doc.schema.options.versionKey] = 0; } if (doc.initializeTimestamps) { return doc.initializeTimestamps().toObject(POJO_TO_OBJECT_OPTIONS); } return doc.toObject(POJO_TO_OBJECT_OPTIONS); }); _this.collection.insertMany(docObjects, options, function(error) { if (error) { callback && callback(error); return; } for (var i = 0; i < docAttributes.length; ++i) { docAttributes[i].isNew = false; docAttributes[i].emit('isNew', false); docAttributes[i].constructor.emit('isNew', false); } callback && callback(null, docAttributes); }); }); }
...
* document.
*
* This function does **not** trigger save middleware.
*
* ####Example:
*
* var arr = [{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }];
* Movies.insertMany(arr, function(error, docs) {});
*
* @param {Array|Object|*} doc(s)
* @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html
#insertMany)
* @param {Function} [callback] callback
* @return {Promise}
* @api public
*/
...
function listenerCount(type) { const events = this._events; if (events) { const evlistener = events[type]; if (typeof evlistener === 'function') { return 1; } else if (evlistener) { return evlistener.length; } } return 0; }
n/a
function listeners(type) { var evlistener; var ret; var events = this._events; if (!events) ret = []; else { evlistener = events[type]; if (!evlistener) ret = []; else if (typeof evlistener === 'function') ret = [evlistener]; else ret = arrayClone(evlistener, evlistener.length); } return ret; }
...
doc[pair[0]].apply(doc, pair[1]);
}
}
}
Document.prototype.$__handleReject = function handleReject(err) {
// emit on the Model if listening
if (this.listeners('error').length) {
this.emit('error', err);
} else if (this.constructor.listeners && this.constructor.listeners('error').length) {
this.constructor.emit('error', err);
} else if (this.listeners && this.listeners('error').length) {
this.emit('error', err);
}
};
...
function mapReduce(o, callback) { var _this = this; if (callback) { callback = this.$wrapCallback(callback); } var resolveToObject = o.resolveToObject; var Promise = PromiseProvider.get(); return new Promise.ES6(function(resolve, reject) { if (!Model.mapReduce.schema) { var opts = {noId: true, noVirtualId: true, strict: false}; Model.mapReduce.schema = new Schema({}, opts); } if (!o.out) o.out = {inline: 1}; if (o.verbose !== false) o.verbose = true; o.map = String(o.map); o.reduce = String(o.reduce); if (o.query) { var q = new _this.Query(o.query); q.cast(_this); o.query = q._conditions; q = undefined; } _this.collection.mapReduce(null, null, o, function(err, ret, stats) { if (err) { callback && callback(err); reject(err); return; } if (ret.findOne && ret.mapReduce) { // returned a collection, convert to Model var model = Model.compile( '_mapreduce_' + ret.collectionName , Model.mapReduce.schema , ret.collectionName , _this.db , _this.base); model._mapreduce = true; callback && callback(null, model, stats); return resolveToObject ? resolve({ model: model, stats: stats }) : resolve(model, stats); } callback && callback(null, ret, stats); if (resolveToObject) { return resolve({ model: ret, stats: stats }); } resolve(ret, stats); }); }); }
...
* `o` is an object specifying all mapReduce options as well as the map and reduce functions. All options are delegated to the driver
implementation. See [node-mongodb-native mapReduce() documentation](http://mongodb.github.io/node-mongodb-native/api-generated/
collection.html#mapreduce) for more detail about options.
*
* ####Example:
*
* var o = {};
* o.map = function () { emit(this.name, 1) }
* o.reduce = function (k, vals) { return vals.length }
* User.mapReduce(o, function (err, results) {
* console.log(results)
* })
*
* ####Other options:
*
* - `query` {Object} query filter object.
* - `sort` {Object} sort input objects using this key
...
function addListener(type, listener) { return _addListener(this, type, listener, false); }
...
require('http').createServer(function(req, res) {
if (req.url === '/favicon.ico') {
req.destroy();
res.statusCode = 204;
return res.end();
}
req.on('end', function() {
server.serve(req, res, function(err) {
if (err) {
console.error(err, req.url);
res.writeHead(err.status, err.headers);
res.end();
}
});
...
function once(type, listener) { if (typeof listener !== 'function') throw new TypeError('"listener" argument must be a function'); this.on(type, _onceWrap(this, type, listener)); return this; }
...
aggregate(_this._pipeline, options || {});
decorateCursor(cursor);
resolve(cursor);
callback && callback(null, cursor);
});
return;
}
_this._model.collection.emitter.once('queue', function() {
var cursor = _this._model.collection.
aggregate(_this._pipeline, options || {});
decorateCursor(cursor);
resolve(cursor);
callback && callback(null, cursor);
});
});
...
populate = function (docs, paths, callback) { var _this = this; if (callback) { callback = this.$wrapCallback(callback); } // normalized paths var noPromise = paths && !!paths.__noPromise; paths = utils.populate(paths); // data that should persist across subPopulate calls var cache = {}; if (noPromise) { _populate(this, docs, paths, cache, callback); } else { var Promise = PromiseProvider.get(); return new Promise.ES6(function(resolve, reject) { _populate(_this, docs, paths, cache, function(error, docs) { if (error) { callback && callback(error); reject(error); } else { callback && callback(null, docs); resolve(docs); } }); }); } }
...
* Populates document references, executing the `callback` when complete.
* If you want to use promises instead, use this function with
* [`execPopulate()`](#document_Document-execPopulate)
*
* ####Example:
*
* doc
* .populate('company')
* .populate({
* path: 'notes',
* match: /airline/,
* select: 'text',
* model: 'modelName'
* options: opts
* }, function (err, user) {
...
function prependListener(type, listener) { return _addListener(this, type, listener, true); }
n/a
function prependOnceListener(type, listener) { if (typeof listener !== 'function') throw new TypeError('"listener" argument must be a function'); this.prependListener(type, _onceWrap(this, type, listener)); return this; }
n/a
function remove(conditions, callback) { if (typeof conditions === 'function') { callback = conditions; conditions = {}; } // get the mongodb collection object var mq = new this.Query(conditions, {}, this, this.collection); if (callback) { callback = this.$wrapCallback(callback); } return mq.remove(callback); }
...
```
The same goes for removing them:
```js
BlogPost.findById(myId, function (err, post) {
if (!err) {
post.comments[0].remove();
post.save(function (err) {
// do something
});
}
});
```
...
function removeAllListeners(type) { var listeners, events; events = this._events; if (!events) return this; // not listening for removeListener, no need to emit if (!events.removeListener) { if (arguments.length === 0) { this._events = new EventHandlers(); this._eventsCount = 0; } else if (events[type]) { if (--this._eventsCount === 0) this._events = new EventHandlers(); else delete events[type]; } return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { var keys = Object.keys(events); for (var i = 0, key; i < keys.length; ++i) { key = keys[i]; if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = new EventHandlers(); this._eventsCount = 0; return this; } listeners = events[type]; if (typeof listeners === 'function') { this.removeListener(type, listeners); } else if (listeners) { // LIFO order do { this.removeListener(type, listeners[listeners.length - 1]); } while (listeners[0]); } return this; }
n/a
function removeListener(type, listener) { var list, events, position, i, originalListener; if (typeof listener !== 'function') throw new TypeError('"listener" argument must be a function'); events = this._events; if (!events) return this; list = events[type]; if (!list) return this; if (list === listener || list.listener === listener) { if (--this._eventsCount === 0) this._events = new EventHandlers(); else { delete events[type]; if (events.removeListener) this.emit('removeListener', type, list.listener || listener); } } else if (typeof list !== 'function') { position = -1; for (i = list.length; i-- > 0;) { if (list[i] === listener || list[i].listener === listener) { originalListener = list[i].listener; position = i; break; } } if (position < 0) return this; if (list.length === 1) { list[0] = undefined; if (--this._eventsCount === 0) { this._events = new EventHandlers(); return this; } else { delete events[type]; } } else { spliceOne(list, position); } if (events.removeListener) this.emit('removeListener', type, originalListener || listener); } return this; }
...
}
if (!(value && value.isMongooseDocumentArray) &&
(!options || !options.skipDocumentArrayCast)) {
value = new MongooseDocumentArray(value, this.path, doc);
if (prev && prev._handlers) {
for (var key in prev._handlers) {
doc.removeListener(key, prev._handlers[key]);
}
}
} else if (value && value.isMongooseDocumentArray) {
// We need to create a new array, otherwise change tracking will
// update the old doc (gh-4449)
value = new MongooseDocumentArray(value, this.path, doc);
}
...
function replaceOne(conditions, doc, options, callback) { return _update(this, 'replaceOne', conditions, doc, options, callback); }
...
}
Query.base.updateOne.call(this, castedQuery, castedDoc, options, callback);
return this;
};
/*!
* Internal thunk for .replaceOne()
*
* @param {Function} callback
* @see Model.replaceOne #model_Model.replaceOne
* @api private
*/
Query.prototype._replaceOne = function(callback) {
var schema = this.model.schema;
...
function setMaxListeners(n) { if (typeof n !== 'number' || n < 0 || isNaN(n)) throw new TypeError('"n" argument must be a positive number'); this._maxListeners = n; return this; }
...
}
var required = this.schema.requiredPaths();
for (var i = 0; i < required.length; ++i) {
this.$__.activePaths.require(required[i]);
}
this.$__.emitter.setMaxListeners(0);
this._doc = this.$__buildDoc(obj, fields, skipId);
if (!skipInit && obj) {
this.init(obj);
}
this.$__registerHooksFromSchema();
...
function update(conditions, doc, options, callback) { return _update(this, 'update', conditions, doc, options, callback); }
...
}
/**
* Sends an update command with this document `_id` as the query selector.
*
* ####Example:
*
* weirdCar.update({$inc: {wheels:1}}, { w: 1 }, callback);
*
* ####Valid options:
*
* - same as in [Model.update](#model_Model.update)
*
* @see Model.update #model_Model.update
* @param {Object} doc
...
function updateMany(conditions, doc, options, callback) { return _update(this, 'updateMany', conditions, doc, options, callback); }
...
}
Query.base.update.call(this, castedQuery, castedDoc, options, callback);
return this;
};
/*!
* Internal thunk for .updateMany()
*
* @param {Function} callback
* @see Model.update #model_Model.update
* @api private
*/
Query.prototype._updateMany = function(callback) {
var schema = this.model.schema;
...
function updateOne(conditions, doc, options, callback) { return _update(this, 'updateOne', conditions, doc, options, callback); }
...
}
Query.base.updateMany.call(this, castedQuery, castedDoc, options, callback);
return this;
};
/*!
* Internal thunk for .updateOne()
*
* @param {Function} callback
* @see Model.update #model_Model.update
* @api private
*/
Query.prototype._updateOne = function(callback) {
var schema = this.model.schema;
...
function where(path, val) { void val; // eslint // get the mongodb collection object var mq = new this.Query({}, {}, this, this.collection).find({}); return mq.where.apply(mq, arguments); }
...
*
* For example, instead of writing:
*
* User.find({age: {$gte: 21, $lte: 65}}, callback);
*
* we can instead write:
*
* User.where('age').gte(21).lte(65).exec(callback);
*
* Since the Query class also supports `where` you can continue chaining
*
* User
* .where('age').gte(21).lte(65)
* .where('name', /^b/i)
* ... etc
...
function increment() { this.$__.version = VERSION_ALL; return this; }
...
// only increment the version if an array position changes.
// modifying elements of an array is ok if position does not change.
if (op === '$push' || op === '$pushAll' || op === '$addToSet') {
self.$__.version = VERSION_INC;
} else if (/^\$p/.test(op)) {
// potentially changing array positions
self.increment();
} else if (Array.isArray(val)) {
// $set an array
self.increment();
} else if (/\.\d+\.|\.\d+$/.test(data.path)) {
// now handling $set, $unset
// subpath of array
self.$__.version = VERSION_WHERE;
...
function model(name) { return this.db.model(name); }
...
});
```
Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
### Accessing a Model
Once we define a model through `mongoose.model('ModelName', mySchema)`, we
can access it through the same function
```js
var myModel = mongoose.model('ModelName');
```
Or just do it all at once
...
function remove(options, fn) { if (typeof options === 'function') { fn = options; options = undefined; } if (!options) { options = {}; } if (this.$__.removing) { if (fn) { this.$__.removing.then( function(res) { fn(null, res); }, function(err) { fn(err); }); } return this; } var _this = this; var Promise = PromiseProvider.get(); if (fn) { fn = this.constructor.$wrapCallback(fn); } this.$__.removing = new Promise.ES6(function(resolve, reject) { var where = _this.$__where(); if (where instanceof Error) { reject(where); fn && fn(where); return; } if (!options.safe && _this.schema.options.safe) { options.safe = _this.schema.options.safe; } _this.collection.remove(where, options, function(err) { if (!err) { _this.emit('remove', _this); _this.constructor.emit('remove', _this); resolve(_this); fn && fn(null, _this); return; } reject(err); fn && fn(err); }); }); return this.$__.removing; }
...
```
The same goes for removing them:
```js
BlogPost.findById(myId, function (err, post) {
if (!err) {
post.comments[0].remove();
post.save(function (err) {
// do something
});
}
});
```
...
save = function (options, fn) { if (typeof options === 'function') { fn = options; options = undefined; } if (!options) { options = {}; } if (fn) { fn = this.constructor.$wrapCallback(fn); } return this.$__save(options, fn); }
...
Then Mongoose will create the model for your __tickets__ collection, not your __ticket__ collection.
Once we have our model, we can then instantiate it, and save it:
```js
var instance = new MyModel();
instance.my.key = 'hello';
instance.save(function (err) {
//
});
```
Or we can find documents from the same collection
```js
...
function DocumentNotFoundError(query) { MongooseError.call(this, 'No document found for query "' + util.inspect(query) + '"'); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'DocumentNotFoundError'; this.query = query; }
n/a
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function SchemaNumber(key, options) { SchemaType.call(this, key, options, 'Number'); }
n/a
cast = function (value, doc, init) { if (SchemaType._isRef(this, value, doc, init)) { // wait! we may need to cast this to a document if (value === null || value === undefined) { return value; } // lazy load Document || (Document = require('./../document')); if (value instanceof Document) { value.$__.wasPopulated = true; return value; } // setting a populated path if (typeof value === 'number') { return value; } else if (Buffer.isBuffer(value) || !utils.isObject(value)) { throw new CastError('number', value, this.path); } // Handle the case where user directly sets a populated // path to a plain object; cast to the Model used in // the population query. var path = doc.$__fullPath(this.path); var owner = doc.ownerDocument ? doc.ownerDocument() : doc; var pop = owner.populated(path, true); var ret = new pop.options.model(value); ret.$__.wasPopulated = true; return ret; } var val = value && typeof value._id !== 'undefined' ? value._id : // documents value; if (!isNaN(val)) { if (val === null) { return val; } if (val === '') { return null; } if (typeof val === 'string' || typeof val === 'boolean') { val = Number(val); } if (val instanceof Number) { return val; } if (typeof val === 'number') { return val; } if (val.toString && !Array.isArray(val) && val.toString() == Number(val)) { return new Number(val); } } throw new CastError('number', value, this.path); }
...
doc[i] = obj[i];
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
...
castForQuery = function ($conditional, val) { var handler; if (arguments.length === 2) { handler = this.$conditionalHandlers[$conditional]; if (!handler) { throw new Error('Can\'t use ' + $conditional + ' with Number.'); } return handler.call(this, val); } val = this.cast($conditional); return val; }
...
}
if (geo) {
var numbertype = new Types.Number('__QueryCasting__');
var value = val[geo];
if (val.$maxDistance != null) {
val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
}
if (val.$minDistance != null) {
val.$minDistance = numbertype.castForQuery(val.$minDistance);
}
if (geo === '$within') {
var withinType = value.$center
...
function checkRequired(value, doc) { if (SchemaType._isRef(this, value, doc, true)) { return !!value; } return typeof value === 'number' || value instanceof Number; }
...
// in here, `this` refers to the validating document.
// no validation when this path wasn't selected in the query.
if ('isSelected' in this && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
return true;
}
return ((typeof required === 'function') && !required.apply(this)) ||
_this.checkRequired(v, this);
};
this.originalRequiredValue = required;
if (typeof required === 'string') {
message = required;
required = undefined;
}
...
function SchemaNumber(key, options) { SchemaType.call(this, key, options, 'Number'); }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
max = function (value, message) { if (this.maxValidator) { this.validators = this.validators.filter(function(v) { return v.validator !== this.maxValidator; }, this); } if (value !== null && value !== undefined) { var msg = message || MongooseError.messages.Number.max; msg = msg.replace(/{MAX}/, value); this.validators.push({ validator: this.maxValidator = function(v) { return v == null || v <= value; }, message: msg, type: 'max', max: value }); } return this; }
n/a
min = function (value, message) { if (this.minValidator) { this.validators = this.validators.filter(function(v) { return v.validator !== this.minValidator; }, this); } if (value !== null && value !== undefined) { var msg = message || MongooseError.messages.Number.min; msg = msg.replace(/{MIN}/, value); this.validators.push({ validator: this.minValidator = function(v) { return v == null || v >= value; }, message: msg, type: 'min', min: value }); } return this; }
n/a
function ObjectExpectedError(path, val) { MongooseError.call(this, 'Tried to set nested object field `' + path + '` to primitive value `' + val + '` and strict mode is set to throw.'); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'ObjectExpectedError'; this.path = path; }
n/a
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function ObjectId(key, options) { SchemaType.call(this, key, options, 'ObjectID'); }
n/a
auto = function (turnOn) { if (turnOn) { this.default(defaultId); this.set(resetId); } return this; }
n/a
cast = function (value, doc, init) { if (SchemaType._isRef(this, value, doc, init)) { // wait! we may need to cast this to a document if (value === null || value === undefined) { return value; } // lazy load Document || (Document = require('./../document')); if (value instanceof Document) { value.$__.wasPopulated = true; return value; } // setting a populated path if (value instanceof oid) { return value; } else if (Buffer.isBuffer(value) || !utils.isObject(value)) { throw new CastError('ObjectId', value, this.path); } // Handle the case where user directly sets a populated // path to a plain object; cast to the Model used in // the population query. var path = doc.$__fullPath(this.path); var owner = doc.ownerDocument ? doc.ownerDocument() : doc; var pop = owner.populated(path, true); var ret = value; if (!doc.$__.populated || !doc.$__.populated[path] || !doc.$__.populated[path].options || !doc.$__.populated[path].options.options || !doc.$__.populated[path].options.options.lean) { ret = new pop.options.model(value); ret.$__.wasPopulated = true; } return ret; } if (value === null || value === undefined) { return value; } if (value instanceof oid) { return value; } if (value._id) { if (value._id instanceof oid) { return value._id; } if (value._id.toString instanceof Function) { try { return new oid(value._id.toString()); } catch (e) { } } } if (value.toString instanceof Function) { try { return new oid(value.toString()); } catch (err) { throw new CastError('ObjectId', value, this.path); } } throw new CastError('ObjectId', value, this.path); }
...
doc[i] = obj[i];
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
...
castForQuery = function ($conditional, val) { var handler; if (arguments.length === 2) { handler = this.$conditionalHandlers[$conditional]; if (!handler) { throw new Error('Can\'t use ' + $conditional + ' with ObjectId.'); } return handler.call(this, val); } return this.cast($conditional); }
...
}
if (geo) {
var numbertype = new Types.Number('__QueryCasting__');
var value = val[geo];
if (val.$maxDistance != null) {
val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
}
if (val.$minDistance != null) {
val.$minDistance = numbertype.castForQuery(val.$minDistance);
}
if (geo === '$within') {
var withinType = value.$center
...
function checkRequired(value, doc) { if (SchemaType._isRef(this, value, doc, true)) { return !!value; } return value instanceof oid; }
...
// in here, `this` refers to the validating document.
// no validation when this path wasn't selected in the query.
if ('isSelected' in this && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
return true;
}
return ((typeof required === 'function') && !required.apply(this)) ||
_this.checkRequired(v, this);
};
this.originalRequiredValue = required;
if (typeof required === 'string') {
message = required;
required = undefined;
}
...
function ObjectId(key, options) { SchemaType.call(this, key, options, 'ObjectID'); }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function OverwriteModelError(name) { MongooseError.call(this, 'Cannot overwrite `' + name + '` model once compiled.'); Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee); this.name = 'OverwriteModelError'; }
n/a
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function Promise(fn) { MPromise.call(this, fn); }
n/a
ES6 = function (resolver) { var promise = new Promise(); // No try/catch for backwards compatibility resolver( function() { promise.complete.apply(promise, arguments); }, function(e) { promise.error(e); }); return promise; }
...
* @param {Function} callback
* @return {Promise}
*/
Aggregate.prototype.explain = function(callback) {
var _this = this;
var Promise = PromiseProvider.get();
return new Promise.ES6(function(resolve, reject) {
if (!_this._pipeline.length) {
var err = new Error('Aggregate has empty pipeline');
if (callback) {
callback(err);
}
reject(err);
return;
...
addBack = function (fn) { if (!fn) return this; if ('function' != typeof fn) throw new TypeError("fn should be a function"); this.on(Promise.FAILURE, function (err) { fn.call(this, err); }); this.on(Promise.SUCCESS, function () { fn.apply(this, strongUnshift(null, arguments)); }); return this; }
...
* // executing a query explicitly
* var query = MyModel.find({ name: /john/i }, null, { skip: 10 })
* query.exec(function (err, docs) {});
*
* // using the promise returned from executing a query
* var query = MyModel.find({ name: /john/i }, null, { skip: 10 });
* var promise = query.exec();
* promise.addBack(function (err, docs) {});
*
* @param {Object} conditions
* @param {Object} [projection] optional fields to return (http://bit.ly/1HotzBo)
* @param {Object} [options] optional
* @param {Function} [callback]
* @return {Query}
* @see field selection #query_Query-select
...
addCallback = function (fn) { if (!fn) return this; if ('function' != typeof fn) throw new TypeError("fn should be a function"); return this.on(Promise.SUCCESS, fn); }
n/a
addErrback = function (fn) { if (!fn) return this; if ('function' != typeof fn) throw new TypeError("fn should be a function"); return this.on(Promise.FAILURE, fn); }
n/a
catch = function (onReject) { return this.then(null, onReject); }
...
Promise.prototype.then = util.deprecate(Promise.prototype.then,
'Mongoose: mpromise (mongoose\'s default promise library) is deprecated, ' +
'plug in your own promise library instead: ' +
'http://mongoosejs.com/docs/promises.html');
/**
* ES6-style `.catch()` shorthand
*
* @method catch
* @memberOf Promise
* @param {Function} onReject
* @return {Promise}
* @api public
*/
...
complete = function () { return this.safeEmit.apply(this, strongUnshift(Promise.SUCCESS, arguments)); }
n/a
error = function (err) { if (!(err instanceof Error)) { if (err instanceof Object) { err = util.inspect(err); } err = new Error(err); } return this.reject(err); }
...
res.statusCode = 204;
return res.end();
}
req.on('end', function() {
server.serve(req, res, function(err) {
if (err) {
console.error(err, req.url);
res.writeHead(err.status, err.headers);
res.end();
}
});
});
req.resume();
}).listen(8088);
...
resolve = function (err) { if (err) return this.error(err); return this.fulfill.apply(this, Array.prototype.slice.call(arguments, 1)); }
n/a
function deprecated() { warned = exports.printDeprecationMessage(msg, warned, deprecated); if (new.target) { return Reflect.construct(fn, arguments, new.target); } return fn.apply(this, arguments); }
...
*
* ####Example:
*
* aggregate.exec(callback);
*
* // Because a promise is returned, the `callback` is optional.
* var promise = aggregate.exec();
* promise.then(..);
*
* @see Promise #promise_Promise
* @param {Function} [callback]
* @return {Promise}
* @api public
*/
...
function Promise(fn) { MPromise.call(this, fn); }
n/a
get = function () { return Promise._promise; }
...
// a setter
Comment.path('name').set(function (v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function (next) {
notify(this.get('email'));
next();
});
```
Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
### Accessing a Model
...
reset = function () { Promise._promise = MPromise; }
...
* Set the current promise constructor
*
* @api private
*/
Promise.set = function(lib) {
if (lib === MPromise) {
return Promise.reset();
}
Promise._promise = require('./ES6Promise');
Promise._promise.use(lib);
require('mquery').Promise = Promise._promise.ES6;
};
/**
...
set = function (lib) { if (lib === MPromise) { return Promise.reset(); } Promise._promise = require('./ES6Promise'); Promise._promise.use(lib); require('mquery').Promise = Promise._promise.ES6; }
...
age: { type: Number, min: 18, index: true },
bio: { type: String, match: /[a-z]/ },
date: { type: Date, default: Date.now },
buff: Buffer
});
// a setter
Comment.path('name').set(function (v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function (next) {
notify(this.get('email'));
next();
...
function Query(conditions, options, model, collection) { // this stuff is for dealing with custom queries created by #toConstructor if (!this._mongooseOptions) { this._mongooseOptions = {}; } // this is the case where we have a CustomQuery, we need to check if we got // options passed in, and if we did, merge them in if (options) { var keys = Object.keys(options); for (var i = 0; i < keys.length; ++i) { var k = keys[i]; this._mongooseOptions[k] = options[k]; } } if (collection) { this.mongooseCollection = collection; } if (model) { this.model = model; this.schema = model.schema; } // this is needed because map reduce returns a model that can be queried, but // all of the queries on said model should be lean if (this.model && this.model._mapreduce) { this.lean(); } // inherit mquery mquery.call(this, this.mongooseCollection, options); if (conditions) { this.find(conditions); } if (this.schema) { var kareemOptions = { useErrorHandlers: true, numCallbackParams: 1 }; this._count = this.model.hooks.createWrapper('count', Query.prototype._count, this, kareemOptions); this._execUpdate = this.model.hooks.createWrapper('update', Query.prototype._execUpdate, this, kareemOptions); this._find = this.model.hooks.createWrapper('find', Query.prototype._find, this, kareemOptions); this._findOne = this.model.hooks.createWrapper('findOne', Query.prototype._findOne, this, kareemOptions); this._findOneAndRemove = this.model.hooks.createWrapper('findOneAndRemove', Query.prototype._findOneAndRemove, this, kareemOptions); this._findOneAndUpdate = this.model.hooks.createWrapper('findOneAndUpdate', Query.prototype._findOneAndUpdate, this, kareemOptions); this._replaceOne = this.model.hooks.createWrapper('replaceOne', Query.prototype._replaceOne, this, kareemOptions); this._updateMany = this.model.hooks.createWrapper('updateMany', Query.prototype._updateMany, this, kareemOptions); this._updateOne = this.model.hooks.createWrapper('updateOne', Query.prototype._updateOne, this, kareemOptions); } }
n/a
function applyPaths() { this._fields = this._fields || {}; helpers.applyPaths(this._fields, this.model.schema); }
...
*/
Query.prototype._find = function(callback) {
if (this._castError) {
callback(this._castError);
return this;
}
this._applyPaths();
this._fields = this._castFields(this._fields);
var fields = this._fieldsForExec();
var options = this._mongooseOptions;
var _this = this;
var cb = function(err, docs) {
...
function _castFields(fields) { var selected, elemMatchKeys, keys, key, out, i; if (fields) { keys = Object.keys(fields); elemMatchKeys = []; i = keys.length; // collect $elemMatch args while (i--) { key = keys[i]; if (fields[key].$elemMatch) { selected || (selected = {}); selected[key] = fields[key]; elemMatchKeys.push(key); } } } if (selected) { // they passed $elemMatch, cast em try { out = this.cast(this.model, selected); } catch (err) { return err; } // apply the casted field args i = elemMatchKeys.length; while (i--) { key = elemMatchKeys[i]; fields[key] = out[key]; } } return fields; }
...
Query.prototype._find = function(callback) {
if (this._castError) {
callback(this._castError);
return this;
}
this._applyPaths();
this._fields = this._castFields(this._fields);
var fields = this._fieldsForExec();
var options = this._mongooseOptions;
var _this = this;
var cb = function(err, docs) {
if (err) {
...
function _castUpdate(obj, overwrite) { var strict; if ('strict' in this._mongooseOptions) { strict = this._mongooseOptions.strict; } else if (this.schema && this.schema.options) { strict = this.schema.options.strict; } else { strict = true; } return castUpdate(this.schema, obj, { overwrite: overwrite, strict: strict }); }
...
// validate the update part of the query
var castedDoc;
try {
var $options = {retainKeyOrder: true};
if (options && options.minimize) {
$options.minimize = true;
}
castedDoc = query._castUpdate(utils.clone(doc, $options),
(options && options.overwrite) || op === 'replaceOne');
} catch (err) {
query._castError = castedQuery;
if (callback) {
callback(err);
return query;
} else if (!options || !options.dontThrowCastError) {
...
_count = function (callback) { try { this.cast(this.model); } catch (err) { process.nextTick(function() { callback(err); }); return this; } var conds = this._conditions; var options = this._optionsForExec(); this._collection.count(conds, options, utils.tick(callback)); }
...
}
this.op = 'count';
if (!callback) {
return this;
}
this._count(callback);
return this;
};
/**
* Declares or executes a distict() operation.
*
...
_execUpdate = function (callback) { var schema = this.model.schema; var doValidate; var _this; var castedQuery = this._conditions; var castedDoc = this._update; var options = this.options; if (this._castError) { callback(this._castError); return this; } if (this.options.runValidators) { _this = this; doValidate = updateValidators(this, schema, castedDoc, options); var _callback = function(err) { if (err) { return callback(err); } Query.base.update.call(_this, castedQuery, castedDoc, options, callback); }; try { doValidate(_callback); } catch (err) { process.nextTick(function() { callback(err); }); } return this; } Query.base.update.call(this, castedQuery, castedDoc, options, callback); return this; }
...
if (!query._update) {
query._update = castedDoc;
}
// Hooks
if (callback) {
if (op === 'update') {
return query._execUpdate(callback);
}
return query['_' + op](callback);
}
return Query.base[op].call(query, castedQuery, castedDoc, options, callback);
}
...
_find = function (callback) { if (this._castError) { callback(this._castError); return this; } this._applyPaths(); this._fields = this._castFields(this._fields); var fields = this._fieldsForExec(); var options = this._mongooseOptions; var _this = this; var cb = function(err, docs) { if (err) { return callback(err); } if (docs.length === 0) { return callback(null, docs); } if (!options.populate) { return options.lean === true ? callback(null, docs) : completeMany(_this.model, docs, fields, null, callback); } var pop = helpers.preparePopulationOptionsMQ(_this, options); pop.__noPromise = true; _this.model.populate(docs, pop, function(err, docs) { if (err) return callback(err); return options.lean === true ? callback(null, docs) : completeMany(_this.model, docs, fields, pop, callback); }); }; return Query.base.find.call(this, {}, cb); }
...
}
// if we don't have a callback, then just return the query object
if (!callback) {
return Query.base.find.call(this);
}
this._find(callback);
return this;
};
/**
* Merges another Query or conditions object into this one.
*
...
_findAndModify = function (type, callback) { if (typeof callback !== 'function') { throw new Error('Expected callback in _findAndModify'); } var model = this.model; var schema = model.schema; var _this = this; var castedQuery; var castedDoc; var fields; var opts; var doValidate; castedQuery = castQuery(this); if (castedQuery instanceof Error) { return callback(castedQuery); } opts = this._optionsForExec(model); if ('strict' in opts) { this._mongooseOptions.strict = opts.strict; } if (type === 'remove') { opts.remove = true; } else { if (!('new' in opts)) { opts.new = false; } if (!('upsert' in opts)) { opts.upsert = false; } if (opts.upsert || opts['new']) { opts.remove = false; } castedDoc = castDoc(this, opts.overwrite); castedDoc = setDefaultsOnInsert(this, schema, castedDoc, opts); if (!castedDoc) { if (opts.upsert) { // still need to do the upsert to empty doc var doc = utils.clone(castedQuery); delete doc._id; castedDoc = {$set: doc}; } else { return this.findOne(callback); } } else if (castedDoc instanceof Error) { return callback(castedDoc); } else { // In order to make MongoDB 2.6 happy (see // https://jira.mongodb.org/browse/SERVER-12266 and related issues) // if we have an actual update document but $set is empty, junk the $set. if (castedDoc.$set && Object.keys(castedDoc.$set).length === 0) { delete castedDoc.$set; } } doValidate = updateValidators(this, schema, castedDoc, opts); } this._applyPaths(); var options = this._mongooseOptions; if (this._fields) { fields = utils.clone(this._fields); opts.fields = this._castFields(fields); if (opts.fields instanceof Error) { return callback(opts.fields); } } if (opts.sort) convertSortToArray(opts); var cb = function(err, doc, res) { if (err) { return callback(err); } if (!doc || (utils.isObject(doc) && Object.keys(doc).length === 0)) { if (opts.rawResult) { return callback(null, res); } // opts.passRawResult will be deprecated soon if (opts.passRawResult) { return callback(null, null, decorateResult(res)); } return callback(null, null); } if (!options.populate) { if (options.lean === true) { return _completeOneLean(doc, res, opts, callback); } return completeOne(_this.model, doc, res, opts, fields, null, callback); } var pop = helpers.preparePopulationOptionsMQ(_this, options); pop.__noPromise = true; _this.model.populate(doc, pop, function(err, doc) { if (err) { return callback(err); } if (options.lean === true) { return _completeOneLean(doc, res, opts, callback); } return completeOne(_this.model, doc, res, opts, fields, pop, callback); }); }; if (opts.runValidators && doValidate) { var _callback = function(error) { if (error) { return callback(error); } _this._collection.findAndModify(castedQuery, castedDoc, opts, utils.tick(function(error, res) { return cb(error, res ? res.value : res, res); })); }; try { doValidate(_callback); } catch (error) { callback(error); } } else { this._collection.findAndModify(castedQuery, castedDoc, opts, utils.tick(function(error, res) { return cb(error, res ? res.value : res, res); })); } return this; }
...
* Thunk around findOneAndUpdate()
*
* @param {Function} [callback]
* @api private
*/
Query.prototype._findOneAndUpdate = function(callback) {
this._findAndModify('update', callback);
return this;
};
/**
* Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) remove command.
*
* Finds a matching document, removes it, passing the found document (if any) to the callback. Executes immediately if `callback`
is passed.
...
_findOne = function (callback) { if (this._castError) { return callback(this._castError); } this._applyPaths(); this._fields = this._castFields(this._fields); var options = this._mongooseOptions; var projection = this._fieldsForExec(); var _this = this; // don't pass in the conditions because we already merged them in Query.base.findOne.call(_this, {}, function(err, doc) { if (err) { return callback(err); } if (!doc) { return callback(null, null); } if (!options.populate) { return options.lean === true ? callback(null, doc) : completeOne(_this.model, doc, null, {}, projection, null, callback); } var pop = helpers.preparePopulationOptionsMQ(_this, options); pop.__noPromise = true; _this.model.populate(doc, pop, function(err, doc) { if (err) { return callback(err); } return options.lean === true ? callback(null, doc) : completeOne(_this.model, doc, null, {}, projection, pop, callback); }); }); }
...
}
if (!callback) {
// already merged in the conditions, don't need to send them in.
return Query.base.findOne.call(this);
}
this._findOne(callback);
return this;
};
/**
* Thunk around count()
*
...
_findOneAndRemove = function (callback) { Query.base.findOneAndRemove.call(this, callback); }
...
options && this.setOptions(options);
if (!callback) {
return this;
}
this._findOneAndRemove(callback);
return this;
};
/*!
* Thunk around findOneAndRemove()
*
...
_findOneAndUpdate = function (callback) { this._findAndModify('update', callback); return this; }
...
this.setOptions(options);
}
if (!callback) {
return this;
}
return this._findOneAndUpdate(callback);
};
/*!
* Thunk around findOneAndUpdate()
*
* @param {Function} [callback]
* @api private
...
_mergeUpdate = function (doc) { if (!this._update) this._update = {}; if (doc instanceof Query) { if (doc._update) { utils.mergeClone(this._update, doc._update); } } else { utils.mergeClone(this._update, doc); } }
...
if (mquery.canMerge(criteria)) {
this.merge(criteria);
}
// apply doc
if (doc) {
this._mergeUpdate(doc);
}
if (options) {
options = utils.clone(options, { retainKeyOrder: true });
if (options.projection) {
this.select(options.projection);
delete options.projection;
...
_optionsForExec = function (model) { var options = Query.base._optionsForExec.call(this); delete options.populate; delete options.retainKeyOrder; model = model || this.model; if (!model) { return options; } if (!('safe' in options) && model.schema.options.safe) { options.safe = model.schema.options.safe; } if (!('readPreference' in options) && model.schema.options.read) { options.readPreference = model.schema.options.read; } return options; }
...
process.nextTick(function() {
callback(err);
});
return this;
}
var conds = this._conditions;
var options = this._optionsForExec();
this._collection.count(conds, options, utils.tick(callback));
};
/**
* Specifying this query as a `count` query.
*
...
_replaceOne = function (callback) { var schema = this.model.schema; var doValidate; var _this; var castedQuery = this._conditions; var castedDoc = this._update; var options = this.options; if (this._castError) { callback(this._castError); return this; } if (this.options.runValidators) { _this = this; doValidate = updateValidators(this, schema, castedDoc, options); var _callback = function(err) { if (err) { return callback(err); } Query.base.updateMany.call(_this, castedQuery, castedDoc, options, callback); }; try { doValidate(_callback); } catch (err) { process.nextTick(function() { callback(err); }); } return this; } Query.base.replaceOne.call(this, castedQuery, castedDoc, options, callback); return this; }
n/a
_updateForExec = function () { var update = utils.clone(this._update, { retainKeyOrder: true, transform: false, depopulate: true }); var ops = Object.keys(update); var i = ops.length; var ret = {}; while (i--) { var op = ops[i]; if (this.options.overwrite) { ret[op] = update[op]; continue; } if ('$' !== op[0]) { // fix up $set sugar if (!ret.$set) { if (update.$set) { ret.$set = update.$set; } else { ret.$set = {}; } } ret.$set[op] = update[op]; ops.splice(i, 1); if (!~ops.indexOf('$set')) ops.push('$set'); } else if ('$set' === op) { if (!ret.$set) { ret[op] = update[op]; } } else { ret[op] = update[op]; } } return ret; }
...
// if doc is undefined at this point, this means this function is being
// executed by exec(not always see below). Grab the update doc from here in
// order to validate
// This could also be somebody calling update() or update({}). Probably not a
// common use case, check for _update to make sure we don't do anything bad
if (!doc && query._update) {
doc = query._updateForExec();
}
if (mquery.canMerge(conditions)) {
query.merge(conditions);
}
// validate the selector part of the query
...
_updateMany = function (callback) { var schema = this.model.schema; var doValidate; var _this; var castedQuery = this._conditions; var castedDoc = this._update; var options = this.options; if (this._castError) { callback(this._castError); return this; } if (this.options.runValidators) { _this = this; doValidate = updateValidators(this, schema, castedDoc, options); var _callback = function(err) { if (err) { return callback(err); } Query.base.updateMany.call(_this, castedQuery, castedDoc, options, callback); }; try { doValidate(_callback); } catch (err) { process.nextTick(function() { callback(err); }); } return this; } Query.base.updateMany.call(this, castedQuery, castedDoc, options, callback); return this; }
n/a
_updateOne = function (callback) { var schema = this.model.schema; var doValidate; var _this; var castedQuery = this._conditions; var castedDoc = this._update; var options = this.options; if (this._castError) { callback(this._castError); return this; } if (this.options.runValidators) { _this = this; doValidate = updateValidators(this, schema, castedDoc, options); var _callback = function(err) { if (err) { return callback(err); } Query.base.updateOne.call(_this, castedQuery, castedDoc, options, callback); }; try { doValidate(_callback); } catch (err) { process.nextTick(function() { callback(err); }); } return this; } Query.base.updateOne.call(this, castedQuery, castedDoc, options, callback); return this; }
n/a
box = function (ll, ur) { if (!Array.isArray(ll) && utils.isObject(ll)) { ur = ll.ur; ll = ll.ll; } return Query.base.box.call(this, ll, ur); }
...
*/
/**
* Defines a `$within` or `$geoWithin` argument for geo-spatial queries.
*
* ####Example
*
* query.where(path).within().box()
* query.where(path).within().circle()
* query.where(path).within().geometry()
*
* query.where('loc').within({ center: [50,50], radius: 10, unique: true, spherical: true });
* query.where('loc').within({ box: [[40.73, -73.9], [40.7, -73.988]] });
* query.where('loc').within({ polygon: [[],[],[],[]] });
*
...
cast = function (model, obj) { obj || (obj = this._conditions); try { return cast(model.schema, obj, { upsert: this.options && this.options.upsert, strict: (this.options && this.options.strict) || (model.schema.options && model.schema.options.strict) }); } catch (err) { // CastError, assign model if (typeof err.setModel === 'function') { err.setModel(model); } throw err; } }
...
doc[i] = obj[i];
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
...
catch = function (reject) { return this.exec().then(null, reject); }
...
Promise.prototype.then = util.deprecate(Promise.prototype.then,
'Mongoose: mpromise (mongoose\'s default promise library) is deprecated, ' +
'plug in your own promise library instead: ' +
'http://mongoosejs.com/docs/promises.html');
/**
* ES6-style `.catch()` shorthand
*
* @method catch
* @memberOf Promise
* @param {Function} onReject
* @return {Promise}
* @api public
*/
...
center = function () { var path, val; if (1 === arguments.length) { this._ensurePath('circle'); path = this._path; val = arguments[0]; } else if (2 === arguments.length) { path = arguments[0]; val = arguments[1]; } else { throw new TypeError("Invalid argument"); } if (!('radius' in val && val.center)) throw new Error('center and radius are required'); var conds = this._conditions[path] || (this._conditions[path] = {}); var type = val.spherical ? '$centerSphere' : '$center'; var wKey = this._geoComparison || $withinCmd; conds[wKey] = {}; conds[wKey][type] = [val.center, val.radius]; if ('unique' in val) conds[wKey].$uniqueDocs = !! val.unique; return this; }
n/a
centerSphere = function () { if (arguments[0] && arguments[0].constructor.name === 'Object') { arguments[0].spherical = true; } if (arguments[1] && arguments[1].constructor.name === 'Object') { arguments[1].spherical = true; } Query.base.circle.apply(this, arguments); }
...
* _DEPRECATED_ Specifies a $centerSphere condition
*
* **Deprecated.** Use [circle](#query_Query-circle) instead.
*
* ####Example
*
* var area = { center: [50, 50], radius: 10 };
* query.where('loc').within().centerSphere(area);
*
* @deprecated
* @param {String} [path]
* @param {Object} val
* @return {Query} this
* @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
* @see $centerSphere http://docs.mongodb.org/manual/reference/operator/centerSphere/
...
collation = function (value) { if (this.options == null) { this.options = {}; } this.options.collation = value; return this; }
...
};
/**
* Adds a collation
*
* ####Example:
*
* Model.aggregate(..).collation({ locale: 'en_US', strength: 1 }).exec
();
*
* @param {Object} collation options
* @param {Boolean} value
* @see mongodb http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#aggregate
*/
Aggregate.prototype.collation = function(collation) {
...
function Query(conditions, options, model, collection) { // this stuff is for dealing with custom queries created by #toConstructor if (!this._mongooseOptions) { this._mongooseOptions = {}; } // this is the case where we have a CustomQuery, we need to check if we got // options passed in, and if we did, merge them in if (options) { var keys = Object.keys(options); for (var i = 0; i < keys.length; ++i) { var k = keys[i]; this._mongooseOptions[k] = options[k]; } } if (collection) { this.mongooseCollection = collection; } if (model) { this.model = model; this.schema = model.schema; } // this is needed because map reduce returns a model that can be queried, but // all of the queries on said model should be lean if (this.model && this.model._mapreduce) { this.lean(); } // inherit mquery mquery.call(this, this.mongooseCollection, options); if (conditions) { this.find(conditions); } if (this.schema) { var kareemOptions = { useErrorHandlers: true, numCallbackParams: 1 }; this._count = this.model.hooks.createWrapper('count', Query.prototype._count, this, kareemOptions); this._execUpdate = this.model.hooks.createWrapper('update', Query.prototype._execUpdate, this, kareemOptions); this._find = this.model.hooks.createWrapper('find', Query.prototype._find, this, kareemOptions); this._findOne = this.model.hooks.createWrapper('findOne', Query.prototype._findOne, this, kareemOptions); this._findOneAndRemove = this.model.hooks.createWrapper('findOneAndRemove', Query.prototype._findOneAndRemove, this, kareemOptions); this._findOneAndUpdate = this.model.hooks.createWrapper('findOneAndUpdate', Query.prototype._findOneAndUpdate, this, kareemOptions); this._replaceOne = this.model.hooks.createWrapper('replaceOne', Query.prototype._replaceOne, this, kareemOptions); this._updateMany = this.model.hooks.createWrapper('updateMany', Query.prototype._updateMany, this, kareemOptions); this._updateOne = this.model.hooks.createWrapper('updateOne', Query.prototype._updateOne, this, kareemOptions); } }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
count = function (conditions, callback) { if (typeof conditions === 'function') { callback = conditions; conditions = undefined; } if (mquery.canMerge(conditions)) { this.merge(conditions); } this.op = 'count'; if (!callback) { return this; } this._count(callback); return this; }
...
};
/**
* Counts number of matching documents in a database collection.
*
* ####Example:
*
* Adventure.count({ type: 'jungle' }, function (err, count) {
* if (err) ..
* console.log('there are %d jungle adventures', count);
* });
*
* @param {Object} conditions
* @param {Function} [callback]
* @return {Query}
...
function cursor(opts) { this._applyPaths(); this._fields = this._castFields(this._fields); this.setOptions({ fields: this._fieldsForExec() }); if (opts) { this.setOptions(opts); } try { this.cast(this.model); } catch (err) { return (new QueryCursor(this, this.options))._markError(err); } return new QueryCursor(this, this.options); }
...
/**
* Sets the cursor option option for the aggregation query (ignored for < 2.6.0).
* Note the different syntax below: .exec() returns a cursor object, and no callback
* is necessary.
*
* ####Example:
*
* var cursor = Model.aggregate(..).cursor({ batchSize: 1000 }).exec();
* cursor.each(function(error, doc) {
* // use doc
* });
*
* @param {Object} options set the cursor batch size
* @see mongodb http://mongodb.github.io/node-mongodb-native/2.0/api/AggregationCursor.html
*/
...
deleteMany = function (cond, callback) { if (typeof cond === 'function') { callback = cond; cond = null; } var cb = typeof callback === 'function'; try { this.cast(this.model); } catch (err) { if (cb) return process.nextTick(callback.bind(null, err)); return this; } return Query.base.deleteMany.call(this, cond, callback); }
...
/**
* Deletes the first document that matches `conditions` from the collection.
* Behaves like `remove()`, but deletes all documents that match `conditions`
* regardless of the `justOne` option.
*
* ####Example:
*
* Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }, function (err) {});
*
* ####Note:
*
* Like `Model.remove()`, this function does **not** trigger `pre('remove')` or `post('remove')` hooks.
*
* @param {Object} conditions
* @param {Function} [callback]
...
deleteOne = function (cond, callback) { if (typeof cond === 'function') { callback = cond; cond = null; } var cb = typeof callback === 'function'; try { this.cast(this.model); } catch (err) { if (cb) return process.nextTick(callback.bind(null, err)); return this; } return Query.base.deleteOne.call(this, cond, callback); }
...
/**
* Deletes the first document that matches `conditions` from the collection.
* Behaves like `remove()`, but deletes at most one document regardless of the
* `justOne` option.
*
* ####Example:
*
* Character.deleteOne({ name: 'Eddard Stark' }, function (err) {});
*
* ####Note:
*
* Like `Model.remove()`, this function does **not** trigger `pre('remove')` or `post('remove')` hooks.
*
* @param {Object} conditions
* @param {Function} [callback]
...
distinct = function (field, conditions, callback) { if (!callback) { if (typeof conditions === 'function') { callback = conditions; conditions = undefined; } else if (typeof field === 'function') { callback = field; field = undefined; conditions = undefined; } } conditions = utils.toObject(conditions); if (mquery.canMerge(conditions)) { this.merge(conditions); } try { this.cast(this.model); } catch (err) { if (!callback) { throw err; } callback(err); return this; } return Query.base.distinct.call(this, {}, field, callback); }
...
/**
* Creates a Query for a `distinct` operation.
*
* Passing a `callback` immediately executes the query.
*
* ####Example
*
* Link.distinct('url', { clicks: {$gt: 100}}, function (err, result) {
* if (err) return handleError(err);
*
* assert(Array.isArray(result));
* console.log('unique urls with more than 100 clicks', result);
* })
*
* var query = Link.distinct('url');
...
function exec(op, callback) { var Promise = PromiseProvider.get(); var _this = this; if (typeof op === 'function') { callback = op; op = null; } else if (typeof op === 'string') { this.op = op; } var _results; var promise = new Promise.ES6(function(resolve, reject) { if (!_this.op) { resolve(); return; } _this[_this.op].call(_this, function(error, res) { if (error) { reject(error); return; } _results = arguments; resolve(res); }); }); if (callback) { promise.then( function() { callback.apply(null, _results); return null; }, function(error) { callback(error); }). catch(function(error) { // If we made it here, we must have an error in the callback re: // gh-4500, so we need to emit. setImmediate(function() { _this.model.emit('error', error); }); }); } return promise; }
...
function getVersion() {
var hist = fs.readFileSync('./History.md', 'utf8').replace(/\r/g, '\n').split('\n');
for (var i = 0; i < hist.length; ++i) {
var line = (hist[i] || '').trim();
if (!line) {
continue;
}
var match = /^\s*([^\s]+)\s/.exec(line);
if (match && match[1]) {
return match[1];
}
}
throw new Error('no match found');
}
...
find = function (conditions, callback) { if (typeof conditions === 'function') { callback = conditions; conditions = {}; } conditions = utils.toObject(conditions); if (mquery.canMerge(conditions)) { this.merge(conditions); } prepareDiscriminatorCriteria(this); try { this.cast(this.model); this._castError = null; } catch (err) { this._castError = err; } // if we don't have a callback, then just return the query object if (!callback) { return Query.base.find.call(this); } this._find(callback); return this; }
...
//
});
```
Or we can find documents from the same collection
```js
MyModel.find({}, function (err, docs) {
// docs.forEach
});
```
You can also `findOne`, `findById`, `update`, etc. For more details check out [the docs](http://mongoosejs.com/docs/queries.html
).
**Important!** If you opened a separate connection using `mongoose.createConnection()` but attempt to access the model through `
mongoose.model('ModelName')` it will not work as expected since it is not hooked up to an active db connection. In this
case access your model through the connection you created:
...
findOne = function (conditions, projection, options, callback) { if (typeof conditions === 'function') { callback = conditions; conditions = null; projection = null; options = null; } else if (typeof projection === 'function') { callback = projection; options = null; projection = null; } else if (typeof options === 'function') { callback = options; options = null; } // make sure we don't send in the whole Document to merge() conditions = utils.toObject(conditions); this.op = 'findOne'; if (options) { this.setOptions(options); } if (projection) { this.select(projection); } if (mquery.canMerge(conditions)) { this.merge(conditions); } else if (conditions != null) { throw new Error('Invalid argument to findOne(): ' + util.inspect(conditions)); } prepareDiscriminatorCriteria(this); try { this.cast(this.model); this._castError = null; } catch (err) { this._castError = err; } if (!callback) { // already merged in the conditions, don't need to send them in. return Query.base.findOne.call(this); } this._findOne(callback); return this; }
...
};
/**
* Checks if `path` was selected in the source query which initialized this document.
*
* ####Example
*
* Thing.findOne().select('name').exec(function (err, doc) {
* doc.isSelected('name') // true
* doc.isSelected('age') // false
* })
*
* @param {String} path
* @return {Boolean}
* @api public
...
findOneAndRemove = function (conditions, options, callback) { this.op = 'findOneAndRemove'; this._validate(); switch (arguments.length) { case 2: if (typeof options === 'function') { callback = options; options = {}; } break; case 1: if (typeof conditions === 'function') { callback = conditions; conditions = undefined; options = undefined; } break; } if (mquery.canMerge(conditions)) { this.merge(conditions); } options && this.setOptions(options); if (!callback) { return this; } this._findOneAndRemove(callback); return this; }
...
* - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
* - `select`: sets the document fields to return
* - `passRawResult`: if true, passes the [raw result from the MongoDB driver as the third callback parameter](http://mongodb.github
.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
* - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
*
* ####Examples:
*
* A.findOneAndRemove(conditions, options, callback) // executes
* A.findOneAndRemove(conditions, options) // return Query
* A.findOneAndRemove(conditions, callback) // executes
* A.findOneAndRemove(conditions) // returns Query
* A.findOneAndRemove() // returns Query
*
* Values are cast to their appropriate types when using the findAndModify helpers.
* However, the below are never executed.
...
findOneAndUpdate = function (criteria, doc, options, callback) { this.op = 'findOneAndUpdate'; this._validate(); switch (arguments.length) { case 3: if (typeof options === 'function') { callback = options; options = {}; } break; case 2: if (typeof doc === 'function') { callback = doc; doc = criteria; criteria = undefined; } options = undefined; break; case 1: if (typeof criteria === 'function') { callback = criteria; criteria = options = doc = undefined; } else { doc = criteria; criteria = options = undefined; } } if (mquery.canMerge(criteria)) { this.merge(criteria); } // apply doc if (doc) { this._mergeUpdate(doc); } if (options) { options = utils.clone(options, { retainKeyOrder: true }); if (options.projection) { this.select(options.projection); delete options.projection; } if (options.fields) { this.select(options.fields); delete options.fields; } this.setOptions(options); } if (!callback) { return this; } return this._findOneAndUpdate(callback); }
...
*
* Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if
any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned.
*
* ####Options:
*
* - `new`: bool - if true, return the modified document rather than the original. defaults to false (changed in 4.0)
* - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
* - `fields`: {Object|String} - Field selection. Equivalent to `.select(fields).findOneAndUpdate
()`
* - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
* - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
* - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators
validate the update operation against the model's schema.
* - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults
.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because
it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
* - `passRawResult`: if true, passes the [raw result from the MongoDB driver as the third callback parameter](http://mongodb.github
.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
* - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
*
...
getQuery = function () { return this._conditions; }
...
/**
* Returns the current query conditions as a JSON object.
*
* ####Example:
*
* var query = new Query();
* query.find({ a: 1 }).where('b').gt(2);
* query.getQuery(); // { a: 1, b: { $gt: 2 } }
*
* @return {Object} current query conditions
* @api public
*/
Query.prototype.getQuery = function() {
return this._conditions;
...
getUpdate = function () { return this._update; }
...
/**
* Returns the current update operations as a JSON object.
*
* ####Example:
*
* var query = new Query();
* query.update({}, { $set: { a: 5 } });
* query.getUpdate(); // { $set: { a: 5 } }
*
* @return {Object} current update operations
* @api public
*/
Query.prototype.getUpdate = function() {
return this._update;
...
lean = function (v) { this._mongooseOptions.lean = arguments.length ? !!v : true; return this; }
...
* // include all properties except for `length`
* Adventure.findById(id, '-length').exec(function (err, adventure) {});
*
* // passing options (in this case return the raw js objects, not mongoose documents by passing `lean`
* Adventure.findById(id, 'name', { lean: true }, function (err, doc) {});
*
* // same as above
* Adventure.findById(id, 'name').lean().exec(function (err, doc) {});
*
* @param {Object|String|Number} id value of `_id` to query by
* @param {Object} [projection] optional fields to return (http://bit.ly/1HotzBo)
* @param {Object} [options] optional
* @param {Function} [callback]
* @return {Query}
* @see field selection #query_Query-select
...
maxscan = function (v) { this._validate(method); this.options[method] = v; return this; }
n/a
merge = function (source) { if (!source) { return this; } var opts = { retainKeyOrder: this.options.retainKeyOrder, overwrite: true }; if (source instanceof Query) { // if source has a feature, apply it to ourselves if (source._conditions) { utils.merge(this._conditions, source._conditions, opts); } if (source._fields) { this._fields || (this._fields = {}); utils.merge(this._fields, source._fields, opts); } if (source.options) { this.options || (this.options = {}); utils.merge(this.options, source.options, opts); } if (source._update) { this._update || (this._update = {}); utils.mergeClone(this._update, source._update); } if (source._distinct) { this._distinct = source._distinct; } return this; } // plain object utils.merge(this._conditions, source, opts); return this; }
...
};
if (isVirtual && virtual.options && virtual.options.options) {
currentOptions.options = utils.clone(virtual.options.options, {
retainKeyOrder: true
});
}
utils.merge(currentOptions, options);
if (schema && !discriminatorKey) {
currentOptions.model = Model;
}
options.model = Model;
available[modelName] = {
Model: Model,
...
mongooseOptions = function (v) { if (arguments.length > 0) { this._mongooseOptions = v; } return this._mongooseOptions; }
n/a
near = function () { var params = []; var sphere = this._mongooseOptions.nearSphere; // TODO refactor if (arguments.length === 1) { if (Array.isArray(arguments[0])) { params.push({center: arguments[0], spherical: sphere}); } else if (typeof arguments[0] === 'string') { // just passing a path params.push(arguments[0]); } else if (utils.isObject(arguments[0])) { if (typeof arguments[0].spherical !== 'boolean') { arguments[0].spherical = sphere; } params.push(arguments[0]); } else { throw new TypeError('invalid argument'); } } else if (arguments.length === 2) { if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') { params.push({center: [arguments[0], arguments[1]], spherical: sphere}); } else if (typeof arguments[0] === 'string' && Array.isArray(arguments[1])) { params.push(arguments[0]); params.push({center: arguments[1], spherical: sphere}); } else if (typeof arguments[0] === 'string' && utils.isObject(arguments[1])) { params.push(arguments[0]); if (typeof arguments[1].spherical !== 'boolean') { arguments[1].spherical = sphere; } params.push(arguments[1]); } else { throw new TypeError('invalid argument'); } } else if (arguments.length === 3) { if (typeof arguments[0] === 'string' && typeof arguments[1] === 'number' && typeof arguments[2] === 'number') { params.push(arguments[0]); params.push({center: [arguments[1], arguments[2]], spherical: sphere}); } else { throw new TypeError('invalid argument'); } } else { throw new TypeError('invalid argument'); } return Query.base.near.apply(this, params); }
...
*
* ####NOTE:
*
* **MUST** be used as the first operator in the pipeline.
*
* ####Examples:
*
* aggregate.near({
* near: [40.724, -73.997],
* distanceField: "dist.calculated", // required
* maxDistance: 0.008,
* query: { type: "public" },
* includeLocs: "dist.location",
* uniqueDocs: true,
* num: 5
...
nearSphere = function () { this._mongooseOptions.nearSphere = true; this.near.apply(this, arguments); return this; }
...
};
/**
* _DEPRECATED_ Specifies a `$nearSphere` condition
*
* ####Example
*
* query.where('loc').nearSphere({ center: [10, 10], maxDistance: 5 });
*
* **Deprecated.** Use `query.near()` instead with the `spherical` option set to `true`.
*
* ####Example
*
* query.where('loc').near({ center: [10, 10], spherical: true });
*
...
populate = function () { if (arguments.length === 0) { return this; } var res = utils.populate.apply(null, arguments); var opts = this._mongooseOptions; if (!utils.isObject(opts.populate)) { opts.populate = {}; } var pop = opts.populate; for (var i = 0; i < res.length; ++i) { var path = res[i].path; if (pop[path] && pop[path].populate && res[i].populate) { res[i].populate = pop[path].populate.concat(res[i].populate); } pop[res[i].path] = res[i]; } return this; }
...
* Populates document references, executing the `callback` when complete.
* If you want to use promises instead, use this function with
* [`execPopulate()`](#document_Document-execPopulate)
*
* ####Example:
*
* doc
* .populate('company')
* .populate({
* path: 'notes',
* match: /airline/,
* select: 'text',
* model: 'modelName'
* options: opts
* }, function (err, user) {
...
function read(pref, tags) { // first cast into a ReadPreference object to support tags var read = readPref.call(readPref, pref, tags); return Query.base.read.call(this, read); }
...
};
/**
* Sets the readPreference option for the aggregation query.
*
* ####Example:
*
* Model.aggregate(..).read('primaryPreferred').exec(callback)
*
* @param {String} pref one of the listed preference options or their aliases
* @param {Array} [tags] optional tags for this query
* @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
* @see driver http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences
*/
...
remove = function (cond, callback) { if (typeof cond === 'function') { callback = cond; cond = null; } var cb = typeof callback === 'function'; try { this.cast(this.model); } catch (err) { if (cb) return process.nextTick(callback.bind(null, err)); return this; } return Query.base.remove.call(this, cond, callback); }
...
```
The same goes for removing them:
```js
BlogPost.findById(myId, function (err, post) {
if (!err) {
post.comments[0].remove();
post.save(function (err) {
// do something
});
}
});
```
...
replaceOne = function (conditions, doc, options, callback) { if (typeof options === 'function') { // .update(conditions, doc, callback) callback = options; options = null; } else if (typeof doc === 'function') { // .update(doc, callback); callback = doc; doc = conditions; conditions = {}; options = null; } else if (typeof conditions === 'function') { // .update(callback) callback = conditions; conditions = undefined; doc = undefined; options = undefined; } else if (typeof conditions === 'object' && !doc && !options && !callback) { // .update(doc) doc = conditions; conditions = undefined; options = undefined; callback = undefined; } this.setOptions({ overwrite: true }); return _update(this, 'replaceOne', conditions, doc, options, callback); }
...
}
Query.base.updateOne.call(this, castedQuery, castedDoc, options, callback);
return this;
};
/*!
* Internal thunk for .replaceOne()
*
* @param {Function} callback
* @see Model.replaceOne #model_Model.replaceOne
* @api private
*/
Query.prototype._replaceOne = function(callback) {
var schema = this.model.schema;
...
setOptions = function (options, overwrite) { // overwrite is only for internal use if (overwrite) { // ensure that _mongooseOptions & options are two different objects this._mongooseOptions = (options && utils.clone(options)) || {}; this.options = options || {}; if ('populate' in options) { this.populate(this._mongooseOptions); } return this; } if (!(options && options.constructor.name === 'Object')) { return this; } if (options && Array.isArray(options.populate)) { var populate = options.populate; delete options.populate; var _numPopulate = populate.length; for (var i = 0; i < _numPopulate; ++i) { this.populate(populate[i]); } } return Query.base.setOptions.call(this, options); }
...
} else if (typeof options === 'function') {
callback = options;
options = null;
}
var mq = new this.Query({}, {}, this, this.collection);
mq.select(projection);
mq.setOptions(options);
if (this.schema.discriminatorMapping && mq.selectedInclusively()) {
mq.select(this.schema.options.discriminatorKey);
}
if (callback) {
callback = this.$wrapCallback(callback);
}
...
sort = function (arg) { if (arguments.length > 1) { throw new Error('sort() only takes 1 Argument'); } return Query.base.sort.call(this, arg); }
...
* If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`.
*
* If a string is passed, it must be a space delimited list of path names. The sort order of each path is ascending unless the path
name is prefixed with `-` which will be treated as descending.
*
* ####Examples:
*
* // these are equivalent
* aggregate.sort({ field: 'asc', test: -1 });
* aggregate.sort('field -test');
*
* @see $sort http://docs.mongodb.org/manual/reference/aggregation/sort/
* @param {Object|String} arg
* @return {Aggregate} this
* @api public
*/
...
function deprecated() { warned = exports.printDeprecationMessage(msg, warned, deprecated); if (new.target) { return Reflect.construct(fn, arguments, new.target); } return fn.apply(this, arguments); }
...
/**
* Returns a Node.js 0.8 style [read stream](http://nodejs.org/docs/v0.8.21/api/stream.html#stream_readable_stream) interface.
*
* ####Example
*
* // follows the nodejs 0.8 stream api
* Thing.find({ name: /^hello/ }).stream().pipe(res)
*
* // manual streaming
* var stream = Thing.find({ name: /^hello/ }).stream();
*
* stream.on('data', function (doc) {
* // do something with the mongoose document
* }).on('error', function (err) {
...
tailable = function (val, opts) { // we need to support the tailable({ awaitdata : true }) as well as the // tailable(true, {awaitdata :true}) syntax that mquery does not support if (val && val.constructor.name === 'Object') { opts = val; val = true; } if (val === undefined) { val = true; } if (opts && typeof opts === 'object') { for (var key in opts) { if (key === 'awaitdata') { // For backwards compatibility this.options[key] = !!opts[key]; } else { this.options[key] = opts[key]; } } } return Query.base.tailable.call(this, val); }
...
Query.prototype.maxscan = Query.base.maxScan;
/**
* Sets the tailable option (for use with capped collections).
*
* ####Example
*
* query.tailable() // true
* query.tailable(true)
* query.tailable(false)
*
* ####Note
*
* Cannot be used with `distinct()`
*
...
then = function (resolve, reject) { return this.exec().then(resolve, reject); }
...
*
* ####Example:
*
* aggregate.exec(callback);
*
* // Because a promise is returned, the `callback` is optional.
* var promise = aggregate.exec();
* promise.then(..);
*
* @see Promise #promise_Promise
* @param {Function} [callback]
* @return {Promise}
* @api public
*/
...
function toConstructor() { var model = this.model; var coll = this.mongooseCollection; var CustomQuery = function(criteria, options) { if (!(this instanceof CustomQuery)) { return new CustomQuery(criteria, options); } this._mongooseOptions = utils.clone(p._mongooseOptions); Query.call(this, criteria, options || null, model, coll); }; util.inherits(CustomQuery, Query); // set inherited defaults var p = CustomQuery.prototype; p.options = {}; p.setOptions(this.options); p.op = this.op; p._conditions = utils.clone(this._conditions, { retainKeyOrder: true }); p._fields = utils.clone(this._fields); p._update = utils.clone(this._update); p._path = this._path; p._distinct = this._distinct; p._collection = this._collection; p._mongooseOptions = this._mongooseOptions; return CustomQuery; }
...
*
* // Create a query for adventure movies and read from the primary
* // node in the replica-set unless it is down, in which case we'll
* // read from a secondary node.
* var query = Movie.find({ tags: 'adventure' }).read('primaryPreferred');
*
* // create a custom Query constructor based off these settings
* var Adventure = query.toConstructor();
*
* // Adventure is now a subclass of mongoose.Query and works the same way but with the
* // default query parameters and options set.
* Adventure().exec(callback)
*
* // further narrow down our query results while still using the previous settings
* Adventure().where({ name: /^Life/ }).exec(callback);
...
update = function (conditions, doc, options, callback) { if (typeof options === 'function') { // .update(conditions, doc, callback) callback = options; options = null; } else if (typeof doc === 'function') { // .update(doc, callback); callback = doc; doc = conditions; conditions = {}; options = null; } else if (typeof conditions === 'function') { // .update(callback) callback = conditions; conditions = undefined; doc = undefined; options = undefined; } else if (typeof conditions === 'object' && !doc && !options && !callback) { // .update(doc) doc = conditions; conditions = undefined; options = undefined; callback = undefined; } return _update(this, 'update', conditions, doc, options, callback); }
...
}
/**
* Sends an update command with this document `_id` as the query selector.
*
* ####Example:
*
* weirdCar.update({$inc: {wheels:1}}, { w: 1 }, callback);
*
* ####Valid options:
*
* - same as in [Model.update](#model_Model.update)
*
* @see Model.update #model_Model.update
* @param {Object} doc
...
updateMany = function (conditions, doc, options, callback) { if (typeof options === 'function') { // .update(conditions, doc, callback) callback = options; options = null; } else if (typeof doc === 'function') { // .update(doc, callback); callback = doc; doc = conditions; conditions = {}; options = null; } else if (typeof conditions === 'function') { // .update(callback) callback = conditions; conditions = undefined; doc = undefined; options = undefined; } else if (typeof conditions === 'object' && !doc && !options && !callback) { // .update(doc) doc = conditions; conditions = undefined; options = undefined; callback = undefined; } return _update(this, 'updateMany', conditions, doc, options, callback); }
...
}
Query.base.update.call(this, castedQuery, castedDoc, options, callback);
return this;
};
/*!
* Internal thunk for .updateMany()
*
* @param {Function} callback
* @see Model.update #model_Model.update
* @api private
*/
Query.prototype._updateMany = function(callback) {
var schema = this.model.schema;
...
updateOne = function (conditions, doc, options, callback) { if (typeof options === 'function') { // .update(conditions, doc, callback) callback = options; options = null; } else if (typeof doc === 'function') { // .update(doc, callback); callback = doc; doc = conditions; conditions = {}; options = null; } else if (typeof conditions === 'function') { // .update(callback) callback = conditions; conditions = undefined; doc = undefined; options = undefined; } else if (typeof conditions === 'object' && !doc && !options && !callback) { // .update(doc) doc = conditions; conditions = undefined; options = undefined; callback = undefined; } return _update(this, 'updateOne', conditions, doc, options, callback); }
...
}
Query.base.updateMany.call(this, castedQuery, castedDoc, options, callback);
return this;
};
/*!
* Internal thunk for .updateOne()
*
* @param {Function} callback
* @see Model.update #model_Model.update
* @api private
*/
Query.prototype._updateOne = function(callback) {
var schema = this.model.schema;
...
function QueryCursor(query, options) { Readable.call(this, { objectMode: true }); this.cursor = null; this.query = query; this._transforms = options.transform ? [options.transform] : []; var _this = this; var model = query.model; model.hooks.execPre('find', query, function() { model.collection.find(query._conditions, options, function(err, cursor) { if (_this._error) { cursor.close(function() {}); _this.listeners('error').length > 0 && _this.emit('error', _this._error); } if (err) { return _this.emit('error', err); } _this.cursor = cursor; _this.emit('cursor', cursor); }); }); }
n/a
function Readable(options) { if (!(this instanceof Readable)) return new Readable(options); this._readableState = new ReadableState(options, this); // legacy this.readable = true; if (options && typeof options.read === 'function') this._read = options.read; Stream.call(this); }
n/a
_markError = function (error) { this._error = error; return this; }
...
if (opts) {
this.setOptions(opts);
}
try {
this.cast(this.model);
} catch (err) {
return (new QueryCursor(this, this.options))._markError(err);
}
return new QueryCursor(this, this.options);
};
// the rest of these are basically to support older Mongoose syntax with mquery
...
_read = function () { var _this = this; _next(this, function(error, doc) { if (error) { return _this.emit('error', error); } if (!doc) { _this.push(null); _this.cursor.close(function(error) { if (error) { return _this.emit('error', error); } setTimeout(function() { _this.emit('close'); }, 0); }); return; } _this.push(doc); }); }
n/a
close = function (callback) { var Promise = PromiseProvider.get(); var _this = this; return new Promise.ES6(function(resolve, reject) { _this.cursor.close(function(error) { if (error) { callback && callback(error); reject(error); return _this.listeners('error').length > 0 && _this.emit('error', error); } _this.emit('close'); resolve(); callback && callback(); }); }); }
...
* For practical reasons, a Connection equals a Db.
*
* @param {Mongoose} base a mongoose instance
* @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
* @event `connecting`: Emitted when `connection.{open,openSet}()` is executed on this connection.
* @event `connected`: Emitted when this connection successfully connects to the db. May be emitted _multiple_ times in `reconnected
` scenarios.
* @event `open`: Emitted after we `connected` and `onOpen` is executed on all of this connections models.
* @event `disconnecting`: Emitted when `connection.close()` was executed.
* @event `disconnected`: Emitted after getting disconnected from the db.
* @event `close`: Emitted after we `disconnected` and `onClose` executed on all of this connections models.
* @event `reconnected`: Emitted after we `connected` and subsequently `disconnected`, followed by successfully another successfull
connection.
* @event `error`: Emitted when an error occurs on this connection.
* @event `fullsetup`: Emitted in a replica-set scenario, when primary and at least one seconaries specified in the connection string
are connected.
* @event `all`: Emitted in a replica-set scenario, when all nodes specified in the connection string are connected.
* @api public
...
eachAsync = function (fn, callback) { var Promise = PromiseProvider.get(); var _this = this; var handleNextResult = function(doc, callback) { var promise = fn(doc); if (promise && typeof promise.then === 'function') { promise.then( function() { callback(null); }, function(error) { callback(error); }); } else { callback(null); } }; var iterate = function(callback) { return _next(_this, function(error, doc) { if (error) { return callback(error); } if (!doc) { return callback(null); } handleNextResult(doc, function(error) { if (error) { return callback(error); } // Make sure to clear the stack re: gh-4697 setTimeout(function() { iterate(callback); }, 0); }); }); }; return new Promise.ES6(function(resolve, reject) { iterate(function(error) { if (error) { callback && callback(error); return reject(error); } callback && callback(null); return resolve(); }); }); }
n/a
map = function (fn) { this._transforms.push(fn); return this; }
...
}
throw new Error('no match found');
}
function getUnstable(ver) {
ver = ver.replace('-pre');
var spl = ver.split('.');
spl = spl.map(function(i) {
return parseInt(i, 10);
});
spl[1]++;
spl[2] = 'x';
return spl.join('.');
}
...
next = function (callback) { var Promise = PromiseProvider.get(); var _this = this; return new Promise.ES6(function(resolve, reject) { _next(_this, function(error, doc) { if (error) { callback && callback(error); return reject(error); } callback && callback(null, doc); resolve(doc); }); }); }
...
function(error) { callback(error); });
} else {
callback(null);
}
};
var iterate = function(callback) {
return cursor.next(function(error, doc) {
if (error) {
return callback(error);
}
if (!doc) {
return callback(null);
}
handleNextResult(doc, function(error) {
...
function applyPaths(fields, schema) { // determine if query is selecting or excluding fields var exclude; var keys; var ki; if (fields) { keys = Object.keys(fields); ki = keys.length; while (ki--) { if (keys[ki][0] === '+') continue; exclude = fields[keys[ki]] === 0; break; } } // if selecting, apply default schematype select:true fields // if excluding, apply schematype select:false fields var selected = [], excluded = [], seen = []; var analyzePath = function(path, type) { if (typeof type.selected !== 'boolean') return; var plusPath = '+' + path; if (fields && plusPath in fields) { // forced inclusion delete fields[plusPath]; // if there are other fields being included, add this one // if no other included fields, leave this out (implied inclusion) if (exclude === false && keys.length > 1 && !~keys.indexOf(path)) { fields[path] = 1; } return; } // check for parent exclusions var root = path.split('.')[0]; if (~excluded.indexOf(root)) return; (type.selected ? selected : excluded).push(path); }; var analyzeSchema = function(schema, prefix) { prefix || (prefix = ''); // avoid recursion if (~seen.indexOf(schema)) return; seen.push(schema); schema.eachPath(function(path, type) { if (prefix) path = prefix + '.' + path; analyzePath(path, type); // array of subdocs? if (type.schema) { analyzeSchema(type.schema, path); } }); }; analyzeSchema(schema); var i; switch (exclude) { case true: for (i = 0; i < excluded.length; ++i) { fields[excluded[i]] = 0; } break; case false: if (schema && schema.paths['_id'] && schema.paths['_id'].options && schema.paths['_id'].options.select === false) { fields._id = 0; } for (i = 0; i < selected.length; ++i) { fields[selected[i]] = 1; } break; case undefined: // user didn't specify fields, implies returning all fields. // only need to apply excluded fields for (i = 0; i < excluded.length; ++i) { fields[excluded[i]] = 0; } break; } }
...
var key = discriminatorMapping && discriminatorMapping.isRoot
? discriminatorMapping.key
: null;
if (key && doc[key] && model.discriminators && model.discriminators[doc[key]]) {
var discriminator = model.discriminators[doc[key]];
var _fields = utils.clone(fields);
exports.applyPaths(_fields, discriminator.schema);
return new model.discriminators[doc[key]](undefined, _fields, true);
}
return new model(undefined, fields, true);
};
/*!
...
function createModel(model, doc, fields) { var discriminatorMapping = model.schema ? model.schema.discriminatorMapping : null; var key = discriminatorMapping && discriminatorMapping.isRoot ? discriminatorMapping.key : null; if (key && doc[key] && model.discriminators && model.discriminators[doc[key]]) { var discriminator = model.discriminators[doc[key]]; var _fields = utils.clone(fields); exports.applyPaths(_fields, discriminator.schema); return new model.discriminators[doc[key]](undefined, _fields, true); } return new model(undefined, fields, true); }
...
*
* @param {Object} obj
* @return {Document}
* @api public
*/
Model.hydrate = function(obj) {
var model = require('./queryhelpers').createModel(this, obj);
model.init(obj);
return model;
};
/**
* Updates one document in the database without returning it.
*
...
function preparePopulationOptions(query, options) { var pop = utils.object.vals(query.options.populate); // lean options should trickle through all queries if (options.lean) pop.forEach(makeLean); return pop; }
n/a
function preparePopulationOptionsMQ(query, options) { var pop = utils.object.vals(query._mongooseOptions.populate); // lean options should trickle through all queries if (options.lean) pop.forEach(makeLean); return pop; }
...
var opts = ctx.query._mongooseOptions;
if (!opts.populate) {
return opts.lean === true ?
callback(null, doc) :
_create(ctx, doc, null, callback);
}
var pop = helpers.preparePopulationOptionsMQ(ctx.query,
ctx.query._mongooseOptions);
pop.forEach(function(option) {
delete option.model;
});
pop.__noPromise = true;
ctx.query.model.populate(doc, pop, function(err, doc) {
if (err) {
...
function QueryStream(query, options) { Stream.call(this); this.query = query; this.readable = true; this.paused = false; this._cursor = null; this._destroyed = null; this._fields = null; this._buffer = null; this._inline = T_INIT; this._running = false; this._transform = options && typeof options.transform === 'function' ? options.transform : K; // give time to hook up events var _this = this; process.nextTick(function() { _this._init(); }); }
n/a
__next = function () { if (this.paused || this._destroyed) { this._running = false; return this._running; } var _this = this; _this._inline = T_INIT; _this._cursor.nextObject(function cursorcb(err, doc) { _this._onNextObject(err, doc); }); // if onNextObject() was already called in this tick // return ourselves to the trampoline. if (T_CONT === this._inline) { return true; } // onNextObject() hasn't fired yet. tell onNextObject // that its ok to call _next b/c we are not within // the trampoline anymore. this._inline = T_IDLE; }
...
while (!this.paused && !this._destroyed && (arg = this._buffer.shift())) { // eslint-disable-line no-cond
-assign
this._onNextObject.apply(this, arg);
}
}
// avoid stack overflows with large result sets.
// trampoline instead of recursion.
while (this.__next()) {
}
};
/**
* Pulls the next doc from the cursor.
*
* @see QueryStream#_next #querystream_QueryStream-_next
...
_init = function () { if (this._destroyed) { return; } var query = this.query, model = query.model, options = query._optionsForExec(model), _this = this; try { query.cast(model); } catch (err) { return _this.destroy(err); } _this._fields = utils.clone(query._fields); options.fields = query._castFields(_this._fields); model.collection.find(query._conditions, options, function(err, cursor) { if (err) { return _this.destroy(err); } _this._cursor = cursor; _this._next(); }); }
...
this._transform = options && typeof options.transform === 'function'
? options.transform
: K;
// give time to hook up events
var _this = this;
process.nextTick(function() {
_this._init();
});
}
/*!
* Inherit from Stream
*/
...
function _next() { if (this.paused || this._destroyed) { this._running = false; return this._running; } this._running = true; if (this._buffer && this._buffer.length) { var arg; while (!this.paused && !this._destroyed && (arg = this._buffer.shift())) { // eslint-disable-line no-cond-assign this._onNextObject.apply(this, arg); } } // avoid stack overflows with large result sets. // trampoline instead of recursion. while (this.__next()) { } }
...
options.fields = query._castFields(_this._fields);
model.collection.find(query._conditions, options, function(err, cursor) {
if (err) {
return _this.destroy(err);
}
_this._cursor = cursor;
_this._next();
});
};
/**
* Trampoline for pulling the next doc from cursor.
*
* @see QueryStream#__next #querystream_QueryStream-__next
...
function _onNextObject(err, doc) { if (this._destroyed) { return; } if (this.paused) { this._buffer || (this._buffer = []); this._buffer.push([err, doc]); this._running = false; return this._running; } if (err) { return this.destroy(err); } // when doc is null we hit the end of the cursor if (!doc) { this.emit('end'); return this.destroy(); } var opts = this.query._mongooseOptions; if (!opts.populate) { return opts.lean === true ? emit(this, doc) : createAndEmit(this, null, doc); } var _this = this; var pop = helpers.preparePopulationOptionsMQ(_this.query, _this.query._mongooseOptions); // Hack to work around gh-3108 pop.forEach(function(option) { delete option.model; }); pop.__noPromise = true; _this.query.model.populate(doc, pop, function(err, doc) { if (err) { return _this.destroy(err); } return opts.lean === true ? emit(_this, doc) : createAndEmit(_this, pop, doc); }); }
...
return this._running;
}
var _this = this;
_this._inline = T_INIT;
_this._cursor.nextObject(function cursorcb(err, doc) {
_this._onNextObject(err, doc);
});
// if onNextObject() was already called in this tick
// return ourselves to the trampoline.
if (T_CONT === this._inline) {
return true;
}
...
destroy = function (err) { if (this._destroyed) { return; } this._destroyed = true; this._running = false; this.readable = false; if (this._cursor) { this._cursor.close(); } if (err) { this.emit('error', err); } this.emit('close'); }
...
var static = require('node-static');
var server = new static.Server('.', {cache: 0});
require('http').createServer(function(req, res) {
if (req.url === '/favicon.ico') {
req.destroy();
res.statusCode = 204;
return res.end();
}
req.on('end', function() {
server.serve(req, res, function(err) {
if (err) {
...
pause = function () { this.paused = true; }
n/a
resume = function () { this.paused = false; if (!this._cursor) { // cannot start if not initialized return; } // are we within the trampoline? if (T_INIT === this._inline) { return; } if (!this._running) { // outside QueryStream control, need manual restart return this._next(); } }
...
if (err) {
console.error(err, req.url);
res.writeHead(err.status, err.headers);
res.end();
}
});
});
req.resume();
}).listen(8088);
console.error('now listening on http://localhost:8088');
...
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.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); } }
n/a
function ObjectId(key, options) { SchemaType.call(this, key, options, 'ObjectID'); }
...
* .unwind('tags')
* .exec(callback)
*
* ####Note:
*
* - The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned).
* - Requires MongoDB >= 2.1
* - Mongoose does **not** cast pipeline stages. `new Aggregate({ $match: { _id: '00000000000000000000000a' } });` will
not work unless `_id` is a string in the database. Use `new Aggregate({ $match: { _id: mongoose.Types.ObjectId('00000000000000000000000a') } });` instead.
*
* @see MongoDB http://docs.mongodb.org/manual/applications/aggregation/
* @see driver http://mongodb.github.com/node-mongodb-native/api-generated/collection.html#aggregate
* @param {Object|Array} [ops] aggregation operator(s) or operator array
* @api public
*/
...
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; } //propagate 'strict' option to child schema if (options.hasOwnProperty('strict')) { childSchemaOptions.strict = options.strict; } 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); }
...
var strict = options && 'strict' in options
? options.strict
: this.$__.strictMode;
if (adhoc) {
adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {});
adhocs[path] = Schema.interpretAsType(path, type, this.schema.options);
}
if (typeof path !== 'string') {
// new Document({ key: val })
if (path === null || path === void 0) {
var _ = path;
...
_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); }
n/a
_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) { var ret; if (parts[p] === '$') { if (p + 1 === parts.length) { // comments.$ return foundschema; } // comments.$.comments.$.title ret = search(parts.slice(p + 1), foundschema.schema); if (ret) { ret.$isUnderneathDocArray = ret.$isUnderneathDocArray || !foundschema.schema.$isSingleNested; } return ret; } // this is the last path of the selector ret = search(parts.slice(p), foundschema.schema); if (ret) { ret.$isUnderneathDocArray = ret.$isUnderneathDocArray || !foundschema.schema.$isSingleNested; } return ret; } } foundschema.$fullPath = resultPath.join('.'); return foundschema; } } } // look for arrays return search(path.split('.'), _this); }
...
modelNameFromQuery = options.model && options.model.modelName || options.model,
schema, refPath, Model, currentOptions, modelNames, modelName, discriminatorKey, modelForFindSchema;
var originalModel = options.model;
var isVirtual = false;
var isRefPathArray = false;
schema = model._getSchema(options.path);
var isUnderneathDocArray = schema && schema.$isUnderneathDocArray;
if (isUnderneathDocArray &&
options &&
options.options &&
options.options.sort) {
return new Error('Cannot populate with `sort` on path ' + options.path +
' because it is a subproperty of a document array');
...
_getVirtual = function (name) { return _getVirtual(this, name); }
...
function setValue(val) {
return valueFilter(val, options);
}
for (var i = 0; i < docs.length; ++i) {
if (utils.getValue(o.path, docs[i]) == null &&
!o.originalModel.schema._getVirtual(o.path)) {
continue;
}
if (o.isVirtual && !o.justOne && !Array.isArray(rawIds[i])) {
rawIds[i] = [rawIds[i]];
}
utils.setValue(o.path, rawIds[i], docs[i], setValue);
...
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]); } } }
...
* Adds a discriminator type.
*
* ####Example:
*
* function BaseSchema() {
* Schema.apply(this, arguments);
*
* this.add({
* name: String,
* createdAt: Date
* });
* }
* util.inherits(BaseSchema, Schema);
*
* var PersonSchema = new BaseSchema();
...
clone = function () { var s = new Schema(this.obj, this.options); // Clone the call queue s.callQueue = this.callQueue.map(function(f) { return f; }); s.methods = utils.clone(this.methods); s.statics = utils.clone(this.statics); s.s.hooks = this.s.hooks.clone(); return s; }
...
Aggregate.prototype.exec = function(callback) {
if (!this._model) {
throw new Error('Aggregate not bound to any Model');
}
var _this = this;
var Promise = PromiseProvider.get();
var options = utils.clone(this.options);
if (options && options.cursor) {
if (options.cursor.async) {
delete options.cursor.async;
return new Promise.ES6(function(resolve) {
if (!_this._model.collection.buffer) {
process.nextTick(function() {
...
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.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); } }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
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; }
...
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)
...
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; }
...
var analyzeSchema = function(schema, prefix) {
prefix || (prefix = '');
// avoid recursion
if (~seen.indexOf(schema)) return;
seen.push(schema);
schema.eachPath(function(path, type) {
if (prefix) path = prefix + '.' + path;
analyzePath(path, type);
// array of subdocs?
if (type.schema) {
analyzeSchema(type.schema, path);
...
get = function (key) { return this.options[key]; }
...
// a setter
Comment.path('name').set(function (v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function (next) {
notify(this.get('email'));
next();
});
```
Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
### Accessing a Model
...
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; }
n/a
index = function (fields, options) { options || (options = {}); if (options.expires) { utils.expires(options); } this._indexes.push([fields, options]); 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
*/
...
function indexedPaths() { if (this._indexedpaths) { return this._indexedpaths; } this._indexedpaths = this.indexes(); return this._indexedpaths; }
...
* @param {Boolean} init whether this is an initialization cast
* @api private
*/
SchemaArray.prototype.cast = function(value, doc, init) {
if (Array.isArray(value)) {
if (!value.length && doc) {
var indexes = doc.schema.indexedPaths();
for (var i = 0, l = indexes.length; i < l; ++i) {
var pathIndex = indexes[i][0][this.path];
if (pathIndex === '2dsphere' || pathIndex === '2d') {
return;
}
}
...
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]]);
}
}
}
...
callback && callback();
resolve();
});
});
};
function _ensureIndexes(model, options, callback) {
var indexes = model.schema.indexes();
if (!indexes.length) {
setImmediate(function() {
callback && callback();
});
return;
}
// Indexes are created one-by-one to support how MongoDB < 2.4 deals
...
loadClass = function (model, virtualsOnly) { if (model === Object.prototype || model === Function.prototype || model.prototype.hasOwnProperty('$isMongooseModelPrototype')) { 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))); }
...
schema.add(o);
}
var model;
if (typeof name === 'function' && name.prototype instanceof Model) {
model = name;
name = model.name;
schema.loadClass(model, true);
model.prototype.$isMongooseModelPrototype = true;
} else {
// generate new class
model = function model(doc, fields, skipId) {
if (!(this instanceof model)) {
return new model(doc, fields, skipId);
}
...
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 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
...
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; }
...
age: { type: Number, min: 18, index: true },
bio: { type: String, match: /[a-z]/ },
date: { type: Date, default: Date.now },
buff: Buffer
});
// a setter
Comment.path('name').set(function (v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function (next) {
notify(this.get('email'));
next();
...
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'; }
...
return this;
}
}
function _handleIndex(i) {
key = keys[i];
var pathName = prefix + key;
pathtype = this.schema.pathType(pathName);
if (path[key] !== null
&& path[key] !== void 0
// need to know if plain object - no Buffer, ObjectId, ref, etc
&& utils.isObject(path[key])
&& (!path[key].constructor || utils.getFunctionName(path[key].constructor) === 'Object')
&& pathtype !== 'virtual'
...
plugin = function (fn, opts) { fn(this, opts); return this; }
n/a
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)); }); }]); }
...
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');
* });
*
...
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); }
...
// a setter
Comment.path('name').set(function (v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function (next) {
notify(this.get('email'));
next();
});
```
Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
...
queue = function (name, args) { this.callQueue.push([name, args]); return this; }
...
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) {
...
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); } }
...
```
The same goes for removing them:
```js
BlogPost.findById(myId, function (err, post) {
if (!err) {
post.comments[0].remove();
post.save(function (err) {
// do something
});
}
});
```
...
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; }
...
this.$__.strictMode = fields;
fields = undefined;
} else {
this.$__.strictMode = this.schema.options && this.schema.options.strict;
this.$__.selected = fields;
}
var required = this.schema.requiredPaths();
for (var i = 0; i < required.length; ++i) {
this.$__.activePaths.require(required[i]);
}
this.$__.emitter.setMaxListeners(0);
this._doc = this.$__buildDoc(obj, fields, skipId);
...
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; }
...
age: { type: Number, min: 18, index: true },
bio: { type: String, match: /[a-z]/ },
date: { type: Date, default: Date.now },
buff: Buffer
});
// a setter
Comment.path('name').set(function (v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function (next) {
notify(this.get('email'));
next();
...
setupTimestamp = function (timestamps) { if (timestamps) { var createdAt = timestamps.createdAt || 'createdAt'; var updatedAt = timestamps.updatedAt || 'updatedAt'; var schemaAdditions = {}; var parts = createdAt.split('.'); var i; var cur = schemaAdditions; for (i = 0; i < parts.length; ++i) { cur[parts[i]] = (i < parts.length - 1 ? cur[parts[i]] || {} : Date); } parts = updatedAt.split('.'); cur = schemaAdditions; for (i = 0; i < parts.length; ++i) { cur[parts[i]] = (i < parts.length - 1 ? cur[parts[i]] || {} : Date); } this.add(schemaAdditions); this.pre('save', function(next) { var defaultTimestamp = new Date(); var auto_id = this._id && this._id.auto; if (!this.get(createdAt) && this.isSelected(createdAt)) { this.set(createdAt, auto_id ? this._id.getTimestamp() : defaultTimestamp); } if (this.isNew || this.isModified()) { this.set(updatedAt, this.isNew ? this.get(createdAt) : defaultTimestamp); } next(); }); var genUpdates = function(currentUpdate, overwrite) { var now = new Date(); var updates = {}; if (overwrite) { if (!currentUpdate[updatedAt]) { updates[updatedAt] = now; } if (!currentUpdate[createdAt]) { updates[createdAt] = now; } return updates; } updates = { $set: {} }; currentUpdate = currentUpdate || {}; updates.$set[updatedAt] = now; if (currentUpdate[createdAt]) { delete currentUpdate[createdAt]; } if (currentUpdate.$set && currentUpdate.$set[createdAt]) { delete currentUpdate.$set[createdAt]; } updates.$setOnInsert = {}; updates.$setOnInsert[createdAt] = now; return updates; }; this.methods.initializeTimestamps = function() { if (!this.get(createdAt)) { this.set(createdAt, new Date()); } if (!this.get(updatedAt)) { this.set(updatedAt, new Date()); } return this; }; this.pre('findOneAndUpdate', function(next) { var overwrite = this.options.overwrite; this.findOneAndUpdate({}, genUpdates(this.getUpdate(), overwrite), { overwrite: overwrite }); applyTimestampsToChildren(this); next(); }); this.pre('update', function(next) { var overwrite = this.options.overwrite; this.update({}, genUpdates(this.getUpdate(), overwrite), { overwrite: overwrite }); applyTimestampsToChildren(this); next(); }); } }
...
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.
*/
...
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; }
...
/**
* 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) {
* //
* });
...
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 this; } 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]; }
...
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);
}
...
virtualpath = function (name) { return this.virtuals[name]; }
...
if (!mixed) {
if (strict === 'throw') {
throw new StrictModeError(path);
}
return this;
}
} else if (pathType === 'virtual') {
schema = this.schema.virtualpath(path);
schema.applySetters(val, this);
return this;
} else {
schema = this.$__path(path);
}
var pathToMark;
...
function SchemaType(path, options, instance) { this.path = path; this.instance = instance; this.validators = []; this.setters = []; this.getters = []; this.options = options; this._index = null; this.selected; for (var i in options) { if (this[i] && typeof this[i] === 'function') { // { unique: true, index: true } if (i === 'index' && this._index) { continue; } var opts = Array.isArray(options[i]) ? options[i] : [options[i]]; this[i].apply(this, opts); } } }
n/a
function CastError(type, value, path, reason) { var stringValue = util.inspect(value); stringValue = stringValue.replace(/^'/, '"').replace(/'$/, '"'); if (stringValue.charAt(0) !== '"') { stringValue = '"' + stringValue + '"'; } MongooseError.call(this, 'Cast to ' + type + ' failed for value ' + stringValue + ' at path "' + path + '"'); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.stringValue = stringValue; this.name = 'CastError'; this.kind = type; this.value = value; this.path = path; this.reason = reason; }
...
this.markModified(path);
cleanModifiedSubpaths(this, path);
} else {
this.set(val, path, constructing);
}
return this;
}
this.invalidate(path, new MongooseError.CastError('Object', val, path));
return this;
}
var schema;
var parts = path.split('.');
if (pathType === 'adhocOrUndefined' && strict) {
...
function ValidatorError(properties) { var msg = properties.message; if (!msg) { msg = MongooseError.messages.general.default; } var message = this.formatMessage(msg, properties); MongooseError.call(this, message); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.properties = properties; this.name = 'ValidatorError'; this.kind = properties.type; this.path = properties.path; this.value = properties.value; this.reason = properties.reason; }
n/a
_isRef = function (self, value, doc, init) { // fast path var ref = init && self.options && self.options.ref; if (!ref && doc && doc.$__fullPath) { // checks for // - this populated with adhoc model and no ref was set in schema OR // - setting / pushing values after population var path = doc.$__fullPath(self.path); var owner = doc.ownerDocument ? doc.ownerDocument() : doc; ref = owner.populated(path); } if (ref) { if (value == null) { return true; } if (!Buffer.isBuffer(value) && // buffers are objects too value._bsontype !== 'Binary' // raw binary value from the db && utils.isObject(value) // might have deselected _id in population query ) { return true; } } return false; }
...
* @param {Any} value
* @param {Document} doc
* @return {Boolean}
* @api public
*/
SchemaBuffer.prototype.checkRequired = function(value, doc) {
if (SchemaType._isRef(this, value, doc, true)) {
return !!value;
}
return !!(value && value.length);
};
/**
* Casts contents
...
applyGetters = function (value, scope) { var v = value, getters = this.getters, len = getters.length; if (!len) { return v; } while (len--) { v = getters[len].call(scope, v, this); } return v; }
...
if (adhoc) {
obj = adhoc.cast(obj);
}
// Check if this path is populated - don't apply getters if it is,
// because otherwise its a nested object. See gh-3357
if (schema && !this.populated(path)) {
obj = schema.applyGetters(obj, this);
}
return obj;
};
/**
* Returns the schematype for the given `path`.
...
applySetters = function (value, scope, init, priorVal, options) { var v = value, setters = this.setters, len = setters.length, caster = this.caster; while (len--) { v = setters[len].call(scope, v, this); } if (Array.isArray(v) && caster && caster.setters) { var newVal = []; for (var i = 0; i < v.length; i++) { newVal.push(caster.applySetters(v[i], scope, init, priorVal)); } v = newVal; } if (v === null || v === undefined) { return v; } // do not cast until all setters are applied #665 v = this.cast(v, scope, init, priorVal, options); return v; }
...
if (strict === 'throw') {
throw new StrictModeError(path);
}
return this;
}
} else if (pathType === 'virtual') {
schema = this.schema.virtualpath(path);
schema.applySetters(val, this);
return this;
} else {
schema = this.$__path(path);
}
var pathToMark;
...
castForQuery = function ($conditional, val) { var handler; if (arguments.length === 2) { handler = this.$conditionalHandlers[$conditional]; if (!handler) { throw new Error('Can\'t use ' + $conditional); } return handler.call(this, val); } val = $conditional; return this.cast(val); }
...
}
if (geo) {
var numbertype = new Types.Number('__QueryCasting__');
var value = val[geo];
if (val.$maxDistance != null) {
val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
}
if (val.$minDistance != null) {
val.$minDistance = numbertype.castForQuery(val.$minDistance);
}
if (geo === '$within') {
var withinType = value.$center
...
checkRequired = function (val) { return val != null; }
...
// in here, `this` refers to the validating document.
// no validation when this path wasn't selected in the query.
if ('isSelected' in this && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
return true;
}
return ((typeof required === 'function') && !required.apply(this)) ||
_this.checkRequired(v, this);
};
this.originalRequiredValue = required;
if (typeof required === 'string') {
message = required;
required = undefined;
}
...
default = function (val) { if (arguments.length === 1) { if (val === void 0) { this.defaultValue = void 0; return void 0; } this.defaultValue = val; return this.defaultValue; } else if (arguments.length > 1) { this.defaultValue = utils.args(arguments); } return this.defaultValue; }
...
if (p in fields) {
continue;
}
def = type.getDefault(_this, false);
if (typeof def !== 'undefined') {
doc_[piece] = def;
_this.$__.activePaths.default(p);
}
} else if (included) {
// selected field
def = type.getDefault(_this, false);
if (typeof def !== 'undefined') {
doc_[piece] = def;
_this.$__.activePaths.default(p);
...
doValidate = function (value, fn, scope) { var err = false; var path = this.path; var count = this.validators.length; if (!count) { return fn(null); } var validate = function(ok, validatorProperties) { if (err) { return; } if (ok === undefined || ok) { --count || fn(null); } else { err = new ValidatorError(validatorProperties); fn(err); } }; var _this = this; this.validators.forEach(function(v) { if (err) { return; } var validator = v.validator; var ok; var validatorProperties = utils.clone(v); validatorProperties.path = path; validatorProperties.value = value; if (validator instanceof RegExp) { validate(validator.test(value), validatorProperties); } else if (typeof validator === 'function') { if (value === undefined && !_this.isRequired) { validate(true, validatorProperties); return; } if (validatorProperties.isAsync) { asyncValidate(validator, scope, value, validatorProperties, validate); } else if (validator.length === 2 && !('isAsync' in validatorProperties)) { legacyAsyncValidate(validator, scope, value, validatorProperties, validate); } else { try { ok = validator.call(scope, value); } catch (error) { ok = false; validatorProperties.reason = error; } if (ok && typeof ok.then === 'function') { ok.then( function(ok) { validate(ok, validatorProperties); }, function(error) { validatorProperties.reason = error; ok = false; validate(ok, validatorProperties); }); } else { validate(ok, validatorProperties); } } } }); }
...
// If user marked as invalid or there was a cast error, don't validate
if (!_this.$isValid(path)) {
--total || complete();
return;
}
var val = _this.getValue(path);
p.doValidate(val, function(err) {
if (err) {
_this.invalidate(path, err, undefined, true);
}
--total || complete();
}, _this);
});
};
...
doValidateSync = function (value, scope) { var err = null, path = this.path, count = this.validators.length; if (!count) { return null; } var validate = function(ok, validatorProperties) { if (err) { return; } if (ok !== undefined && !ok) { err = new ValidatorError(validatorProperties); } }; var validators = this.validators; if (value === void 0) { if (this.validators.length > 0 && this.validators[0].type === 'required') { validators = [this.validators[0]]; } else { return null; } } validators.forEach(function(v) { if (err) { return; } var validator = v.validator; var validatorProperties = utils.clone(v); validatorProperties.path = path; validatorProperties.value = value; var ok; if (validator instanceof RegExp) { validate(validator.test(value), validatorProperties); } else if (typeof validator === 'function') { // if not async validators if (validator.length !== 2 && !validatorProperties.isAsync) { try { ok = validator.call(scope, value); } catch (error) { ok = false; validatorProperties.reason = error; } validate(ok, validatorProperties); } } }); return err; }
...
return;
}
if (!_this.$isValid(path)) {
return;
}
var val = _this.getValue(path);
var err = p.doValidateSync(val, _this);
if (err) {
_this.invalidate(path, err, undefined, true);
}
});
var err = _this.$__.validationError;
_this.$__.validationError = undefined;
...
get = function (fn) { if (typeof fn !== 'function') { throw new TypeError('A getter must be a function.'); } this.getters.push(fn); return this; }
...
// a setter
Comment.path('name').set(function (v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function (next) {
notify(this.get('email'));
next();
});
```
Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
### Accessing a Model
...
getDefault = function (scope, init) { var ret = typeof this.defaultValue === 'function' ? this.defaultValue.call(scope) : this.defaultValue; if (ret !== null && ret !== undefined) { var casted = this.cast(ret, scope, init); if (casted && casted.$isSingleNested) { casted.$parent = scope; } return casted; } return ret; }
...
if (fields && exclude !== null) {
if (exclude === true) {
// apply defaults to all non-excluded fields
if (p in fields) {
continue;
}
def = type.getDefault(_this, false);
if (typeof def !== 'undefined') {
doc_[piece] = def;
_this.$__.activePaths.default(p);
}
} else if (included) {
// selected field
def = type.getDefault(_this, false);
...
index = function (options) { this._index = options; utils.expires(this._index); 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
*/
...
required = function (required, message) { if (required === false) { this.validators = this.validators.filter(function(v) { return v.validator !== this.requiredValidator; }, this); this.isRequired = false; return this; } var _this = this; this.isRequired = true; this.requiredValidator = function(v) { // in here, `this` refers to the validating document. // no validation when this path wasn't selected in the query. if ('isSelected' in this && !this.isSelected(_this.path) && !this.isModified(_this.path)) { return true; } return ((typeof required === 'function') && !required.apply(this)) || _this.checkRequired(v, this); }; this.originalRequiredValue = required; if (typeof required === 'string') { message = required; required = undefined; } var msg = message || MongooseError.messages.general.required; this.validators.unshift({ validator: this.requiredValidator, message: msg, type: 'required' }); return this; }
...
* 'username is required if id is specified'
* ]
* }
* })
*
* // or through the path API
*
* Schema.path('name').required(true);
*
* // with custom error messaging
*
* Schema.path('name').required(true, 'grrr :( ');
*
* // or make a path conditionally required based on a function
* var isOver18 = function() { return this.age >= 18; };
...
function select(val) { this.selected = !!val; return this; }
...
};
/**
* Checks if `path` was selected in the source query which initialized this document.
*
* ####Example
*
* Thing.findOne().select('name').exec(function (err, doc) {
* doc.isSelected('name') // true
* doc.isSelected('age') // false
* })
*
* @param {String} path
* @return {Boolean}
* @api public
...
set = function (fn) { if (typeof fn !== 'function') { throw new TypeError('A setter must be a function.'); } this.setters.push(fn); return this; }
...
age: { type: Number, min: 18, index: true },
bio: { type: String, match: /[a-z]/ },
date: { type: Date, default: Date.now },
buff: Buffer
});
// a setter
Comment.path('name').set(function (v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function (next) {
notify(this.get('email'));
next();
...
sparse = function (bool) { if (this._index === null || this._index === undefined || typeof this._index === 'boolean') { this._index = {}; } else if (typeof this._index === 'string') { this._index = {type: this._index}; } this._index.sparse = bool; return this; }
n/a
text = function (bool) { if (this._index === null || this._index === undefined || typeof this._index === 'boolean') { this._index = {}; } else if (typeof this._index === 'string') { this._index = {type: this._index}; } this._index.text = bool; return this; }
n/a
unique = function (bool) { if (this._index === false) { if (!bool) { return; } throw new Error('Path "' + this.path + '" may not have `index` set to ' + 'false and `unique` set to true'); } if (this._index == null || this._index === true) { this._index = {}; } else if (typeof this._index === 'string') { this._index = {type: this._index}; } this._index.unique = bool; return this; }
...
if (mod.options.match) {
match = utils.object.shallowCopy(mod.options.match);
} else {
match = {};
}
var ids = utils.array.flatten(mod.ids, flatten);
ids = utils.array.unique(ids);
if (ids.length === 0 || ids.every(utils.isNullOrUndefined)) {
--_remaining;
continue;
}
hasOne = true;
...
validate = function (obj, message, type) { if (typeof obj === 'function' || obj && utils.getFunctionName(obj.constructor) === 'RegExp') { var properties; if (message instanceof Object && !type) { properties = utils.clone(message); if (!properties.message) { properties.message = properties.msg; } properties.validator = obj; properties.type = properties.type || 'user defined'; } else { if (!message) { message = MongooseError.messages.general.default; } if (!type) { type = 'user defined'; } properties = {message: message, type: type, validator: obj}; } this.validators.push(properties); return this; } var i, length, arg; for (i = 0, length = arguments.length; i < length; i++) { arg = arguments[i]; if (!(arg && utils.getFunctionName(arg.constructor) === 'Object')) { var msg = 'Invalid validator. Received (' + typeof arg + ') ' + arg + '. See http://mongoosejs.com/docs/api.html#schematype_SchemaType-validate'; throw new Error(msg); } this.validate(arg.validator, arg); } return this; }
...
*
* ####Note:
*
* This method is called `pre` save and if a validation rule is violated, [save](#model_Model-save) is aborted and the error is returned
to your `callback`.
*
* ####Example:
*
* doc.validate(function (err) {
* if (err) handleError(err);
* else // validation passed
* });
*
* @param {Object} optional options internal options
* @param {Function} callback optional callback called after validation completes, passing an error if one occurred
* @return {Promise} Promise
...
function StateMachine() { }
n/a
ctor = function () { var states = utils.args(arguments); var ctor = function() { StateMachine.apply(this, arguments); this.paths = {}; this.states = {}; this.stateNames = states; var i = states.length, state; while (i--) { state = states[i]; this.states[state] = {}; } }; ctor.prototype = new StateMachine(); states.forEach(function(state) { // Changes the `path`'s state to `state`. ctor.prototype[state] = function(path) { this._changeState(path, state); }; }); return ctor; }
...
/*!
* Dependencies
*/
var StateMachine = require('./statemachine');
var ActiveRoster = StateMachine.ctor('require', 'modify', 'init
', 'default', 'ignore');
module.exports = exports = InternalCache;
function InternalCache() {
this.strictMode = undefined;
this.selected = undefined;
this.shardval = undefined;
...
function _changeState(path, nextState) { var prevBucket = this.states[this.paths[path]]; if (prevBucket) delete prevBucket[path]; this.paths[path] = nextState; this.states[nextState][path] = true; }
...
};
ctor.prototype = new StateMachine();
states.forEach(function(state) {
// Changes the `path`'s state to `state`.
ctor.prototype[state] = function(path) {
this._changeState(path, state);
};
});
return ctor;
};
/*!
...
function _iter(iterMethod) { return function() { var numArgs = arguments.length, states = utils.args(arguments, 0, numArgs - 1), callback = arguments[numArgs - 1]; if (!states.length) states = this.stateNames; var _this = this; var paths = states.reduce(function(paths, state) { return paths.concat(Object.keys(_this.states[state])); }, []); return paths[iterMethod](function(path, i, paths) { return callback(path, i, paths); }); }; }
...
* @param {String} [state]
* @param {String} [state]
* @param {Function} callback
* @private
*/
StateMachine.prototype.forEach = function forEach() {
this.forEach = this._iter('forEach');
return this.forEach.apply(this, arguments);
};
/*!
* Maps over the paths that belong to one of the parameter states.
*
* The function profile can look like:
...
function clear(state) { var keys = Object.keys(this.states[state]), i = keys.length, path; while (i--) { path = keys[i]; delete this.states[state][path]; delete this.paths[path]; } }
...
var type = dirt.value;
if (type && type._atomics) {
type._atomics = {};
}
});
// Clear 'dirty' cache
this.$__.activePaths.clear('modify');
this.$__.activePaths.clear('default');
this.$__.validationError = undefined;
this.errors = undefined;
_this = this;
this.schema.requiredPaths().forEach(function(path) {
_this.$__.activePaths.require(path);
});
...
function forEach() { this.forEach = this._iter('forEach'); return this.forEach.apply(this, arguments); }
...
} else {
console.log('%s : rendered ', new Date, newfile);
}
});
});
}
files.forEach(function(file) {
var filename = __dirname + '/' + file;
jadeify(filename, filemap[file]);
if (process.argv[2] === '--watch') {
fs.watchFile(filename, {interval: 1000}, function(cur, prev) {
if (cur.mtime > prev.mtime) {
jadeify(filename, filemap[file]);
...
function map() { this.map = this._iter('map'); return this.map.apply(this, arguments); }
...
}
throw new Error('no match found');
}
function getUnstable(ver) {
ver = ver.replace('-pre');
var spl = ver.split('.');
spl = spl.map(function(i) {
return parseInt(i, 10);
});
spl[1]++;
spl[2] = 'x';
return spl.join('.');
}
...
function some() { var _this = this; var what = arguments.length ? arguments : this.stateNames; return Array.prototype.some.call(what, function(state) { return Object.keys(_this.states[state]).length; }); }
...
if (typeof obj !== 'object') {
return false;
}
k = Object.keys(obj);
return k.length === 1 && k
.some(function(key) {
return key[0] === '$';
});
}
/*!
* Adds the appropriate `$match` pipeline step to the top of an aggregate's
* pipeline, should it's model is a non-root discriminator type. This is
...
function StrictModeError(path, msg) { msg = msg || 'Field `' + path + '` is not in schema and strict ' + 'mode is set to throw.'; MongooseError.call(this, msg); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'StrictModeError'; this.path = path; }
n/a
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function SchemaString(key, options) { this.enumValues = []; this.regExp = null; SchemaType.call(this, key, options, 'String'); }
n/a
cast = function (value, doc, init) { if (SchemaType._isRef(this, value, doc, init)) { // wait! we may need to cast this to a document if (value === null || value === undefined) { return value; } // lazy load Document || (Document = require('./../document')); if (value instanceof Document) { value.$__.wasPopulated = true; return value; } // setting a populated path if (typeof value === 'string') { return value; } else if (Buffer.isBuffer(value) || !utils.isObject(value)) { throw new CastError('string', value, this.path); } // Handle the case where user directly sets a populated // path to a plain object; cast to the Model used in // the population query. var path = doc.$__fullPath(this.path); var owner = doc.ownerDocument ? doc.ownerDocument() : doc; var pop = owner.populated(path, true); var ret = new pop.options.model(value); ret.$__.wasPopulated = true; return ret; } // If null or undefined if (value === null || value === undefined) { return value; } if (typeof value !== 'undefined') { // handle documents being passed if (value._id && typeof value._id === 'string') { return value._id; } // Re: gh-647 and gh-3030, we're ok with casting using `toString()` // **unless** its the default Object.toString, because "[object Object]" // doesn't really qualify as useful data if (value.toString && value.toString !== Object.prototype.toString) { return value.toString(); } } throw new CastError('string', value, this.path); }
...
doc[i] = obj[i];
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
...
castForQuery = function ($conditional, val) { var handler; if (arguments.length === 2) { handler = this.$conditionalHandlers[$conditional]; if (!handler) { throw new Error('Can\'t use ' + $conditional + ' with String.'); } return handler.call(this, val); } val = $conditional; if (Object.prototype.toString.call(val) === '[object RegExp]') { return val; } return this.cast(val); }
...
}
if (geo) {
var numbertype = new Types.Number('__QueryCasting__');
var value = val[geo];
if (val.$maxDistance != null) {
val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
}
if (val.$minDistance != null) {
val.$minDistance = numbertype.castForQuery(val.$minDistance);
}
if (geo === '$within') {
var withinType = value.$center
...
function checkRequired(value, doc) { if (SchemaType._isRef(this, value, doc, true)) { return !!value; } return (value instanceof String || typeof value === 'string') && value.length; }
...
// in here, `this` refers to the validating document.
// no validation when this path wasn't selected in the query.
if ('isSelected' in this && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
return true;
}
return ((typeof required === 'function') && !required.apply(this)) ||
_this.checkRequired(v, this);
};
this.originalRequiredValue = required;
if (typeof required === 'string') {
message = required;
required = undefined;
}
...
function SchemaString(key, options) { this.enumValues = []; this.regExp = null; SchemaType.call(this, key, options, 'String'); }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
enum = function () { if (this.enumValidator) { this.validators = this.validators.filter(function(v) { return v.validator !== this.enumValidator; }, this); this.enumValidator = false; } if (arguments[0] === void 0 || arguments[0] === false) { return this; } var values; var errorMessage; if (utils.isObject(arguments[0])) { values = arguments[0].values; errorMessage = arguments[0].message; } else { values = arguments; errorMessage = MongooseError.messages.String.enum; } for (var i = 0; i < values.length; i++) { if (undefined !== values[i]) { this.enumValues.push(this.cast(values[i])); } } var vals = this.enumValues; this.enumValidator = function(v) { return undefined === v || ~vals.indexOf(v); }; this.validators.push({ validator: this.enumValidator, message: errorMessage, type: 'enum', enumValues: vals }); return this; }
n/a
lowercase = function (shouldApply) { if (arguments.length > 0 && !shouldApply) { return this; } return this.set(function(v, self) { if (typeof v !== 'string') { v = self.cast(v); } if (v) { return v.toLowerCase(); } return v; }); }
n/a
function match(regExp, message) { // yes, we allow multiple match validators var msg = message || MongooseError.messages.String.match; var matchValidator = function(v) { if (!regExp) { return false; } var ret = ((v != null && v !== '') ? regExp.test(v) : true); return ret; }; this.validators.push({ validator: matchValidator, message: msg, type: 'regexp', regexp: regExp }); return this; }
...
*/
/**
* Appends a new custom $match operator to this aggregate pipeline.
*
* ####Examples:
*
* aggregate.match({ department: { $in: [ "sales", "engineering"
; ] } });
*
* @see $match http://docs.mongodb.org/manual/reference/aggregation/match/
* @method match
* @memberOf Aggregate
* @param {Object} arg $match operator contents
* @return {Aggregate}
* @api public
...
maxlength = function (value, message) { if (this.maxlengthValidator) { this.validators = this.validators.filter(function(v) { return v.validator !== this.maxlengthValidator; }, this); } if (value !== null && value !== undefined) { var msg = message || MongooseError.messages.String.maxlength; msg = msg.replace(/{MAXLENGTH}/, value); this.validators.push({ validator: this.maxlengthValidator = function(v) { return v === null || v.length <= value; }, message: msg, type: 'maxlength', maxlength: value }); } return this; }
n/a
minlength = function (value, message) { if (this.minlengthValidator) { this.validators = this.validators.filter(function(v) { return v.validator !== this.minlengthValidator; }, this); } if (value !== null && value !== undefined) { var msg = message || MongooseError.messages.String.minlength; msg = msg.replace(/{MINLENGTH}/, value); this.validators.push({ validator: this.minlengthValidator = function(v) { return v === null || v.length >= value; }, message: msg, type: 'minlength', minlength: value }); } return this; }
n/a
trim = function (shouldTrim) { if (arguments.length > 0 && !shouldTrim) { return this; } return this.set(function(v, self) { if (typeof v !== 'string') { v = self.cast(v); } if (v) { return v.trim(); } return v; }); }
...
// add custom jade filters
require('./docs/helpers/filters')(jade);
function getVersion() {
var hist = fs.readFileSync('./History.md', 'utf8').replace(/\r/g, '\n').split('\n');
for (var i = 0; i < hist.length; ++i) {
var line = (hist[i] || '').trim();
if (!line) {
continue;
}
var match = /^\s*([^\s]+)\s/.exec(line);
if (match && match[1]) {
return match[1];
}
...
uppercase = function (shouldApply) { if (arguments.length > 0 && !shouldApply) { return this; } return this.set(function(v, self) { if (typeof v !== 'string') { v = self.cast(v); } if (v) { return v.toUpperCase(); } return v; }); }
n/a
function Subdocument(value, fields) { this.$isSingleNested = true; Document.call(this, value, fields); }
n/a
invalidate = function (path, err, val) { Document.prototype.invalidate.call(this, path, err, val); if (this.$parent) { this.$parent.invalidate([this.$basePath, path].join('.'), err, val); } else if (err.kind === 'cast' || err.name === 'CastError') { throw err; } }
...
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
try {
doc[i] = schema.cast(obj[i], self, true);
} catch (e) {
self.invalidate(e.path, new ValidatorError({
path: e.path,
message: e.message,
type: 'cast',
value: e.value
}));
}
} else {
...
markModified = function (path) { Document.prototype.markModified.call(this, path); if (this.$parent) { if (this.$parent.isDirectModified(this.$basePath)) { return; } this.$parent.markModified([this.$basePath, path].join('.')); } }
...
if (!merge) {
this.setValue(path, null);
cleanModifiedSubpaths(this, path);
}
if (Object.keys(val).length === 0) {
this.setValue(path, {});
this.markModified(path);
cleanModifiedSubpaths(this, path);
} else {
this.set(val, path, constructing);
}
return this;
}
this.invalidate(path, new MongooseError.CastError('Object', val, path));
...
ownerDocument = function () { if (this.$__.ownerDocument) { return this.$__.ownerDocument; } var parent = this.$parent; if (!parent) { return this; } while (parent.$parent || parent.__parent) { parent = parent.$parent || parent.__parent; } this.$__.ownerDocument = parent; return this.$__.ownerDocument; }
...
// the correct model name, treat it as populated
var didPopulate = false;
if (schema.options &&
schema.options.ref &&
val instanceof Document &&
(schema.options.ref === val.constructor.modelName || schema.options.ref === val.constructor.baseModelName)) {
if (this.ownerDocument) {
this.ownerDocument().populated(this.$__fullPath(path),
val._id, {model: val.constructor});
} else {
this.populated(path, val._id, {model: val.constructor});
}
didPopulate = true;
}
...
populate = function () { throw new Error('Mongoose does not support calling populate() on nested ' + 'docs. Instead of `doc.nested.populate("path")`, use ' + '`doc.populate("nested.path")`'); }
...
* Populates document references, executing the `callback` when complete.
* If you want to use promises instead, use this function with
* [`execPopulate()`](#document_Document-execPopulate)
*
* ####Example:
*
* doc
* .populate('company')
* .populate({
* path: 'notes',
* match: /airline/,
* select: 'text',
* model: 'modelName'
* options: opts
* }, function (err, user) {
...
remove = function (options, callback) { if (typeof options === 'function') { callback = options; options = null; } // If removing entire doc, no need to remove subdoc if (!options || !options.noop) { this.$parent.set(this.$basePath, null); registerRemoveListener(this); } if (typeof callback === 'function') { callback(null); } }
...
```
The same goes for removing them:
```js
BlogPost.findById(myId, function (err, post) {
if (!err) {
post.comments[0].remove();
post.save(function (err) {
// do something
});
}
});
```
...
save = function (fn) { var Promise = PromiseProvider.get(); return new Promise.ES6(function(resolve) { fn && fn(); resolve(); }); }
...
Then Mongoose will create the model for your __tickets__ collection, not your __ticket__ collection.
Once we have our model, we can then instantiate it, and save it:
```js
var instance = new MyModel();
instance.my.key = 'hello';
instance.save(function (err) {
//
});
```
Or we can find documents from the same collection
```js
...
toBSON = function () { return this.toObject({ transform: false, virtuals: false, _skipDepopulateTopLevel: true, depopulate: true, flattenDecimals: false }); }
n/a
function PopulateOptions(path, select, match, options, model, subPopulate) { this.path = path; this.match = match; this.select = select; this.options = options; this.model = model; if (typeof subPopulate === 'object') { this.populate = subPopulate; } this._docs = {}; }
n/a
args = function (args, slice, sliceEnd) { var ret = []; var len = args.length; if (0 === len) return ret; var start = slice < 0 ? Math.max(0, slice + len) : slice || 0; if (sliceEnd !== undefined) { len = sliceEnd < 0 ? sliceEnd + len : sliceEnd } while (len-- > start) { ret[len - start] = args[len]; } return ret; }
...
* @return {Aggregate}
* @api public
*/
Aggregate.prototype.append = function() {
var args = (arguments.length === 1 && util.isArray(arguments[0]))
? arguments[0]
: utils.args(arguments);
if (!args.every(isOperator)) {
throw new Error('Arguments must be aggregate pipeline operators');
}
this._pipeline = this._pipeline.concat(args);
...
function clone(obj, options) { if (obj === undefined || obj === null) { return obj; } if (Array.isArray(obj)) { return cloneArray(obj, options); } if (isMongooseObject(obj)) { if (options && options.json && typeof obj.toJSON === 'function') { return obj.toJSON(options); } return obj.toObject(options); } if (obj.constructor) { switch (exports.getFunctionName(obj.constructor)) { case 'Object': return cloneObject(obj, options); case 'Date': return new obj.constructor(+obj); case 'RegExp': return cloneRegExp(obj); default: // ignore break; } } if (obj instanceof ObjectId) { return new ObjectId(obj.id); } if (obj instanceof Decimal) { if (options && options.flattenDecimals) { return obj.toJSON(); } return Decimal.fromString(obj.toString()); } if (!obj.constructor && exports.isObject(obj)) { // object created with Object.create(null) return cloneObject(obj, options); } if (obj.valueOf) { return obj.valueOf(); } }
...
Aggregate.prototype.exec = function(callback) {
if (!this._model) {
throw new Error('Aggregate not bound to any Model');
}
var _this = this;
var Promise = PromiseProvider.get();
var options = utils.clone(this.options);
if (options && options.cursor) {
if (options.cursor.async) {
delete options.cursor.async;
return new Promise.ES6(function(resolve) {
if (!_this._model.collection.buffer) {
process.nextTick(function() {
...
decorate = function (destination, source) { for (var key in source) { destination[key] = source[key]; } }
n/a
function deepEqual(a, b) { if (a === b) { return true; } if (a instanceof Date && b instanceof Date) { return a.getTime() === b.getTime(); } if ((a instanceof ObjectId && b instanceof ObjectId) || (a instanceof Decimal && b instanceof Decimal)) { return a.toString() === b.toString(); } if (a instanceof RegExp && b instanceof RegExp) { return a.source === b.source && a.ignoreCase === b.ignoreCase && a.multiline === b.multiline && a.global === b.global; } if (typeof a !== 'object' && typeof b !== 'object') { return a == b; } if (a === null || b === null || a === undefined || b === undefined) { return false; } if (a.prototype !== b.prototype) { return false; } // Handle MongooseNumbers if (a instanceof Number && b instanceof Number) { return a.valueOf() === b.valueOf(); } if (Buffer.isBuffer(a)) { return exports.buffer.areEqual(a, b); } if (isMongooseObject(a)) { a = a.toObject(); } if (isMongooseObject(b)) { b = b.toObject(); } try { var ka = Object.keys(a), kb = Object.keys(b), key, i; } catch (e) { // happens when one is a string literal and the other isn't return false; } // having the same number of owned properties (keys incorporates // hasOwnProperty) if (ka.length !== kb.length) { return false; } // the same set of keys (although not necessarily the same order), ka.sort(); kb.sort(); // ~~~cheap key test for (i = ka.length - 1; i >= 0; i--) { if (ka[i] !== kb[i]) { return false; } } // equivalent values for every corresponding key, and // ~~~possibly expensive deep test for (i = ka.length - 1; i >= 0; i--) { key = ka[i]; if (!deepEqual(a[key], b[key])) { return false; } } return true; }
n/a
each = function (arr, fn) { for (var i = 0; i < arr.length; ++i) { fn(arr[i]); } }
...
* Sets the cursor option option for the aggregation query (ignored for < 2.6.0).
* Note the different syntax below: .exec() returns a cursor object, and no callback
* is necessary.
*
* ####Example:
*
* var cursor = Model.aggregate(..).cursor({ batchSize: 1000 }).exec();
* cursor.each(function(error, doc) {
* // use doc
* });
*
* @param {Object} options set the cursor batch size
* @see mongodb http://mongodb.github.io/node-mongodb-native/2.0/api/AggregationCursor.html
*/
...
function expires(object) { if (!(object && object.constructor.name === 'Object')) { return; } if (!('expires' in object)) { return; } var when; if (typeof object.expires !== 'string') { when = object.expires; } else { when = Math.round(ms(object.expires) / 1000); } object.expireAfterSeconds = when; delete object.expires; }
...
* @api public
*/
Schema.prototype.index = function(fields, options) {
options || (options = {});
if (options.expires) {
utils.expires(options);
}
this._indexes.push([fields, options]);
return this;
};
/**
...
getFunctionName = function (fn) { if (fn.name) { return fn.name; } return (fn.toString().trim().match(/^function\s*([^\s(]+)/) || [])[1]; }
...
var keys;
var ki;
var _this = this;
// determine if this doc is a result of a query with
// excluded fields
if (fields && utils.getFunctionName(fields.constructor) === 'Object
x27;) {
keys = Object.keys(fields);
ki = keys.length;
if (ki === 1 && keys[0] === '_id') {
exclude = !!fields[keys[ki]];
} else {
while (ki--) {
...
getValue = function (path, obj, map) { return mpath.get(path, obj, '_doc', map); }
...
// If doc._id is not null or undefined
if (doc._id !== null && doc._id !== undefined &&
opts && opts.populated && opts.populated.length) {
var id = String(doc._id);
for (var i = 0; i < opts.populated.length; ++i) {
var item = opts.populated[i];
if (item.isVirtual) {
this.populated(item.path, utils.getValue(item.path, doc), item);
} else {
this.populated(item.path, item._docs[id], item);
}
}
}
init(this, doc, this._doc);
...
isMongooseObject = function (v) { Document || (Document = require('./document')); MongooseArray || (MongooseArray = require('./types').Array); MongooseBuffer || (MongooseBuffer = require('./types').Buffer); return v instanceof Document || (v && v.isMongooseArray) || (v && v.isMongooseBuffer); }
...
Array.isArray(value.$geometry.coordinates)) {
if (value.$maxDistance != null) {
value.$maxDistance = numbertype.castForQuery(value.$maxDistance);
}
if (value.$minDistance != null) {
value.$minDistance = numbertype.castForQuery(value.$minDistance);
}
if (utils.isMongooseObject(value.$geometry)) {
value.$geometry = value.$geometry.toObject({
transform: false,
virtuals: false
});
}
value = value.$geometry.coordinates;
} else if (geo === '$geoWithin') {
...
isNullOrUndefined = function (val) { return val === null || val === undefined; }
...
* @api private
*/
exports.toObject = function toObject(obj) {
Document || (Document = require('./document'));
var ret;
if (exports.isNullOrUndefined(obj)) {
return obj;
}
if (obj instanceof Document) {
return obj.toObject();
}
...
isObject = function (arg) { if (Buffer.isBuffer(arg)) { return true; } return toString.call(arg) === '[object Object]'; }
...
* @return {Aggregate}
* @api public
*/
Aggregate.prototype.graphLookup = function(options) {
var cloneOptions = {};
if (options) {
if (!utils.isObject(options)) {
throw new TypeError('Invalid graphLookup() argument. Must be an object.');
}
utils.mergeClone(cloneOptions, options);
var startWith = cloneOptions.startWith;
if (startWith && typeof startWith === 'string') {
...
function merge(to, from, options) { options = options || {}; var keys = Object.keys(from); var i = 0; var len = keys.length; var key; if (options.retainKeyOrder) { while (i < len) { key = keys[i++]; if (to[key] == null) { to[key] = from[key]; } else if (exports.isObject(from[key])) { merge(to[key], from[key], options); } else if (options.overwrite) { to[key] = from[key]; } } } else { while (len--) { key = keys[len]; if (to[key] == null) { to[key] = from[key]; } else if (exports.isObject(from[key])) { merge(to[key], from[key], options); } else if (options.overwrite) { to[key] = from[key]; } } } }
...
};
if (isVirtual && virtual.options && virtual.options.options) {
currentOptions.options = utils.clone(virtual.options.options, {
retainKeyOrder: true
});
}
utils.merge(currentOptions, options);
if (schema && !discriminatorKey) {
currentOptions.model = Model;
}
options.model = Model;
available[modelName] = {
Model: Model,
...
mergeClone = function (to, fromObj) { var keys = Object.keys(fromObj); var len = keys.length; var i = 0; var key; while (i < len) { key = keys[i++]; if (typeof to[key] === 'undefined') { // make sure to retain key order here because of a bug handling the $each // operator in mongodb 2.4.4 to[key] = exports.clone(fromObj[key], {retainKeyOrder: 1}); } else { if (exports.isObject(fromObj[key])) { var obj = fromObj[key]; if (isMongooseObject(fromObj[key]) && !fromObj[key].isMongooseBuffer) { obj = obj.toObject({ transform: false, virtuals: false }); } if (fromObj[key].isMongooseBuffer) { obj = new Buffer(obj); } exports.mergeClone(to[key], obj); } else { // make sure to retain key order here because of a bug handling the // $each operator in mongodb 2.4.4 to[key] = exports.clone(fromObj[key], {retainKeyOrder: 1}); } } } }
...
Aggregate.prototype.graphLookup = function(options) {
var cloneOptions = {};
if (options) {
if (!utils.isObject(options)) {
throw new TypeError('Invalid graphLookup() argument. Must be an object.');
}
utils.mergeClone(cloneOptions, options);
var startWith = cloneOptions.startWith;
if (startWith && typeof startWith === 'string') {
cloneOptions.startWith = cloneOptions.startWith.charAt(0) === '$' ?
cloneOptions.startWith :
'$' + cloneOptions.startWith;
}
...
options = function (defaults, options) { var keys = Object.keys(defaults), i = keys.length, k; options = options || {}; while (i--) { k = keys[i]; if (!(k in options)) { options[k] = defaults[k]; } } return options; }
...
}
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,
...
function populate(path, select, model, match, options, subPopulate) { // The order of select/conditions args is opposite Model.find but // necessary to keep backward compatibility (select could be // an array, string, or object literal). // might have passed an object specifying all arguments if (arguments.length === 1) { if (path instanceof PopulateOptions) { return [path]; } if (Array.isArray(path)) { return path.map(function(o) { return exports.populate(o)[0]; }); } if (exports.isObject(path)) { match = path.match; options = path.options; select = path.select; model = path.model; subPopulate = path.populate; path = path.path; } } else if (typeof model !== 'string' && typeof model !== 'function') { options = match; match = model; model = undefined; } if (typeof path !== 'string') { throw new TypeError('utils.populate: invalid path. Expected string. Got typeof `' + typeof path + '`'); } if (typeof subPopulate === 'object') { subPopulate = exports.populate(subPopulate); } var ret = []; var paths = path.split(' '); options = exports.clone(options, { retainKeyOrder: true }); for (var i = 0; i < paths.length; ++i) { ret.push(new PopulateOptions(paths[i], select, match, options, model, subPopulate)); } return ret; }
...
* Populates document references, executing the `callback` when complete.
* If you want to use promises instead, use this function with
* [`execPopulate()`](#document_Document-execPopulate)
*
* ####Example:
*
* doc
* .populate('company')
* .populate({
* path: 'notes',
* match: /airline/,
* select: 'text',
* model: 'modelName'
* options: opts
* }, function (err, user) {
...
random = function () { return Math.random().toString().substr(3); }
...
/*!
* Generates a random string
*
* @api private
*/
exports.random = function() {
return Math.random().toString().substr(3);
};
/*!
* Merges `from` into `to` without overwriting existing properties.
*
* @param {Object} to
* @param {Object} from
...
setValue = function (path, val, obj, map) { mpath.set(path, val, obj, '_doc', map); }
...
}
var pathType = this.schema.pathType(path);
if (pathType === 'nested' && val) {
if (utils.isObject(val) &&
(!val.constructor || utils.getFunctionName(val.constructor) === 'Object')) {
if (!merge) {
this.setValue(path, null);
cleanModifiedSubpaths(this, path);
}
if (Object.keys(val).length === 0) {
this.setValue(path, {});
this.markModified(path);
cleanModifiedSubpaths(this, path);
...
function tick(callback) { if (typeof callback !== 'function') { return; } return function() { try { callback.apply(this, arguments); } catch (err) { // only nextTick on err to get out of // the event loop and avoid state corruption. process.nextTick(function() { throw err; }); } }; }
...
var indexFields = index[0];
var options = index[1];
_handleSafe(options);
indexSingleStart(indexFields, options);
model.collection.ensureIndex(indexFields, options, utils.tick(function(err, name) {
indexSingleDone(err, indexFields, options, name);
if (err) {
return done(err);
}
create();
}));
};
...
toCollectionName = function (name, options) { options = options || {}; if (name === 'system.profile') { return name; } if (name === 'system.indexes') { return name; } if (options.pluralization === false) { return name; } return pluralize(name.toLowerCase()); }
...
? schema
: _this.prototype.schema;
var options = s.options || {};
if (!collection) {
collection = _this.prototype.schema.get('collection')
|| utils.toCollectionName(_this.modelName, options);
}
var collectionOptions = {
bufferCommands: s ? options.bufferCommands : true,
capped: s && options.capped
};
...
function toObject(obj) { Document || (Document = require('./document')); var ret; if (exports.isNullOrUndefined(obj)) { return obj; } if (obj instanceof Document) { return obj.toObject(); } if (Array.isArray(obj)) { ret = []; for (var i = 0, len = obj.length; i < len; ++i) { ret.push(toObject(obj[i])); } return ret; } if ((obj.constructor && exports.getFunctionName(obj.constructor) === 'Object') || (!obj.constructor && exports.isObject(obj))) { ret = {}; for (var k in obj) { ret[k] = toObject(obj[k]); } return ret; } return obj; }
...
if (value.$maxDistance != null) {
value.$maxDistance = numbertype.castForQuery(value.$maxDistance);
}
if (value.$minDistance != null) {
value.$minDistance = numbertype.castForQuery(value.$minDistance);
}
if (utils.isMongooseObject(value.$geometry)) {
value.$geometry = value.$geometry.toObject({
transform: false,
virtuals: false
});
}
value = value.$geometry.coordinates;
} else if (geo === '$geoWithin') {
if (value.$geometry) {
...
function ValidationError(instance) { this.errors = {}; if (instance && instance.constructor.name === 'model') { MongooseError.call(this, instance.constructor.modelName + ' validation failed'); } else { MongooseError.call(this, 'Validation failed'); } if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.name = 'ValidationError'; if (instance) { instance.errors = this.errors; } }
n/a
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
toString = function () { var ret = this.name + ': '; var msgs = []; Object.keys(this.errors || {}).forEach(function(key) { if (this === this.errors[key]) { return; } msgs.push(String(this.errors[key])); }, this); return ret + msgs.join(', '); }
...
```
Moreover, you can mutate the incoming `method` arguments so that subsequent middleware see different values for those arguments.
To do so, just pass the new values to `next`:
```js
.pre(method, function firstPre (next, methodArg1, methodArg2) {
// Mutate methodArg1
next("altered-" + methodArg1.toString(), methodArg2);
});
// pre declaration is chainable
.pre(method, function secondPre (next, methodArg1, methodArg2) {
console.log(methodArg1);
// => 'altered-originalValOfMethodArg1'
...
function ValidatorError(properties) { var msg = properties.message; if (!msg) { msg = MongooseError.messages.general.default; } var message = this.formatMessage(msg, properties); MongooseError.call(this, message); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.properties = properties; this.name = 'ValidatorError'; this.kind = properties.type; this.path = properties.path; this.value = properties.value; this.reason = properties.reason; }
n/a
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
formatMessage = function (msg, properties) { var propertyNames = Object.keys(properties); for (var i = 0; i < propertyNames.length; ++i) { var propertyName = propertyNames[i]; if (propertyName === 'message') { continue; } msg = msg.replace('{' + propertyName.toUpperCase() + '}', properties[propertyName]); } return msg; }
...
function ValidatorError(properties) {
var msg = properties.message;
if (!msg) {
msg = MongooseError.messages.general.default;
}
var message = this.formatMessage(msg, properties);
MongooseError.call(this, message);
if (Error.captureStackTrace) {
Error.captureStackTrace(this);
} else {
this.stack = new Error().stack;
}
this.properties = properties;
...
toString = function () { return this.message; }
...
```
Moreover, you can mutate the incoming `method` arguments so that subsequent middleware see different values for those arguments.
To do so, just pass the new values to `next`:
```js
.pre(method, function firstPre (next, methodArg1, methodArg2) {
// Mutate methodArg1
next("altered-" + methodArg1.toString(), methodArg2);
});
// pre declaration is chainable
.pre(method, function secondPre (next, methodArg1, methodArg2) {
console.log(methodArg1);
// => 'altered-originalValOfMethodArg1'
...
function VersionError(doc) { MongooseError.call(this, 'No matching document found for id "' + doc._id + '"'); this.name = 'VersionError'; }
n/a
function MongooseError(msg) { Error.call(this); if (Error.captureStackTrace) { Error.captureStackTrace(this); } else { this.stack = new Error().stack; } this.message = msg; this.name = 'MongooseError'; }
...
}
if (obj.constructor) {
switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
return new obj.constructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
...
function VirtualType(options, name) { this.path = name; this.getters = []; this.setters = []; this.options = options || {}; }
n/a
applyGetters = function (value, scope) { var v = value; for (var l = this.getters.length - 1; l >= 0; l--) { v = this.getters[l].call(scope, v, this); } return v; }
...
if (adhoc) {
obj = adhoc.cast(obj);
}
// Check if this path is populated - don't apply getters if it is,
// because otherwise its a nested object. See gh-3357
if (schema && !this.populated(path)) {
obj = schema.applyGetters(obj, this);
}
return obj;
};
/**
* Returns the schematype for the given `path`.
...
applySetters = function (value, scope) { var v = value; for (var l = this.setters.length - 1; l >= 0; l--) { v = this.setters[l].call(scope, v, this); } return v; }
...
if (strict === 'throw') {
throw new StrictModeError(path);
}
return this;
}
} else if (pathType === 'virtual') {
schema = this.schema.virtualpath(path);
schema.applySetters(val, this);
return this;
} else {
schema = this.$__path(path);
}
var pathToMark;
...
get = function (fn) { this.getters.push(fn); return this; }
...
// a setter
Comment.path('name').set(function (v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function (next) {
notify(this.get('email'));
next();
});
```
Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
### Accessing a Model
...
set = function (fn) { this.setters.push(fn); return this; }
...
age: { type: Number, min: 18, index: true },
bio: { type: String, match: /[a-z]/ },
date: { type: Date, default: Date.now },
buff: Buffer
});
// a setter
Comment.path('name').set(function (v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function (next) {
notify(this.get('email'));
next();
...