name
, conn
, opts
)Abstract Collection constructor
show codefunction Collection (name, conn, opts) {
this.name = name;
this.conn = conn;
this.buffer = true;
this.queue = [];
if ('number' == typeof opts) opts = { size: opts };
this.opts = opts || {};
if (STATES.connected == this.conn.readyState) {
this.onOpen();
}
};
name
<String> name of the collectionconn
<Connection> A MongooseConnection instanceopts
<Object> optional collection optionsThis is the base class that drivers inherit from and implement.
Called when the database connects
show codeCollection.prototype.onOpen = function () {
var self = this;
this.buffer = false;
self.doQueue();
};
Called when the database disconnects
show codeCollection.prototype.onClose = function () {
this.buffer = true;
};
name
, args
)Queues a method for later execution when its
database connection opens.
Collection.prototype.addQueue = function (name, args) {
this.queue.push([name, args]);
return this;
};
Executes all queued methods and clears the queue.
show codeCollection.prototype.doQueue = function () {
for (var i = 0, l = this.queue.length; i < l; i++){
this[this.queue[i][0]].apply(this, this.queue[i][1]);
}
this.queue = [];
return this;
};
Abstract method that drivers must implement.
show codeCollection.prototype.ensureIndex = function(){
throw new Error('Collection#ensureIndex unimplemented by driver');
};
Abstract method that drivers must implement.
show codeCollection.prototype.findAndModify = function(){
throw new Error('Collection#findAndModify unimplemented by driver');
};
Abstract method that drivers must implement.
show codeCollection.prototype.findOne = function(){
throw new Error('Collection#findOne unimplemented by driver');
};
Abstract method that drivers must implement.
show codeCollection.prototype.find = function(){
throw new Error('Collection#find unimplemented by driver');
};
Abstract method that drivers must implement.
show codeCollection.prototype.insert = function(){
throw new Error('Collection#insert unimplemented by driver');
};
Abstract method that drivers must implement.
show codeCollection.prototype.save = function(){
throw new Error('Collection#save unimplemented by driver');
};
Abstract method that drivers must implement.
show codeCollection.prototype.update = function(){
throw new Error('Collection#update unimplemented by driver');
};
Abstract method that drivers must implement.
show codeCollection.prototype.getIndexes = function(){
throw new Error('Collection#getIndexes unimplemented by driver');
};
Abstract method that drivers must implement.
show codeCollection.prototype.mapReduce = function(){
throw new Error('Collection#mapReduce unimplemented by driver');
};
The Connection instance
The collection name
base
)Connection constructor
show codefunction Connection (base) {
this.base = base;
this.collections = {};
this.models = {};
this.replica = false;
this.host = null;
this.port = null;
this.user = null;
this.pass = null;
this.name = null;
this.options = null;
this._readyState = STATES.disconnected;
};
base
<Mongoose> a mongoose instanceFor practical reasons, a Connection equals a Db.
connection_string
, [database]
, [port]
, [options]
, [callback]
)Opens the connection to MongoDB.
show codeConnection.prototype.open = function (host, database, port, options, callback) {
var self = this
, uri;
if ('string' === typeof database) {
switch (arguments.length) {
case 2:
port = 27017;
case 3:
switch (typeof port) {
case 'function':
callback = port, port = 27017;
break;
case 'object':
options = port, port = 27017;
break;
}
break;
case 4:
if ('function' === typeof options)
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;
}
uri = url.parse(host);
host = uri.hostname;
port = uri.port || 27017;
database = uri.pathname && uri.pathname.replace(/\//g, '');
}
this.options = this.defaultOptions(options);
// make sure we can open
if (STATES.disconnected !== this.readyState) {
var err = new Error('Trying to open unclosed connection.');
err.state = this.readyState;
this.error(err, callback);
return this;
}
if (!host) {
this.error(new Error('Missing connection hostname.'), callback);
return this;
}
if (!database) {
this.error(new Error('Missing connection database.'), callback);
return this;
}
// handle authentication
if (uri && uri.auth) {
var auth = uri.auth.split(':');
this.user = auth[0];
this.pass = auth[1];
// Check hostname for user/pass
} else if (/@/.test(host) && /:/.test(host.split('@')[0])) {
host = host.split('@');
var auth = host.shift().split(':');
host = host.pop();
this.user = auth[0];
this.pass = auth[1];
// user/pass options
} else if (options && options.user && options.pass) {
this.user = options.user;
this.pass = options.pass;
} else {
this.user = this.pass = undefined;
}
this.name = database;
this.host = host;
this.port = port;
this._open(callback);
return this;
};
options
is a hash with the following possible properties:
db - passed to the connection db instance
server - passed to the connection server instance(s)
replset - passed to the connection ReplSetServer instance
user - username for authentication
pass - password for authentication
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.
uris
, [database]
, [options]
, [callback]
)Connects to a replica set.
show codeConnection.prototype.openSet = function (uris, database, options, callback) {
var uris = uris.split(',')
, self = this;
switch (arguments.length) {
case 3:
this.name = database;
if ('function' === typeof options) callback = options, options = {};
break;
case 2:
switch (typeof database) {
case 'string':
this.name = database;
case 'function':
callback = database, database = null;
break;
case 'object':
options = database, database = null;
break;
}
}
this.options = options = this.defaultOptions(options);
if (uris.length < 2) {
this.error(new Error('Please provide comma-separated URIs'), callback);
return this;
}
this.replica = true;
this.host = [];
this.port = [];
uris.forEach(function (uri) {
// handle missing protocols
if (!rgxProtocol.test(uri))
uri = 'mongodb://' + uri;
var uri = url.parse(uri);
self.host.push(uri.hostname);
self.port.push(uri.port || 27017);
if (!self.name && uri.pathname && uri.pathname.replace(/\//g, ''))
self.name = uri.pathname.replace(/\//g, '');
if (!self.user && uri.auth) {
var auth = uri.auth.split(':');
self.user = auth[0];
self.pass = auth[1];
}
});
if (!this.name) {
this.error(new Error('No database name provided for replica set'), callback);
return this;
}
this._open(callback);
return this;
};
var db = mongoose.createConnection();
db.openSet("mongodb://user:pwd@localhost:27020/testing,mongodb://example.com:27020,mongodb://localhost:27019");
The database name and/or auth need only be included in one URI.
The options
are passed to the internal driver connection object.
err
, callback
)error
show codeConnection.prototype.error = function (err, callback) {
if (callback) return callback(err);
this.emit('error', err);
}
Graceful error handling, passes error to callback
if available, else emits error on the connection.
callback
)_open
show codeConnection.prototype._open = function (callback) {
this.readyState = STATES.connecting;
var self = this;
var method = this.replica
? 'doOpenSet'
: 'doOpen';
// open connection
this[method](function (err) {
if (err) {
self.readyState = STATES.disconnected;
self.error(err, callback);
return;
}
self.onOpen();
callback && callback();
});
}
callback
<Function> Handles opening the connection with the appropriate
method based on connection type.
Called when the connection is opened
show codeConnection.prototype.onOpen = function () {
var self = this;
function open () {
self.readyState = STATES.connected;
// avoid having the collection subscribe to our event emitter
// to prevent 0.3 warning
for (var i in self.collections)
self.collections[i].onOpen();
self.emit('open');
};
// re-authenticate
if (self.user && self.pass)
self.db.authenticate(self.user, self.pass, open);
else
open();
};
[callback]
)Closes the connection
show codeConnection.prototype.close = function (callback) {
var self = this;
switch (this.readyState){
case 0: // disconnected
callback && callback();
break;
case 1: // connected
this.readyState = STATES.disconnecting;
this.doClose(function(err){
if (err){
self.error(err, callback);
} else {
self.onClose();
callback && callback();
}
});
break;
case 2: // connecting
this.once('open', function(){
self.close(callback);
});
break;
case 3: // disconnecting
if (!callback) break;
this.once('close', function () {
callback();
});
break;
}
return this;
};
[callback]
<Function> optionalCalled when the connection closes
show codeConnection.prototype.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)
this.collections[i].onClose();
this.emit('close');
};
name
, [options]
)Retrieves a collection, creating it if not cached.
show codeConnection.prototype.collection = function (name, options) {
if (!(name in this.collections))
this.collections[name] = new Collection(name, this, options);
return this.collections[name];
};
name
, [schema]
, [collection]
)Defines or retrieves a model.
show codeConnection.prototype.model = function (name, schema, collection) {
if (!this.models[name]) {
var model = this.base.model(name, schema, collection, true)
, Model
if (this != model.prototype.db) {
// subclass model using this connection and collection name
Model = function Model (doc, fields, skipId) {
if (!(this instanceof Model))
return new Model(doc, fields, skipId);
model.call(this, doc, fields, skipId);
};
Model.__proto__ = model;
Model.prototype.__proto__ = model.prototype;
Model.db = Model.prototype.db = this;
// collection name discovery
if ('string' === typeof schema) {
collection = schema;
}
if (!collection) {
collection = model.prototype.schema.set('collection') || utils.toCollectionName(name);
}
var s = 'string' != typeof schema
? schema
: model.prototype.schema;
Model.prototype.collection = this.collection(collection, s && s.options.capped);
Model.collection = Model.prototype.collection;
Model.init();
}
this.models[name] = Model || model;
}
return this.models[name];
};
var mongoose = require('mongoose');
var db = mongoose.createConnection(..);
db.model('Venue', new Schema(..));
var Ticket = db.model('Ticket', new Schema(..));
var Venue = db.model('Venue');
level
, [ms]
, callback
)Set profiling level.
show codeConnection.prototype.setProfiling = function (level, ms, callback) {
if (STATES.connected !== this.readyState) {
return this.on('open', this.setProfiling.bind(this, level, ms, callback));
}
if (!callback) callback = ms, ms = 100;
var cmd = {};
switch (level) {
case 0:
case 'off':
cmd.profile = 0;
break;
case 1:
case 'slow':
cmd.profile = 1;
if ('number' !== typeof ms) {
ms = parseInt(ms, 10);
if (isNaN(ms)) ms = 100;
}
cmd.slowms = ms;
break;
case 2:
case 'all':
cmd.profile = 2;
break;
default:
return callback(new Error('Invalid profiling level: '+ level));
}
this.db.executeDbCommand(cmd, function (err, resp) {
if (err) return callback(err);
var doc = resp.documents[0];
err = 1 === doc.ok
? null
: new Error('Could not set profiling level to: '+ level)
callback(err, doc);
});
};
options
)Prepares default connection options.
show codeConnection.prototype.defaultOptions = function (options) {
var o = options || {};
o.server = o.server || {};
if (!('auto_reconnect' in o.server)) {
o.server.auto_reconnect = true;
}
o.db = o.db || {};
o.db.forceServerObjectId = false;
return o;
}
options
<Object> The mongodb.Db instance, set when the connection is opened
A hash of the collections associated with this connection
Connection ready state
Each state change emits its associated event name.
conn.on('connected', callback);
conn.on('disconnected', callback);
obj
, [fields]
, [skipId]
)Document constructor.
show codefunction Document (obj, fields, skipId) {
// node <0.4.3 bug
if (!this._events) this._events = {};
this.setMaxListeners(0);
if ('boolean' === typeof fields) {
this._strictMode = fields;
this._selected = fields = undefined;
} else {
this._strictMode = this.schema.options && this.schema.options.strict;
this._selected = fields;
}
this.isNew = true;
this.errors = undefined;
this._shardval = undefined;
this._saveError = undefined;
this._validationError = undefined;
this._adhocPaths = undefined;
this._removing = undefined;
this._inserting = undefined;
this.__version = undefined;
this.__getters = {};
this.__id = undefined;
this._activePaths = new ActiveRoster;
var required = this.schema.requiredPaths();
for (var i = 0; i < required.length; ++i) {
this._activePaths.require(required[i]);
}
this._doc = this._buildDoc(obj, fields, skipId);
if (obj) this.set(obj, undefined, true);
this._registerHooks();
};
obj
, [fields]
, [skipId]
)Builds the default doc structure
show codeDocument.prototype._buildDoc = function (obj, fields, skipId) {
var doc = {}
, self = this
, exclude
, keys
, key
, ki
// determine if this doc is a result of a query with
// excluded fields
if (fields && 'Object' === fields.constructor.name) {
keys = Object.keys(fields);
ki = keys.length;
while (ki--) {
if ('_id' !== keys[ki]) {
exclude = 0 === fields[keys[ki]];
break;
}
}
}
var paths = Object.keys(this.schema.paths)
, plen = paths.length
, ii = 0
for (; ii < plen; ++ii) {
var p = paths[ii];
if ('_id' == p) {
if (skipId) continue;
if (obj && '_id' in obj) continue;
}
var type = this.schema.paths[p]
, path = p.split('.')
, len = path.length
, last = len-1
, doc_ = doc
, i = 0
for (; i < len; ++i) {
var piece = path[i]
, def
if (i === last) {
if (fields) {
if (exclude) {
// apply defaults to all non-excluded fields
if (p in fields) continue;
def = type.getDefault(self, true);
if ('undefined' !== typeof def) {
doc_[piece] = def;
self._activePaths.default(p);
}
} else if (p in fields) {
// selected field
def = type.getDefault(self, true);
if ('undefined' !== typeof def) {
doc_[piece] = def;
self._activePaths.default(p);
}
}
} else {
def = type.getDefault(self, true);
if ('undefined' !== typeof def) {
doc_[piece] = def;
self._activePaths.default(p);
}
}
} else {
doc_ = doc_[piece] || (doc_[piece] = {});
}
}
};
return doc;
};
doc
, fn
)Initializes the document without setters or marking modified.
show codeDocument.prototype.init = function (doc, fn) {
this.isNew = false;
init(this, doc, this._doc);
this._storeShard();
this.emit('init');
if (fn) fn(null);
return this;
};
Called internally after a document is returned from mongodb.
Stores the current values of the shard keys.
show codeDocument.prototype._storeShard = function _storeShard () {
var key = this.schema.options.shardkey;
if (!(key && 'Object' == key.constructor.name)) return;
var orig = this._shardval = {}
, paths = Object.keys(key)
, len = paths.length
, val
for (var i = 0; i < len; ++i) {
val = this.getValue(paths[i]);
if (isMongooseObject(val)) {
orig[paths[i]] = val.toObject({ depopulate: true })
} else if (null != val && val.valueOf) {
orig[paths[i]] = val.valueOf();
} else {
orig[paths[i]] = val;
}
}
}
Shard key values do not / are not allowed to change.
doc
, options
, callback
)Sends an update command with this document _id
as the query selector.
Document.prototype.update = function update () {
var args = utils.args(arguments);
args.unshift({_id: this._id});
this.constructor.update.apply(this.constructor, args);
}
weirdCar.update({$inc: {wheels:1}, { safe: true }, callback);
safe
(boolean) safe mode (defaults to value set in schema (true))upsert
(boolean) whether to create the doc if it doesn't match (false)path
, val
, [type]
)Sets the value of a path, or many paths.
show codeDocument.prototype.set = function (path, val, type) {
var constructing = true === type
, adhoc = type && true !== type
, adhocs
if (adhoc) {
adhocs = this._adhocPaths || (this._adhocPaths = {});
adhocs[path] = Schema.interpretAsType(path, type);
}
if ('string' !== typeof path) {
// new Document({ key: val })
if (null === path || undefined === path) {
var _ = path;
path = val;
val = _;
} else {
var prefix = val
? val + '.'
: '';
if (path instanceof Document) path = path._doc;
var keys = Object.keys(path)
, i = keys.length
, pathtype
, key
while (i--) {
key = keys[i];
if (null != path[key] && 'Object' === path[key].constructor.name
&& !(this._path(prefix + key) instanceof MixedSchema)) {
this.set(path[key], prefix + key, constructing);
} else if (this._strictMode) {
pathtype = this.schema.pathType(prefix + key);
if ('real' === pathtype || 'virtual' === pathtype) {
this.set(prefix + key, path[key], constructing);
} else if ('throw' == this._strictMode) {
throw new Error("Field `" + key + "` is not in schema.");
}
} else if (undefined !== path[key]) {
this.set(prefix + key, path[key], constructing);
}
}
return this;
}
}
// ensure _strict is honored for obj props
// docschema = new Schema({ path: { nest: 'string' }})
// doc.set('path', obj);
var pathType = this.schema.pathType(path);
if ('nested' == pathType && val && 'Object' == val.constructor.name) {
this.set(val, path, constructing);
return this;
}
var schema;
if ('adhocOrUndefined' == pathType && this._strictMode) {
return this;
} else if ('virtual' == pathType) {
schema = this.schema.virtualpath(path);
schema.applySetters(val, this);
return this;
} else {
schema = this._path(path);
}
var parts = path.split('.')
, pathToMark
// When using the $set operator the path to the field must already exist.
// Else mongodb throws: "LEFT_SUBFIELD only supports Object"
if (parts.length <= 1) {
pathToMark = path;
} else {
for (var i = 0; i < parts.length; ++i) {
var part = parts[i];
var subpath = parts.slice(0, i).concat(part).join('.');
if (this.isDirectModified(subpath) // earlier prefixes that are already
// marked as dirty have precedence
|| this.get(subpath) === null) {
pathToMark = subpath;
break;
}
}
if (!pathToMark) pathToMark = path;
}
if (!schema || null === val || undefined === val) {
this._set(pathToMark, path, constructing, parts, schema, val);
return this;
}
var self = this;
// if this doc is being constructed we should not
// trigger getters.
var priorVal = constructing
? undefined
: this.get(path);
var shouldSet = this.try(function(){
var casted = schema.cast(val, self, false, priorVal);
val = schema.applySetters(casted, self);
});
if (shouldSet) {
this._set(pathToMark, path, constructing, parts, schema, val, priorVal);
}
return this;
}
// path, value
doc.set(path, value)
// object
doc.set({
path : value
, path2 : {
path : value
}
})
// only-the-fly cast to number
doc.set(path, value, Number)
// only-the-fly cast to string
doc.set(path, value, String)
Handles the actual setting of the value and marking the path modified if appropriate.
show codeDocument.prototype._set = function (pathToMark, path, constructing, parts, schema, val, priorVal) {
if (this.isNew) {
this.markModified(pathToMark);
} else {
priorVal || (priorVal = this.get(path));
if (!this.isDirectModified(pathToMark)) {
if (undefined === val && !this.isSelected(path)) {
// special case:
// when a path is not selected in a query its initial
// value will be undefined.
this.markModified(pathToMark, priorVal);
} else if (undefined === val && path in this._activePaths.states.default) {
// do nothing
// unsetting the default value which was never saved
} else if (!deepEqual(val, priorVal)) {
this.markModified(pathToMark, priorVal);
} else if (!constructing &&
null != val &&
path in this._activePaths.states.default &&
deepEqual(val, schema.getDefault(this, constructing))) {
// special case:
// a path with a default was $unset on the server
// and the user is setting it to the same value again
this.markModified(pathToMark, priorVal);
}
}
}
var obj = this._doc
, i = 0
, l = parts.length
for (; i < l; i++) {
var next = i + 1
, last = next === l;
if (last) {
obj[parts[i]] = val;
} else {
if (obj[parts[i]] && 'Object' === obj[parts[i]].constructor.name) {
obj = obj[parts[i]];
} else if (obj[parts[i]] && Array.isArray(obj[parts[i]])) {
obj = obj[parts[i]];
} else {
obj = obj[parts[i]] = {};
}
}
}
};
path
)Gets a raw value from a path (no getters)
show codeDocument.prototype.getValue = function (path) {
var parts = path.split('.')
, obj = this._doc
, part;
for (var i = 0, l = parts.length; i < l; i++) {
part = parts[i];
obj = obj.getValue
? obj.getValue(part) // If we have an embedded array document member
: obj[part];
if (!obj) return obj;
}
return obj;
}
path
<String> path
, value
)Sets a raw value for a path (no casting, setters, transformations)
show codeDocument.prototype.setValue = function (path, val) {
var parts = path.split('.')
, obj = this._doc;
for (var i = 0, len = parts.length-1; i < len; i++) {
obj = obj[parts[i]];
}
obj[parts[len]] = val;
return this;
};
path
, [type]
)Returns the value of a path.
show codeDocument.prototype.get = function (path, type) {
var adhocs;
if (type) {
adhocs = this._adhocPaths || (this._adhocPaths = {});
adhocs[path] = Schema.interpretAsType(path, type);
}
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 = null == obj ? null : obj[pieces[i]];
}
if (schema) {
obj = schema.applyGetters(obj, this);
}
return obj;
};
// path
doc.get('age') // 47
// dynamic casting to a string
doc.get('age', String) // "47"
path
)Returns the schematype for the given path
.
Document.prototype._path = function (path) {
var adhocs = this._adhocPaths
, adhocType = adhocs && adhocs[path];
if (adhocType) {
return adhocType;
} else {
return this.schema.path(path);
}
};
path
<String> path
)Marks the path as having pending changes to write to the db.
show codeDocument.prototype.markModified = function (path) {
this._activePaths.modify(path);
};
path
<String> the path to mark modifiedVery helpful when using Mixed types.
doc.mixed.type = 'changed';
doc.markModified('mixed.type');
doc.save() // changes to mixed.type are now persisted
fn
, scope
)Catches errors that occur during execution of fn and stores them to later be passed when save() is executed.
show codeDocument.prototype.try = function (fn, scope) {
var res;
try {
fn.call(scope);
res = true;
} catch (e) {
this._error(e);
res = false;
}
return res;
};
Returns the list of paths that have been modified.
show codeDocument.prototype.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('.'));
}, []));
}, []);
};
[path]
)Returns true if this document was modified, else false.
show codeDocument.prototype.isModified = function (path) {
return path
? !!~this.modifiedPaths().indexOf(path)
: this._activePaths.some('modify');
};
[path]
<String> optionalIf path
is given, checks if a path or any full path containing path
as part of its path chain has been modified.
doc.set('documents.0.title', 'changed');
doc.isModified('documents') // true
doc.isModified('documents.0.title') // true
doc.isDirectModified('documents') // false
path
)Returns true if path
was directly set and modified, else false.
Document.prototype.isDirectModified = function (path) {
return (path in this._activePaths.states.modify);
};
path
<String> doc.set('documents.0.title', 'changed');
doc.isDirectModified('documents.0.title') // true
doc.isDirectModified('documents') // false
path
)Checks if path
was initialized.
Document.prototype.isInit = function (path) {
return (path in this._activePaths.states.init);
};
path
<String> path
)Checks if path
was selected in the source query which initialized this document.
Document.prototype.isSelected = function isSelected (path) {
if (this._selected) {
if ('_id' === path) {
return 0 !== this._selected._id;
}
var paths = Object.keys(this._selected)
, i = paths.length
, inclusive = false
, cur
if (1 === i && '_id' === paths[0]) {
// only _id was selected.
return 0 === this._selected._id;
}
while (i--) {
cur = paths[i];
if ('_id' == cur) continue;
inclusive = !! this._selected[cur];
break;
}
if (path in this._selected) {
return inclusive;
}
i = paths.length;
var pathDot = path + '.';
while (i--) {
cur = paths[i];
if ('_id' == cur) continue;
if (0 === cur.indexOf(pathDot)) {
return inclusive;
}
if (0 === pathDot.indexOf(cur)) {
return inclusive;
}
}
return ! inclusive;
}
return true;
}
path
<String> Thing.findOne().select('name').exec(function (err, doc) {
doc.isSelected('name') // true
doc.isSelected('age') // false
})
cb
)Executes registered validation rules for this document.
show codeDocument.prototype.validate = function (cb) {
var self = this
// only validate required fields when necessary
var paths = Object.keys(this._activePaths.states.require).filter(function (path) {
if (!self.isSelected(path) && !self.isModified(path)) return false;
return true;
});
paths = paths.concat(Object.keys(this._activePaths.states.init));
paths = paths.concat(Object.keys(this._activePaths.states.modify));
if (0 === paths.length) {
complete();
return this;
}
var validating = {}
, total = 0;
paths.forEach(validatePath);
return this;
function validatePath (path) {
if (validating[path]) return;
validating[path] = true;
total++;
process.nextTick(function(){
var p = self.schema.path(path);
if (!p) return --total || complete();
p.doValidate(self.getValue(path), function (err) {
if (err) self.invalidate(path, err);
--total || complete();
}, self);
});
}
function complete () {
cb(self._validationError);
self._validationError = null;
}
};
cb
<Function> called after validation completes, passing an error if one occurreddoc.validate(function (err) {
if (err) handleError(err);
else // validation passed
});
path
, err
)Marks a path as invalid, causing validation to fail.
show codeDocument.prototype.invalidate = function (path, err) {
if (!this._validationError) {
this._validationError = new ValidationError(this);
}
if (!err || 'string' === typeof err) {
err = new ValidatorError(path, err);
}
this._validationError.errors[path] = err;
}
Resets the internal modified state of this document.
show codeDocument.prototype._reset = function reset () {
var self = this;
DocumentArray || (DocumentArray = require('./types/documentarray'));
this._activePaths
.map('init', 'modify', function (i) {
return self.getValue(i);
})
.filter(function (val) {
return (val && val instanceof DocumentArray && val.length);
})
.forEach(function (array) {
array.forEach(function (doc) {
doc._reset();
});
});
// clear atomics
this._dirty().forEach(function (dirt) {
var type = dirt.value;
if (type && type._atomics) {
type._atomics = {};
}
});
// Clear 'modify'('dirty') cache
this._activePaths.clear('modify');
var self = this;
this.schema.requiredPaths().forEach(function (path) {
self._activePaths.require(path);
});
return this;
}
Returns this documents dirty paths / vals.
show codeDocument.prototype._dirty = function _dirty () {
var self = this;
var all = this._activePaths.map('modify', function (path) {
return { path: path
, value: self.getValue(path)
, schema: self._path(path) };
});
// Sort dirty paths in a flat hierarchy.
all.sort(function (a, b) {
return (a.path < b.path ? -1 : (a.path > b.path ? 1 : 0));
});
// Ignore "foo.a" if "foo" is dirty already.
var minimal = []
, lastPath
, top;
all.forEach(function (item, i) {
if (item.path.indexOf(lastPath) !== 0) {
lastPath = item.path + '.';
minimal.push(item);
top = item;
} else {
if (!(item.value && top.value)) return;
// special case for top level MongooseArrays
if (top.value._atomics && top.value.hasAtomics()) {
// and the item is not a MongooseArray
if (!(item.value._atomics && item.value.hasAtomics())) {
// theres a sub path of top being explicitly set.
// the only way to honor all of their modifications
// is through a $set of entire array.
// change top to a $set op
top.value._atomics = {};
top.value._atomics.$set = top.value;
}
}
}
});
top = lastPath = null;
return minimal;
}
schema
)Assigns/compiles schema
into this documents prototype.
Document.prototype._setSchema = function (schema) {
compile(schema.tree, this);
this.schema = schema;
}
schema
<Schema> Register default hooks
show codeDocument.prototype._registerHooks = function _registerHooks () {
if (!this.save) return;
DocumentArray || (DocumentArray = require('./types/documentarray'));
this.pre('save', function (next) {
// we keep the error semaphore to make sure we don't
// call `save` unnecessarily (we only need 1 error)
var subdocs = 0
, error = false
, self = this;
// check for DocumentArrays
var arrays = this._activePaths
.map('init', 'modify', function (i) {
return self.getValue(i);
})
.filter(function (val) {
return (val && val instanceof DocumentArray && val.length);
});
if (!arrays.length)
return next();
arrays.forEach(function (array) {
subdocs += array.length;
array.forEach(function (value) {
if (!error)
value.save(function (err) {
if (!error) {
if (err) {
error = true;
next(err);
} else
--subdocs || next();
}
});
});
});
}, function (err) {
this.db.emit('error', err);
}).pre('save', function checkForExistingErrors (next) {
// if any doc.set() calls failed
if (this._saveError) {
next(this._saveError);
this._saveError = null;
} else {
next();
}
}).pre('save', function validation (next) {
return this.validate(next);
});
// add user defined queues
this._doQueue();
};
err
)Registers an error
show codeDocument.prototype._error = function (err) {
this._saveError = err;
return this;
};
err
<Error> Executes methods queued from the Schema definition
show codeDocument.prototype._doQueue = function () {
var q = this.schema && this.schema.callQueue;
if (q) {
for (var i = 0, l = q.length; i < l; i++) {
this[q[i][0]].apply(this, q[i][1]);
}
}
return this;
};
[options]
)Converts this document into a plain javascript object
show codeDocument.prototype.toObject = function (options) {
// When internally saving this document we always pass options,
// bypassing the custom schema options.
if (!(options && 'Object' == options.constructor.name)) {
options = this.schema.options.toObject
? clone(this.schema.options.toObject)
: {};
}
;('minimize' in options) || (options.minimize = this.schema.options.minimize);
var ret = clone(this._doc, options);
if (options.virtuals || options.getters && false !== options.virtuals) {
applyGetters(this, ret, 'virtuals', options);
}
if (options.getters) {
applyGetters(this, ret, 'paths', options);
}
return ret;
};
[options]
<Object> Available options
getters
apply all getters (path and virtual getters)virtuals
apply virtual getters (can override getters
option)minimize
remove empty objects (defaults to true)Example of only applying path getters
doc.toObject({ getters: true, virtuals: false })
Example of only applying virtual getters
doc.toObject({ virtuals: true })
Example of applying both path and virtual getters
doc.toObject({ getters: true })
options
)The return value of this method is used in calls to JSON.stringify(doc).
show codeDocument.prototype.toJSON = function (options) {
// check for object type since an array of documents
// being stringified passes array indexes instead
// of options objects. JSON.stringify([doc, doc])
if (!(options && 'Object' == options.constructor.name)) {
options = this.schema.options.toJSON
? clone(this.schema.options.toJSON)
: {};
}
options.json = true;
return this.toObject(options);
};
options
<Object> same options as Document#toObjectHelper for console.log
show codeDocument.prototype.inspect = function (options) {
var opts = options && 'Object' == options.constructor.name
? options
: undefined
return inspect(this.toObject(opts));
};
Helper for console.log
doc
)Returns true if the Document stores the same data as doc.
show codeDocument.prototype.equals = function (doc) {
var tid = this.get('_id');
var docid = doc.get('_id');
return tid.equals
? tid.equals(docid)
: tid === docid;
};
doc
<Document> a document to compareDocuments are considered equal when they have matching _id
s.
Hash containing current validation errors.
Boolean flag specifying if the document is new.
The documents schema.
A node-mongodb-native collection implementation.
show codefunction NativeCollection () {
this.collection = null;
MongooseCollection.apply(this, arguments);
}
All methods methods from the node-mongodb-native driver are copied and wrapped in queue management.
Called when the connection opens.
show codeNativeCollection.prototype.onOpen = function () {
var self = this;
if (this.collection) {
return MongooseCollection.prototype.onOpen.call(self);
}
if (!self.opts.size) {
// non-capped
return self.conn.db.collection(self.name, callback);
}
// capped
return self.conn.db.collection(self.name, function (err, c) {
if (err) return callback(err);
// discover if this collection exists and if it is capped
c.options(function (err, exists) {
if (err) return callback(err);
if (exists) {
if (exists.capped) {
callback(null, c);
} else {
var msg = 'A non-capped collection exists with this name.
'
+ ' To use this collection as a capped collection, please '
+ 'first convert it.
'
+ ' http://www.mongodb.org/display/DOCS/Capped+Collections#CappedCollections-Convertingacollectiontocapped'
err = new Error(msg);
callback(err);
}
} else {
// create
var opts = utils.clone(self.opts);
opts.capped = true;
self.conn.db.createCollection(self.name, opts, callback);
}
});
});
function callback (err, collection) {
if (err) {
// likely a strict mode error
self.conn.emit('error', err);
} else {
self.collection = collection;
MongooseCollection.prototype.onOpen.call(self);
}
};
};
Called when the connection closes
show codeNativeCollection.prototype.onClose = function () {
MongooseCollection.prototype.onClose.call(this);
};
callback
)Retreives information about this collections indexes.
callback
<Function> A node-mongodb-native connection implementation.
show codefunction NativeConnection() {
MongooseConnection.apply(this, arguments);
};
fn
)Opens the connection to MongoDB.
show codeNativeConnection.prototype.doOpen = function (fn) {
var server;
if (!this.db) {
server = new mongo.Server(this.host, Number(this.port), this.options.server);
this.db = new mongo.Db(this.name, server, this.options.db);
}
this.db.open(fn);
return this;
};
fn
<Function> fn
)Opens a connection to a MongoDB ReplicaSet.
show codeNativeConnection.prototype.doOpenSet = function (fn) {
if (!this.db) {
var servers = []
, ports = this.port
, self = this
this.host.forEach(function (host, i) {
servers.push(new mongo.Server(host, Number(ports[i]), self.options.server));
});
var server = new ReplSetServers(servers, this.options.replset);
this.db = new mongo.Db(this.name, server, this.options.db);
}
this.db.open(fn);
return this;
};
fn
<Function> See description of doOpen for server options. In this case options.replset
is also passed to ReplSetServers.
fn
)Closes the connection
show codeNativeConnection.prototype.doClose = function (fn) {
this.db.close();
if (fn) fn();
return this;
}
fn
<Function> Mongoose error
show codefunction MongooseError (msg) {
Error.call(this);
Error.captureStackTrace(this, arguments.callee);
this.message = msg;
this.name = 'MongooseError';
};
type
, value
)Casting Error constructor.
show codefunction CastError (type, value) {
MongooseError.call(this, 'Cast to ' + type + ' failed for value "' + value + '"');
Error.captureStackTrace(this, arguments.callee);
this.name = 'CastError';
this.type = type;
this.value = value;
};
msg
)Document Error
show codefunction DocumentError (msg) {
MongooseError.call(this, msg);
Error.captureStackTrace(this, arguments.callee);
this.name = 'DocumentError';
};
msg
<String> instance
)Document Validation Error
show codefunction ValidationError (instance) {
MongooseError.call(this, "Validation failed");
Error.captureStackTrace(this, arguments.callee);
this.name = 'ValidationError';
this.errors = instance.errors = {};
};
instance
<Document> Console.log helper
show codeValidationError.prototype.toString = function () {
return this.name + ': ' + Object.keys(this.errors).map(function (key) {
return String(this.errors[key]);
}, this).join(', ');
};
path
, msg
)Schema validator error
show codefunction ValidatorError (path, type) {
var msg = type
? '"' + type + '" '
: '';
MongooseError.call(this, 'Validator ' + msg + 'failed for path ' + path);
Error.captureStackTrace(this, arguments.callee);
this.name = 'ValidatorError';
this.path = path;
this.type = type;
};
Mongoose constructor.
show codefunction Mongoose () {
this.connections = [];
this.plugins = [];
this.models = {};
this.modelSchemas = {};
this.options = {};
this.createConnection(); // default connection
};
The exports object of the mongoose
module is an instance of this class.
Most apps will only use this one instance.
key
, value
)Sets mongoose options
show codeMongoose.prototype.set = function (key, value) {
if (arguments.length == 1)
return this.options[key];
this.options[key] = value;
return this;
};
mongoose.set('test', value) // sets the 'test' option to `value`
key
)Gets mongoose options
key
<String> mongoose.get('test') // returns the 'test' value
[uri]
)Creates a Connection instance.
show codeMongoose.prototype.createConnection = function () {
var conn = new Connection(this);
this.connections.push(conn);
if (arguments.length) {
if (rgxReplSet.test(arguments[0])) {
conn.openSet.apply(conn, arguments);
} else {
conn.open.apply(conn, arguments);
}
}
return conn;
};
[uri]
<String> a mongodb:// URI// with mongodb:// URI
db = mongoose.createConnection('mongodb://localhost:port/database');
// with [host, database_name[, port] signature
db = mongoose.createConnection('localhost', 'database', port)
// initialize now, connect later
db = mongoose.createConnection();
db.open('localhost', 'database', port);
Opens the default mongoose connection.
show codeMongoose.prototype.connect = function () {
var conn = this.connection;
if (rgxReplSet.test(arguments[0])) {
conn.openSet.apply(conn, arguments);
} else {
conn.open.apply(conn, arguments);
}
return this;
};
If arguments are passed, they are proxied to either Connection#open or Connection#openSet appropriately.
[fn]
)Disconnects all connections.
show codeMongoose.prototype.disconnect = function (fn) {
var count = this.connections.length
, error
this.connections.forEach(function(conn){
conn.close(function(err){
if (error) return;
if (err) {
error = err;
if (fn) return fn(err);
throw err;
}
if (fn)
--count || fn();
});
});
return this;
};
[fn]
<Function> called after all connection close.name
, [schema]
, [collection]
, [skipInit]
)Defines a model or retrieves it.
show codeMongoose.prototype.model = function (name, schema, collection, skipInit) {
// normalize collection
if (!(schema instanceof Schema)) {
collection = schema;
schema = false;
}
if ('boolean' === typeof collection) {
skipInit = collection;
collection = null;
}
// look up models for the collection
if (!this.modelSchemas[name]) {
if (!schema && name in SchemaDefaults) {
schema = SchemaDefaults[name];
}
if (schema) {
this.modelSchemas[name] = schema;
for (var i = 0, l = this.plugins.length; i < l; i++) {
schema.plugin(this.plugins[i][0], this.plugins[i][1]);
}
} else {
throw new Error('Schema hasn\'t been registered for model "' + name + '".
'
+ 'Use mongoose.model(name, schema)');
}
}
if (!this.models[name]) {
schema || (schema = this.modelSchemas[name]);
collection || (collection = schema.set('collection') || format(name));
var model = Model.compile(name
, this.modelSchemas[name]
, collection
, this.connection
, this);
if (!skipInit) model.init();
this.models[name] = model;
}
return this.models[name];
};
Models defined on the mongoose
instance are available to all connection created by the same mongoose
instance.
var mongoose = require('mongoose');
// define an Actor model with this mongoose instance
mongoose.model('Actor', new Schema({ name: String }));
// create a new connection
var conn = mongoose.createConnection(..);
// retrieve the Actor model
var Actor = conn.model('Actor');
When no collection
argument is passed, Mongoose produces a collection name by passing the model name
to the utils.toCollectionName method. This method pluralizes the name. Collection names can also be declared through schema options.
var schema = new Schema({ name: String });
schema.set('collection', 'actor');
fn
, [opts]
)Declares a global plugin executed on all Schemas.
show codeMongoose.prototype.plugin = function (fn, opts) {
this.plugins.push([fn, opts]);
return this;
};
Equivalent to calling .plugin(fn)
on each Schema you create.
The exports object is an instance of Mongoose.
show codemodule.exports = exports = new Mongoose;
var mongoose = module.exports;
Mongoose version
show codemongoose.version = JSON.parse(
require('fs').readFileSync(__dirname + '/../package.json', 'utf8')
).version;
The Mongoose constructor
show codemongoose.Mongoose = Mongoose;
The exports of the mongoose module is an instance of this class.
var mongoose = require('mongoose');
var mongoose2 = new mongoose.Mongoose();
The Mongoose Schema constructor
show codemongoose.Schema = Schema;
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CatSchema = new Schema(..);
The Mongoose VirtualType constructor.
show codemongoose.VirtualType = VirtualType;
The various Mongoose Types.
show codemongoose.Types = Types;
var mongoose = require('mongoose');
var array = mongoose.Types.Array;
Using this exposed access to the ObjectId
type, we can construct ids on demand.
var ObjectId = mongoose.Types.ObjectId;
var id1 = new ObjectId;
The node-mongodb-native driver Mongoose uses.
show codemongoose.mongo = require('mongodb');
The default connection of the mongoose module.
var mongoose = require('mongoose');
mongoose.connect(...);
mongoose.connection.on('error', cb);
This is the connection used by default for every model created using mongoose.model.
doc
)Model constructor
show codefunction Model (doc, fields, skipId) {
Document.call(this, doc, fields, skipId);
};
doc
<Object> values to with which to create the documentquery
)Returns what paths can be populated
show codeModel.prototype._getPopulationKeys = function getPopulationKeys (query) {
if (!(query && query.options.populate)) return;
var names = Object.keys(query.options.populate)
, n = names.length
, name
, paths = {}
, hasKeys
, schema
while (n--) {
name = names[n];
schema = this.schema.path(name);
hasKeys = true;
if (!schema) {
// if the path is not recognized, it's potentially embedded docs
// walk path atoms from right to left to find a matching path
var pieces = name.split('.')
, i = pieces.length;
while (i--) {
var path = pieces.slice(0, i).join('.')
, pathSchema = this.schema.path(path);
// loop until we find an array schema
if (pathSchema && pathSchema.caster) {
if (!paths[path]) {
paths[path] = { sub: {} };
}
paths[path].sub[pieces.slice(i).join('.')] = query.options.populate[name];
hasKeys || (hasKeys = true);
break;
}
}
} else {
paths[name] = query.options.populate[name];
hasKeys || (hasKeys = true);
}
}
return hasKeys && paths;
};
query
<Query> objectschema
, oid
, query
, fn
)Populates an object
show codeModel.prototype._populate = function populate (schema, oid, query, fn) {
if (!Array.isArray(oid)) {
var conditions = query.conditions || {};
conditions._id = oid;
return this
.db.model(query.model || schema.options.ref)
.findOne(conditions, query.fields, query.options, fn);
}
if (!oid.length) {
return fn(null, oid);
}
var model = this.db.model(query.model || schema.caster.options.ref)
, conditions = query && query.conditions || {};
conditions._id || (conditions._id = { $in: oid });
model.find(conditions, query.fields, query.options, function (err, docs) {
if (err) return fn(err);
// user specified sort order?
if (query.options && query.options.sort) {
return fn(null, docs);
}
// put back in original id order (using a hash reduces complexity from n*n to 2n)
var docHash = {};
docs.forEach(function (doc) {
docHash[doc._id] = doc;
});
var arr = [];
oid.forEach(function (id) {
if (id in docHash) arr.push(docHash[id]);
});
fn(null, arr);
});
};
schema
<SchemaType> type for the oidoid
<Object> object id or array of object idsquery
<Object> object specifying query conditions, fields, and optionsfn
<Function> doc
, query
, fn
)Performs auto-population of relations.
show codeModel.prototype.init = function init (doc, query, fn) {
if ('function' == typeof query) {
fn = query;
query = null;
}
var populate = this._getPopulationKeys(query);
if (!populate) {
return Document.prototype.init.call(this, doc, fn);
}
// population from other models is necessary
var self = this;
init(doc, '', function (err) {
if (err) return fn(err);
Document.prototype.init.call(self, doc, fn);
});
return this;
function init (obj, prefix, fn) {
prefix = prefix || '';
var keys = Object.keys(obj)
, len = keys.length;
function next () {
if (--len < 0) return fn();
var i = keys[len]
, path = prefix + i
, schema = self.schema.path(path)
, total = 0
, poppath
if (!schema && obj[i] && 'Object' === obj[i].constructor.name) {
// assume nested object
return init(obj[i], path + '.', next);
}
if (!(obj[i] && schema && populate[path])) return next();
// this query object is re-used and passed around. we clone
// it to prevent query condition contamination between
// one populate call to the next.
poppath = utils.clone(populate[path]);
if (poppath.sub) {
obj[i].forEach(function (subobj) {
var pkeys = Object.keys(poppath.sub)
, pi = pkeys.length
, key
while (pi--) {
key = pkeys[pi];
if (subobj[key]) (function (key) {
total++;
self._populate(schema.schema.path(key), subobj[key], poppath.sub[key], done);
function done (err, doc) {
if (err) return error(err);
subobj[key] = doc;
--total || next();
}
})(key);
}
});
if (0 === total) return next();
} else {
self._populate(schema, obj[i], poppath, function (err, doc) {
if (err) return error(err);
obj[i] = doc;
next();
});
}
};
next();
};
function error (err) {
if (error.err) return;
fn(error.err = err);
}
};
[fn]
)Saves this document.
show codeModel.prototype.save = function save (fn) {
var promise = new Promise(fn)
, complete = handleSave(promise, this)
, options = {}
if (this.schema.options.safe) {
options.safe = this.schema.options.safe;
}
if (this.isNew) {
// send entire doc
var obj = this.toObject({ depopulate: 1 });
this._version(true, obj);
this.collection.insert(obj, options, complete);
this._reset();
this.isNew = false;
this.emit('isNew', false);
// Make it possible to retry the insert
this._inserting = true;
} else {
// Make sure we don't treat it as a new object on error,
// since it already exists
this._inserting = false;
var delta = this._delta();
if (delta) {
var where = this._where(delta[0]);
this.collection.update(where, delta[1], options, complete);
} else {
complete(null);
}
this._reset();
this.emit('isNew', false);
}
};
[fn]
<Function> optional callbackProduces a special query document of the modified properties used in updates.
show codeModel.prototype._delta = function _delta () {
var dirty = this._dirty();
if (!dirty.length) return;
var self = this
, where = {}
, delta = {}
, len = dirty.length
, d = 0
, val
, obj
for (; d < len; ++d) {
var data = dirty[d]
var value = data.value
var schema = data.schema
if (undefined === value) {
operand(self, where, delta, data, 1, '$unset');
} else if (null === value) {
operand(self, where, delta, data, null);
} else if (value._path && value._registerAtomic) {
handleMongooseArray(self, where, delta, data, value);
} else if (value._path && value.write && value.toObject) {
// MongooseBuffer
value = value.toObject();
operand(self, where, delta, data, value);
} else {
// nested object literal
value = utils.clone(value);
operand(self, where, delta, data, value);
}
}
if (this.__version) {
this._version(where, delta);
}
return [where, delta];
}
Appends versioning to the where and update clauses.
show codeModel.prototype._version = function _version (where, delta) {
var key = this.schema.options.versionKey;
if (true === where) {
// this is an insert
if (key) this.setValue(key, delta[key] = 0);
return;
}
// updates
// only apply versioning if our versionKey was selected. else
// there is no way to select the correct version. we could fail
// fast here and force them to include the versionKey but
// thats a bit intrusive. can we do this automatically?
// TODO fail fast option?
if (!this.isSelected(key)) {
return;
}
// $push $addToSet don't need the where clause set
if (VERSION_WHERE === (VERSION_WHERE & this.__version)) {
where[key] = this.getValue(key);
}
if (VERSION_INC === (VERSION_INC & this.__version)) {
delta.$inc || (delta.$inc = {});
delta.$inc[key] = 1;
}
}
Signal that we desire an increment of
this documents version.
Model.prototype.increment = function increment () {
this.__version = VERSION_ALL;
return this;
}
Returns a query object which applies shardkeys if they exist.
show codeModel.prototype._where = function _where (where) {
where || (where = {});
var paths
, len
if (this._shardval) {
paths = Object.keys(this._shardval)
len = paths.length
for (var i = 0; i < len; ++i) {
where[paths[i]] = this._shardval[paths[i]];
}
}
where._id = this._doc._id;
return where;
}
[fn]
)Remove the document
show codeModel.prototype.remove = function remove (fn) {
if (this._removing) return this;
var promise = this._removing = new Promise(fn)
, where = this._where()
, self = this
, options = {}
if (this.schema.options.safe) {
options.safe = this.schema.options.safe;
}
this.collection.remove(where, options, tick(function (err) {
if (err) {
promise.error(err);
promise = self = self._removing = where = options = null;
return;
}
promise.complete();
self.emit('remove', self);
promise = self = where = options = null;
}));
return this;
};
[fn]
<Function> optional callbackRegister hooks override
show codeModel.prototype._registerHooks = function registerHooks () {
Document.prototype._registerHooks.call(this);
};
name
)Shortcut to access another model.
show codeModel.prototype.model = function model (name) {
return this.db.model(name);
};
name
<String> model nameDocument schema
show codeModel.schema;
Database instance the model uses.
show codeModel.db;
Collection the model uses.
show codeModel.collection;
Base Mongoose instance for the model.
show codeModel.base;
Sometimes you need to query for things in mongodb using a JavaScript
expression. You can do so via find({$where: javascript}), or you can
use the mongoose shortcut method $where via a Query chain or from
your mongoose Model.
Model.$where = function $where () {
var q = new Query().bind(this, 'find');
return q.$where.apply(q, arguments);
};
Called when the model compiles
show codeModel.init = function init () {
if (this.schema.options.autoIndex)
this.ensureIndexes();
this.schema.emit('init', this);
};
[cb]
)Sends ensureIndex
commands to mongo for each index declared in the schema.
Model.ensureIndexes = function ensureIndexes (cb) {
var indexes = this.schema.indexes();
if (!indexes.length) {
return cb && cb();
}
var self = this
, safe = self.schema.options.safe
, count = indexes.length
, error
indexes.forEach(function (index) {
var options = index[1];
options.safe = safe;
self.collection.ensureIndex(index[0], options, tick(function (err) {
if (err) error = err;
if (--count) return;
self.emit('index', error);
cb && cb(error);
}));
});
}
[cb]
<Function> optional callbackconditions
, [callback]
)Removes documents from the collection.
show codeModel.remove = function remove (conditions, callback) {
if ('function' === typeof conditions) {
callback = conditions;
conditions = {};
}
var query = new Query(conditions).bind(this, 'remove');
if ('undefined' === typeof callback)
return query;
this._applyNamedScope(query);
return query.remove(callback);
};
conditions
, [fields]
, [options]
, [callback]
)Finds documents
show codeModel.find = function find (conditions, fields, options, callback) {
if ('function' == typeof conditions) {
callback = conditions;
conditions = {};
fields = null;
options = null;
} else if ('function' == typeof fields) {
callback = fields;
fields = null;
options = null;
} else if ('function' == typeof options) {
callback = options;
options = null;
}
var query = new Query(conditions, options);
query.bind(this, 'find');
query.select(fields);
if ('undefined' === typeof callback)
return query;
this._applyNamedScope(query);
return query.find(callback);
};
// retrieve only certain keys
MyModel.find({ name: /john/i }, 'name friends', function () { })
// pass options
MyModel.find({ name: /john/i }, null, { skip: 10 } )
query
)Merges the current named scope query into query
.
Model._applyNamedScope = function _applyNamedScope (query) {
var cQuery = this._cumulativeQuery;
if (cQuery) {
merge(query._conditions, cQuery._conditions);
if (query._fields && cQuery._fields)
merge(query._fields, cQuery._fields);
if (query.options && cQuery.options)
merge(query.options, cQuery.options);
delete this._cumulativeQuery;
}
return query;
}
query
<Query> id
, [fields]
, [options]
, [callback]
)Finds by id
show codeModel.findById = function findById (id, fields, options, callback) {
return this.findOne({ _id: id }, fields, options, callback);
};
conditions
, [fields]
, [options]
, [callback]
)Finds one document
show codeModel.findOne = function findOne (conditions, fields, options, callback) {
if ('function' == typeof options) {
// TODO Handle all 3 of the following scenarios
// Hint: Only some of these scenarios are possible if cQuery is present
// Scenario: findOne(conditions, fields, callback);
// Scenario: findOne(fields, options, callback);
// Scenario: findOne(conditions, options, callback);
callback = options;
options = null;
} else if ('function' == typeof fields) {
// TODO Handle all 2 of the following scenarios
// Scenario: findOne(conditions, callback)
// Scenario: findOne(fields, callback)
// Scenario: findOne(options, callback);
callback = fields;
fields = null;
options = null;
} else if ('function' == typeof conditions) {
callback = conditions;
conditions = {};
fields = null;
options = null;
}
var query = new Query(conditions, options).select(fields).bind(this, 'findOne');
if ('undefined' == typeof callback)
return query;
this._applyNamedScope(query);
return query.findOne(callback);
};
conditions
, [callback]
)Counts documents
show codeModel.count = function count (conditions, callback) {
if ('function' === typeof conditions)
callback = conditions, conditions = {};
var query = new Query(conditions).bind(this, 'count');
if ('undefined' == typeof callback)
return query;
this._applyNamedScope(query);
return query.count(callback);
};
field
, [conditions]
, [callback]
)Executes a DISTINCT command
show codeModel.distinct = function distinct (field, conditions, callback) {
var query = new Query(conditions).bind(this, 'distinct');
if ('undefined' == typeof callback) {
query._distinctArg = field;
return query;
}
this._applyNamedScope(query);
return query.distinct(field, callback);
};
path
, [val]
)Creates a Query, applies the passed conditions, and returns the Query.
show codeModel.where = function where (path, val) {
var q = new Query().bind(this, 'find');
return q.where.apply(q, 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
[conditions]
, [update]
, [options]
, [callback]
)Issues a mongodb findAndModify update command.
show codeModel.findOneAndUpdate = function (conditions, update, options, callback) {
if ('function' == typeof options) {
callback = options;
options = null;
}
else if (1 === arguments.length) {
if ('function' == typeof conditions) {
var msg = 'Model.findOneAndUpdate(): First argument must not be a function.
'
+ ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options, callback)
'
+ ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options)
'
+ ' ' + this.modelName + '.findOneAndUpdate(conditions, update)
'
+ ' ' + this.modelName + '.findOneAndUpdate(update)
'
+ ' ' + this.modelName + '.findOneAndUpdate()
';
throw new TypeError(msg)
}
update = conditions;
conditions = undefined;
}
var fields;
if (options && options.fields) {
fields = options.fields;
options.fields = undefined;
}
var query = new Query(conditions, options);
query.select(fields);
query.bind(this, 'findOneAndUpdate', update);
if ('undefined' == typeof callback)
return query;
this._applyNamedScope(query);
return query.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 ifcallback
is passed else a Query object is returned.
new
: bool - true to return the modified document rather than the original. defaults to trueupsert
: bool - creates the object if it doesn't exist. defaults to false.sort
: if multiple docs are found by the conditions, sets the sort order to choose which doc to updateA.findOneAndUpdate(conditions, update, options, callback) // executes
A.findOneAndUpdate(conditions, update, options) // returns Query
A.findOneAndUpdate(conditions, update, callback) // executes
A.findOneAndUpdate(conditions, update) // returns Query
A.findOneAndUpdate() // returns Query
id
, [update]
, [options]
, [callback]
)Issue a mongodb findAndModify update command by a documents id.
show codeModel.findByIdAndUpdate = function (id, update, options, callback) {
var args;
if (1 === arguments.length) {
if ('function' == typeof id) {
var msg = 'Model.findByIdAndUpdate(): First argument must not be a function.
'
+ ' ' + this.modelName + '.findByIdAndUpdate(id, callback)
'
+ ' ' + this.modelName + '.findByIdAndUpdate(id)
'
+ ' ' + this.modelName + '.findByIdAndUpdate()
';
throw new TypeError(msg)
}
return this.findOneAndUpdate({_id: id }, undefined);
}
args = utils.args(arguments, 1);
args.unshift({ _id: id });
return this.findOneAndUpdate.apply(this, args);
}
conditions
, [options]
, [callback]
)Issue a mongodb findAndModify remove command.
show codeModel.findOneAndRemove = function (conditions, options, callback) {
if (1 === arguments.length && 'function' == typeof conditions) {
var msg = 'Model.findOneAndRemove(): First argument must not be a function.
'
+ ' ' + this.modelName + '.findOneAndRemove(conditions, callback)
'
+ ' ' + this.modelName + '.findOneAndRemove(conditions)
'
+ ' ' + this.modelName + '.findOneAndRemove()
';
throw new TypeError(msg)
}
if ('function' == typeof options) {
callback = options;
options = undefined;
}
var fields;
if (options) {
fields = options.select;
options.select = undefined;
}
var query = new Query(conditions, options);
query.bind(this, 'findOneAndRemove');
query.select(fields);
if ('undefined' == typeof callback)
return query;
this._applyNamedScope(query);
return query.findOneAndRemove(callback);
}
Finds a matching document, removes it, passing the found
document (if any) to the callback. Executes immediately if callback
is passed else a Query object is returned.
sort
: if multiple docs are found by the conditions, sets the sort order to choose which doc to updateA.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
id
, [options]
, [callback]
)findByIdAndRemove
show codeModel.findByIdAndRemove = function (id, options, callback) {
if (1 === arguments.length && 'function' == typeof id) {
var msg = 'Model.findByIdAndRemove(): First argument must not be a function.
'
+ ' ' + this.modelName + '.findByIdAndRemove(id, callback)
'
+ ' ' + this.modelName + '.findByIdAndRemove(id)
'
+ ' ' + this.modelName + '.findByIdAndRemove()
';
throw new TypeError(msg)
}
return this.findOneAndRemove({ _id: id }, options, callback);
}
Issue a mongodb findAndModify remove command by a documents id.
doc
, fn
)Shortcut for creating a new Document that is automatically saved to the db if valid.
show codeModel.create = function create (doc, fn) {
if (1 === arguments.length) {
return 'function' === typeof doc && doc(null);
}
var self = this
, docs = [null]
, promise
, count
, args
if (Array.isArray(doc)) {
args = doc;
} else {
args = utils.args(arguments, 0, arguments.length - 1);
fn = arguments[arguments.length - 1];
}
if (0 === args.length) return fn(null);
promise = new Promise(fn);
count = args.length;
args.forEach(function (arg, i) {
var doc = new self(arg);
docs[i+1] = doc;
doc.save(function (err) {
if (err) return promise.error(err);
--count || fn.apply(null, docs);
});
});
// TODO
// utilize collection.insertAll for batch processing?
};
conditions
, doc
, [options]
, [callback]
)Updates documents.
show codeModel.update = function update (conditions, doc, options, callback) {
if (arguments.length < 4) {
if ('function' === typeof options) {
// Scenario: update(conditions, doc, callback)
callback = options;
options = null;
} else if ('function' === typeof doc) {
// Scenario: update(doc, callback);
callback = doc;
doc = conditions;
conditions = {};
options = null;
}
}
var query = new Query(conditions, options).bind(this, 'update', doc);
if ('undefined' == typeof callback)
return query;
this._applyNamedScope(query);
return query.update(doc, callback);
};
MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn);
MyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, fn);
o
, callback
)Executes a mapReduce command.
show codeModel.mapReduce = function mapReduce (o, callback) {
if ('function' != typeof callback) throw new Error('missing callback');
var self = this;
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 };
o.map = String(o.map);
o.reduce = String(o.reduce);
this.collection.mapReduce(null, null, o, function (err, ret, stats) {
if (err) return callback(err);
if (ret.findOne && ret.mapReduce) {
// returned a collection, convert to Model
var model = Model.compile(
'_mapreduce_' + ret.collectionName
, Model.mapReduce.schema
, ret.collectionName
, self.db
, self.base);
model._mapreduce = true;
return callback(err, model, stats);
}
callback(err, 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.
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)
})
query
{Object} query filter object.limit
{Number} max number of documentskeeptemp
{Boolean, default:false} keep temporary datafinalize
{Function} finalize functionscope
{Object} scope variables exposed to map/reduce/finalize during executionjsMode
{Boolean, default:false} it is possible to make the execution stay in JS. Provided in MongoDB > 2.0.Xverbose
{Boolean, default:false} provide statistics on job execution time.out*
{Object, default: {inline:1}} sets the output target for the map reduce job.{inline:1}
the results are returned in an array{replace: 'collectionName'}
add the results to collectionName: the results replace the collection{reduce: 'collectionName'}
add the results to collectionName: if dups are detected, uses the reducer / finalize functions{merge: 'collectionName'}
add the results to collectionName: if dups exist the new docs overwrite the oldIf options.out
is set to replace
, merge
, or reduce
, a Model instance is returned that can be used for further querying. Queries run against this model are all executed with the lean
option; meaning only the js object is returned and no Mongoose magic is applied (getters, setters, etc).
var o = {};
o.map = function () { emit(this.name, 1) }
o.reduce = function (k, vals) { return vals.length }
o.out = { replace: 'createdCollectionNameForResults' }
o.verbose = true;
User.mapReduce(o, function (err, model, stats) {
console.log('map reduce took %d ms', stats.processtime)
model.find().where('value').gt(10).exec(function (err, docs) {
console.log(docs);
});
})
The name of the model
Collection the model uses.
Connection the model uses.
target
, getters
)Decorate
show codeNamedScope.prototype.decorate = function (target, getters) {
var name = this.name
, block = this.block
, query = this.query;
if (block) {
if (block.length === 0) {
Object.defineProperty(target, name, {
get: getters.block0(block)
});
} else {
target[name] = getters.blockN(block);
}
} else {
Object.defineProperty(target, name, {
get: getters.basic(query)
});
}
};
NamedScope.prototype.compile = function (model) {
var allScopes = this.scopesByName
, scope;
for (var k in allScopes) {
scope = allScopes[k];
scope.decorate(model, {
block0: function (block) {
return function () {
var cquery = this._cumulativeQuery || (this._cumulativeQuery = new Query().bind(this));
block.call(cquery);
return this;
};
},
blockN: function (block) {
return function () {
var cquery = this._cumulativeQuery || (this._cumulativeQuery = new Query().bind(this));
block.apply(cquery, arguments);
return this;
};
},
basic: function (query) {
return function () {
var cquery = this._cumulativeQuery || (this._cumulativeQuery = new Query().bind(this));
cquery.find(query);
return this;
};
}
});
}
};
module.exports = NamedScope;
target
<NamedScope> getters
<Object> back
)Promise constructor.
show codefunction Promise (back) {
this.emitted = {};
if ('function' == typeof back)
this.addBack(back);
};
back
<Function> a callback+errback that accepts `fn(err, ...){}` as signatureevent
, callback
)Adds listener
to the event
.
Promise.prototype.on = function (event, callback) {
if (this.emitted[event])
callback.apply(this, this.emitted[event]);
else
EventEmitter.prototype.on.call(this, event, callback);
return this;
};
If event
is either error
or complete
and the event has already been emitted, thelistener
is called immediately and passed the results of the original emitted event.
Keeps track of emitted events to run them on on
.
Promise.prototype.emit = function (event) {
// ensures a promise can't be complete() or error() twice
if (event == 'err' || event == 'complete'){
if (this.emitted.err || this.emitted.complete) {
return this;
}
this.emitted[event] = util.args(arguments, 1);
}
return EventEmitter.prototype.emit.apply(this, arguments);
};
Shortcut for emitting the complete
event.
Promise.prototype.complete = function () {
var args = util.args(arguments);
return this.emit.apply(this, ['complete'].concat(args));
};
Shortcut for emitting the err
event.
Promise.prototype.error = function (err) {
if (!(err instanceof Error)) err = new Error(err);
return this.emit('err', err);
};
Shortcut for .on('complete', fn)
.
Promise.prototype.addCallback = function (fn) {
return this.on('complete', fn);
};
Shortcut for .on('err', fn)
.
Promise.prototype.addErrback = function (fn) {
return this.on('err', fn);
};
fn
)Adds a single function that's both a callback and errback.
show codePromise.prototype.addBack = function (fn) {
this.on('err', function(err){
fn.call(this, err);
});
this.on('complete', function(){
var args = util.args(arguments);
fn.apply(this, [null].concat(args));
});
return this;
};
fn
<Function> err
, val
)Sugar for handling cases where you may be resolving to either an error condition or a success condition.
show codePromise.prototype.resolve = function (err, val) {
if (err) return this.error(err);
return this.complete(val);
};
criteria
, options
)Query constructor
show codefunction Query (criteria, options) {
this.setOptions(options, true);
this._conditions = {};
this._updateArg = {};
if (criteria) this.find(criteria);
}
options
)Sets query options.
show codeQuery.prototype.setOptions = function (options, overwrite) {
// overwrite is internal use only
if (overwrite) {
options = this.options = options || {};
this.safe = options.safe
// normalize population options
var pop = this.options.populate;
this.options.populate = {};
if (pop && Array.isArray(pop)) {
for (var i = 0, l = pop.length; i < l; i++) {
this.options.populate[pop[i]] = {};
}
}
return this;
}
if (!(options && 'Object' == options.constructor.name))
return this;
if ('safe' in options)
this.safe = options.safe;
// set arbitrary options
var methods = Object.keys(options)
, i = methods.length
, method
while (i--) {
method = methods[i];
// use methods if exist (safer option manipulation)
if ('function' == typeof this[method]) {
var args = Array.isArray(options[method])
? options[method]
: [options[method]];
this[method].apply(this, args)
} else {
this.options[method] = options[method];
}
}
return this;
}
options
<Object> model
, op
, updateArg
)Binds this query to a model.
show codeQuery.prototype.bind = function bind (model, op, updateArg) {
this.model = model;
this.op = op;
if (model._mapreduce) this.options.lean = true;
if (op == 'update' || op == 'findOneAndUpdate') {
merge(this._updateArg, updateArg || {});
}
return this;
};
[operation]
, [callback]
)Executes the query
show codeQuery.prototype.exec = function exec (op, callback) {
var promise = new Promise();
switch (typeof op) {
case 'function':
callback = op;
op = null;
break;
case 'string':
this.op = op;
break;
}
if (callback) promise.addBack(callback);
if (!this.op) {
promise.complete();
return promise;
}
if ('update' == this.op) {
this[this.op](this._updateArg, promise.resolve.bind(promise));
return promise;
}
if ('distinct' == this.op) {
this.distinct(this._distinctArg, promise.resolve.bind(promise));
return promise;
}
this[this.op](promise.resolve.bind(promise));
return promise;
};
query.exec();
query.exec(callback);
query.exec('update');
query.exec('find', callback);
[criteria]
, [callback]
)Finds documents.
show codeQuery.prototype.find = function (criteria, callback) {
this.op = 'find';
if ('function' === typeof criteria) {
callback = criteria;
criteria = {};
} else if (criteria instanceof Query) {
// TODO Merge options, too
merge(this._conditions, criteria._conditions);
} else if (criteria instanceof Document) {
merge(this._conditions, criteria.toObject());
} else if (criteria && 'Object' === criteria.constructor.name) {
merge(this._conditions, criteria);
}
if (!callback) return this;
return this.execFind(callback);
};
When no callback
is passed, the query is not executed.
query.find().find({ name: 'Los Pollos' }).find(callback)
model
, [obj]
)Casts this query to the schema of model
Query.prototype.cast = function (model, obj) {
obj || (obj= this._conditions);
var schema = model.schema
, paths = Object.keys(obj)
, i = paths.length
, any$conditionals
, schematype
, nested
, path
, type
, val;
while (i--) {
path = paths[i];
val = obj[path];
if ('$or' === path || '$nor' === path) {
var k = val.length
, orComponentQuery;
while (k--) {
orComponentQuery = new Query(val[k]);
orComponentQuery.cast(model);
val[k] = orComponentQuery._conditions;
}
} else if (path === '$where') {
type = typeof val;
if ('string' !== type && 'function' !== type) {
throw new Error("Must have a string or function for $where");
}
if ('function' === type) {
obj[path] = val.toString();
}
continue;
} else {
if (!schema) {
// no casting for Mixed types
continue;
}
schematype = schema.path(path);
if (!schematype) {
// Handle potential embedded array queries
var split = path.split('.')
, j = split.length
, pathFirstHalf
, pathLastHalf
, remainingConds
, castingQuery;
// Find the part of the var path that is a path of the Schema
while (j--) {
pathFirstHalf = split.slice(0, j).join('.');
schematype = schema.path(pathFirstHalf);
if (schematype) break;
}
// If a substring of the input path resolves to an actual real path...
if (schematype) {
// Apply the casting; similar code for $elemMatch in schema/array.js
if (schematype.caster && schematype.caster.schema) {
remainingConds = {};
pathLastHalf = split.slice(j).join('.');
remainingConds[pathLastHalf] = val;
castingQuery = new Query(remainingConds);
castingQuery.cast(schematype.caster);
obj[path] = castingQuery._conditions[pathLastHalf];
} else {
obj[path] = val;
}
}
} else if (val === null || val === undefined) {
continue;
} else if ('Object' === val.constructor.name) {
any$conditionals = Object.keys(val).some(function (k) {
return k.charAt(0) === '$' && k !== '$id' && k !== '$ref';
});
if (!any$conditionals) {
obj[path] = schematype.castForQuery(val);
} else {
var ks = Object.keys(val)
, k = ks.length
, $cond;
while (k--) {
$cond = ks[k];
nested = val[$cond];
if ('$exists' === $cond) {
if ('boolean' !== typeof nested) {
throw new Error("$exists parameter must be Boolean");
}
continue;
}
if ('$type' === $cond) {
if ('number' !== typeof nested) {
throw new Error("$type parameter must be Number");
}
continue;
}
if ('$not' === $cond) {
this.cast(model, nested);
} else {
val[$cond] = schematype.castForQuery($cond, nested);
}
}
}
} else {
obj[path] = schematype.castForQuery(val);
}
}
}
return obj;
};
If obj
is present, it is cast instead of this query.
model
)Returns default options.
show codeQuery.prototype._optionsForExec = function (model) {
var options = utils.clone(this.options, { retainKeyOrder: true });
delete options.populate;
if (! ('safe' in options)) options.safe = model.schema.options.safe;
return options;
};
model
<Model> Applies schematype selected options to this query.
show codeQuery.prototype._applyPaths = function applyPaths () {
// determine if query is selecting or excluding fields
var fields = this._fields
, exclude
, keys
, ki
if (fields) {
keys = Object.keys(fields);
ki = keys.length;
while (ki--) {
if ('+' == keys[ki][0]) continue;
exclude = 0 === fields[keys[ki]];
break;
}
}
// if selecting, apply default schematype select:true fields
// if excluding, apply schematype select:false fields
var selected = []
, excluded = []
, seen = [];
analyzeSchema(this.model.schema);
switch (exclude) {
case true:
excluded.length && this.select('-' + excluded.join(' -'));
break;
case false:
selected.length && this.select(selected.join(' '));
break;
case undefined:
// user didn't specify fields, implies returning all fields.
// only need to apply excluded fields
excluded.length && this.select('-' + excluded.join(' -'));
break;
}
return seen = excluded = selected = keys = fields = null;
function analyzeSchema (schema, prefix) {
prefix || (prefix = '');
// avoid recursion
if (~seen.indexOf(schema)) return;
seen.push(schema);
schema.eachPath(function (path, type) {
if (prefix) path = prefix + '.' + path;
// array of subdocs?
if (type.schema) {
analyzeSchema(type.schema, path);
}
analyzePath(path, type);
});
}
function analyzePath (path, type) {
if ('boolean' != typeof type.selected) return;
if (fields && ('+' + path) in fields) {
// forced inclusion
delete fields['+' + path];
// if there are other fields being included, add this one
// if no other included fields, leave this out (implied inclusion)
if (false === exclude && keys.length > 1) {
fields[path] = 1;
}
return
};
;(type.selected ? selected : excluded).push(path);
}
}
js
)Specifies a $where
condition
Use $where
when you need to select documents using a JavaScript expression.
query.$where('this.comments.length > 10 || this.name.length > 5')
query.$where(function () {
return this.comments.length > 10 || this.name.length > 5;
})
[path]
, [val]
)Specifies a path
for use with chaining.
Query.prototype.where = function (path, val) {
if (!arguments.length) return this;
if ('string' != typeof path) {
throw new TypeError('path must be a string');
}
this._currPath = path;
if (2 === arguments.length) {
this._conditions[path] = val;
}
return this;
};
// instead of writing:
User.find({age: {$gte: 21, $lte: 65}}, callback);
// we can instead write:
User.where('age').gte(21).lte(65);
// Moreover, you can also chain a bunch of these together:
User
.where('age').gte(21).lte(65)
.where('name', /^b/i)
.where('friends').slice(10)
.exec(callback)
val
)Specifies the complementary comparison value for paths specified with where()
Query.prototype.equals = function equals (val) {
var path = this._currPath;
if (!path) throw new Error('equals() must be used after where()');
this._conditions[path] = val;
return this;
}
val
<Object> User.where('age').equals(49);
// is the same as
User.where('age', 49);
array
)Specifies arguments for an $or
condition.
Query.prototype.or = function or (array) {
var or = this._conditions.$or || (this._conditions.$or = []);
if (!Array.isArray(array)) array = [array];
or.push.apply(or, array);
return this;
}
array
<Array> array of conditionsquery.or([{ color: 'red' }, { status: 'emergency' }])
array
)Specifies arguments for a $nor
condition.
Query.prototype.nor = function nor (array) {
var nor = this._conditions.$nor || (this._conditions.$nor = []);
if (!Array.isArray(array)) array = [array];
nor.push.apply(nor, array);
return this;
}
array
<Array> array of conditionsquery.nor([{ color: 'green' }, { status: 'ok' }])
path
, val
)Specifies a $gt query condition.
When called with one argument, the most recent path passed to where()
is used.
Thing.find().where('age').gt(21)
// or
Thing.find().gt('age', 21)
path
, val
)Specifies a $gte query condition.
When called with one argument, the most recent path passed to where()
is used.
path
, val
)Specifies a $lt query condition.
When called with one argument, the most recent path passed to where()
is used.
path
, val
)Specifies a $lte query condition.
When called with one argument, the most recent path passed to where()
is used.
path
, val
)Specifies a $ne query condition.
When called with one argument, the most recent path passed to where()
is used.
path
, val
)Specifies an $in query condition.
When called with one argument, the most recent path passed to where()
is used.
path
, val
)Specifies an $nin query condition.
When called with one argument, the most recent path passed to where()
is used.
path
, val
)Specifies an $all query condition.
When called with one argument, the most recent path passed to where()
is used.
path
, val
)Specifies an $size query condition.
When called with one argument, the most recent path passed to where()
is used.
path
, val
)Specifies a $regex query condition.
When called with one argument, the most recent path passed to where()
is used.
path
, val
)Specifies a $maxDistance query condition.
When called with one argument, the most recent path passed to where()
is used.
path
, val
)Specifies a $near
condition
Query.prototype.near = function (path, val) {
if (arguments.length === 1) {
val = path;
path = this._currPath
} else if (arguments.length === 2 && !Array.isArray(val)) {
val = utils.args(arguments);
path = this._currPath;
} else if (arguments.length === 3) {
val = utils.args(arguments, 1);
}
var conds = this._conditions[path] || (this._conditions[path] = {});
conds.$near = val;
return this;
}
path
, val
)Specifies a $nearSphere
condition.
Query.prototype.nearSphere = function (path, val) {
if (arguments.length === 1) {
val = path;
path = this._currPath
} else if (arguments.length === 2 && !Array.isArray(val)) {
val = utils.args(arguments);
path = this._currPath;
} else if (arguments.length === 3) {
val = utils.args(arguments, 1);
}
var conds = this._conditions[path] || (this._conditions[path] = {});
conds.$nearSphere = val;
return this;
}
path
, val
)Specifies a $mod
condition
Query.prototype.mod = function (path, val) {
if (arguments.length === 1) {
val = path;
path = this._currPath
} else if (arguments.length === 2 && !Array.isArray(val)) {
val = utils.args(arguments);
path = this._currPath;
} else if (arguments.length === 3) {
val = utils.args(arguments, 1);
}
var conds = this._conditions[path] || (this._conditions[path] = {});
conds.$mod = val;
return this;
}
path
, val
)Specifies an $exists
condition
Query.prototype.exists = function (path, val) {
if (arguments.length === 0) {
path = this._currPath
val = true;
} else if (arguments.length === 1) {
if ('boolean' === typeof path) {
val = path;
path = this._currPath;
} else {
val = true;
}
}
var conds = this._conditions[path] || (this._conditions[path] = {});
conds['$exists'] = val;
return this;
};
path
, criteria
)Specifies an $elemMatch
condition
Query.prototype.elemMatch = function (path, criteria) {
var block;
if ('Object' === path.constructor.name) {
criteria = path;
path = this._currPath;
} else if ('function' === typeof path) {
block = path;
path = this._currPath;
} else if ('Object' === criteria.constructor.name) {
} else if ('function' === typeof criteria) {
block = criteria;
} else {
throw new Error("Argument error");
}
var conds = this._conditions[path] || (this._conditions[path] = {});
if (block) {
criteria = new Query();
block(criteria);
conds['$elemMatch'] = criteria._conditions;
} else {
conds['$elemMatch'] = criteria;
}
return this;
};
// Spatial queries
query.elemMatch('comment', { author: 'autobot', votes: {$gte: 5}})
query.where('comment').elemMatch({ author: 'autobot', votes: {$gte: 5}})
query.elemMatch('comment', function (elem) {
elem.where('author').equals('autobot');
elem.where('votes').gte(5);
})
query.where('comment').elemMatch(function (elem) {
elem.where('author').equals('autobot');
elem.where('votes').gte(5);
})
path
, val
)Specifies a $box condition
show codeQuery.prototype.box = function (path, val) {
if (arguments.length === 1) {
val = path;
path = this._currPath;
}
var conds = this._conditions[path] || (this._conditions[path] = {});
conds['$within'] = { '$box': [val.ll, val.ur] };
return this;
};
var lowerLeft = [40.73083, -73.99756]
var upperRight= [40.741404, -73.988135]
query.where('loc').within.box({ ll: lowerLeft , ur: upperRight })
path
, val
, [opts]
)Specifies a $center condition
show codeQuery.prototype.center = function (path, val, opts) {
if (arguments.length === 1) {
val = path;
path = this._currPath;
}
var conds = this._conditions[path] || (this._conditions[path] = {});
conds['$within'] = { '$center': [val.center, val.radius] };
// copy any options
if (opts && 'Object' == opts.constructor.name) {
utils.options(opts, conds.$within);
}
return this;
};
var area = { center: [50, 50], radius: 10 }
query.where('loc').within.center(area)
path
, val
)Specifies a $centerSphere condition
show codeQuery.prototype.centerSphere = function (path, val) {
if (arguments.length === 1) {
val = path;
path = this._currPath;
}
var conds = this._conditions[path] || (this._conditions[path] = {});
conds['$within'] = { '$centerSphere': [val.center, val.radius] };
return this;
};
var area = { center: [50, 50], radius: 10 }
query.where('loc').within.centerSphere(area)
path
, val
)Specifies a $polygon condition
show codeQuery.prototype.polygon = function (path, val) {
if (arguments.length === 1) {
val = path;
path = this._currPath;
}
var conds = this._conditions[path] || (this._conditions[path] = {});
conds['$within'] = { '$polygon': val };
return this;
};
var polyA = [ [ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ] ]
query.where('loc').within.polygon(polyA)
// or
var polyB = { a : { x : 10, y : 20 }, b : { x : 15, y : 25 }, c : { x : 20, y : 20 } }
query.where('loc').within.polygon(polyB)
arg
)Specifies which document fields to include or exclude
show codeQuery.prototype.select = function select (arg) {
if (!arg) return this;
var fields = this._fields || (this._fields = {});
if ('Object' === arg.constructor.name) {
Object.keys(arg).forEach(function (field) {
fields[field] = arg[field];
});
} else if (1 === arguments.length && 'string' == typeof arg) {
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 TypeError('Invalid select() argument. Must be a string or object.');
}
return this;
};
When using string syntax, prefixing a path with -
will flag that path as excluded. When a path does not have the -
prefix, it is included. Lastly, if a path is prefixed with +
, it forces inclusion of the path, which is useful for paths excluded at the schema level.
// include a and b, exclude c
query.select('a b -c');
// or you may use object notation, useful when
// you have keys already prefixed with a "-"
query.select({a: 1, b: 1, c: 0});
// force inclusion of field excluded at schema level
query.select('+path')
path
, val
)Specifies a $slice condition
show codeQuery.prototype.slice = function (path, val) {
if (arguments.length === 1) {
val = path;
path = this._currPath
} else if (arguments.length === 2) {
if ('number' === typeof path) {
val = [path, val];
path = this._currPath;
}
} else if (arguments.length === 3) {
val = utils.args(arguments, 1);
}
var myFields = this._fields || (this._fields = {});
myFields[path] = { '$slice': val };
return this;
};
query.slice('comments', 5)
query.slice('comments', -5)
query.slice('comments', [10, 5])
query.where('comments').slice(5)
query.where('comments').slice([-10, 5])
arg
)Sets the sort order
show codeQuery.prototype.sort = function (arg) {
if (!arg) return this;
var sort = this.options.sort || (this.options.sort = []);
if ('Object' === arg.constructor.name) {
Object.keys(arg).forEach(function (field) {
push(sort, field, arg[field]);
});
} else if (1 === arguments.length && 'string' == typeof arg) {
arg.split(/\s+/).forEach(function (field) {
if (!field) return;
var ascend = '-' == field[0] ? -1 : 1;
if (ascend === -1) field = field.substring(1);
push(sort, field, ascend);
});
} else {
throw new TypeError('Invalid sort() argument. Must be a string or object.');
}
return this;
};
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.
// these are equivalent
query.sort({ field: 'asc', test: -1 });
query.sort('field -test');
val
)Specifies the limit option.
val
<Number> Kitten.find().limit(20)
val
)Specifies the skip option.
val
<Number> Kitten.find().skip(100).limit(20)
val
)Specifies the maxscan option.
val
<Number> Kitten.find().maxscan(100)
val
)Specifies the batchSize option.
val
<Number> Kitten.find().batchSize(100)
val
)Specifies the comment
option.
val
<Number> Kitten.findOne(condition).comment('login query')
Specifies this query as a snapshot
query.
Query.prototype.snapshot = function () {
this.options.snapshot = true;
return this;
};
Kitten.find().snapshot()
val
)Sets query hints.
show codeQuery.prototype.hint = function (val) {
if (!val) return this;
var hint = this.options.hint || (this.options.hint = {});
if ('Object' === val.constructor.name) {
// must keep object keys in order so don't use Object.keys()
for (var k in val) {
hint[k] = val[k];
}
} else {
throw new TypeError('Invalid hint. ' + val);
}
return this;
};
val
<Object> a hint objectModel.find().hint({ indexA: 1, indexB: -1})
v
)Sets the slaveOk option.
show codeQuery.prototype.slaveOk = function (v) {
this.options.slaveOk = arguments.length ? !!v : true;
return this;
};
v
<Boolean> defaults to truenew Query().slaveOk() // true
new Query().slaveOk(true)
new Query().slaveOk(false)
v
)Sets tailable option.
show codeQuery.prototype.tailable = function (v) {
this.options.tailable = arguments.length ? !!v : true;
return this;
};
v
<Boolean> defaults to trueKitten.find().tailable() <== true
Kitten.find().tailable(true)
Kitten.find().tailable(false)
callback
)Executes the query as a find() operation.
show codeQuery.prototype.execFind = function (callback) {
var model = this.model
, promise = new Promise(callback);
try {
this.cast(model);
} catch (err) {
promise.error(err);
return this;
}
// apply default schematype path selections
this._applyPaths();
var self = this
, castQuery = this._conditions
, options = this._optionsForExec(model)
var fields = utils.clone(options.fields = this._fields);
model.collection.find(castQuery, options, function (err, cursor) {
if (err) return promise.error(err);
cursor.toArray(tick(cb));
});
function cb (err, docs) {
if (err) return promise.error(err);
if (true === options.lean)
return promise.complete(docs);
var arr = []
, count = docs.length;
if (!count) return promise.complete([]);
for (var i = 0, l = docs.length; i < l; i++) {
arr[i] = new model(undefined, fields, true);
arr[i].init(docs[i], self, function (err) {
if (err) return promise.error(err);
--count || promise.complete(arr);
});
}
}
return this;
};
callback
<Function> callback
)Executes the query as a findOne() operation.
show codeQuery.prototype.findOne = function (callback) {
this.op = 'findOne';
if (!callback) return this;
var model = this.model;
var promise = new Promise(callback);
try {
this.cast(model);
} catch (err) {
promise.error(err);
return this;
}
// apply default schematype path selections
this._applyPaths();
var self = this
, castQuery = this._conditions
, options = this._optionsForExec(model)
var fields = utils.clone(options.fields = this._fields);
model.collection.findOne(castQuery, options, tick(function (err, doc) {
if (err) return promise.error(err);
if (!doc) return promise.complete(null);
if (true === options.lean) return promise.complete(doc);
var casted = new model(undefined, fields, true);
casted.init(doc, self, function (err) {
if (err) return promise.error(err);
promise.complete(casted);
});
}));
return this;
};
callback
<Function> Kitten.where('color', 'white').findOne(function (err, kitten) {
if (err) return handleError(err);
// kitten may be null if no document matched
if (kitten) {
...
}
})
callback
)Exectues the query as a count() operation.
show codeQuery.prototype.count = function (callback) {
this.op = 'count';
var model = this.model;
try {
this.cast(model);
} catch (err) {
return callback(err);
}
var castQuery = this._conditions;
model.collection.count(castQuery, tick(callback));
return this;
};
callback
<Function> Kitten.where('color', 'black').count(function (err, count) {
if (err) return handleError(err);
console.log('there are %d black kittens', count);
})
field
, callback
)Executes this query as a distict() operation.
show codeQuery.prototype.distinct = function (field, callback) {
this.op = 'distinct';
var model = this.model;
try {
this.cast(model);
} catch (err) {
return callback(err);
}
var castQuery = this._conditions;
model.collection.distinct(field, castQuery, tick(callback));
return this;
};
doc
, callback
)Executes this query as an update() operation.
show codeQuery.prototype.update = function update (doc, callback) {
this.op = 'update';
this._updateArg = doc;
var model = this.model
, options = this._optionsForExec(model)
, fn = 'function' == typeof callback
, castedQuery
, castedDoc
castedQuery = castQuery(this);
if (castedQuery instanceof Error) {
if (fn) {
process.nextTick(callback.bind(null, castedQuery));
return this;
}
throw castedQuery;
}
castedDoc = castDoc(this);
if (!castedDoc) {
fn && process.nextTick(callback.bind(null, null, 0));
return this;
}
if (castedDoc instanceof Error) {
if (fn) {
process.nextTick(callback.bind(null, castedDoc));
return this;
}
throw castedDoc;
}
if (!fn) {
delete options.safe;
}
model.collection.update(castedQuery, castedDoc, options, tick(callback));
return this;
};
All paths passed that are not $atomic operations will become $set ops so we retain backwards compatibility.
Model.update({..}, { title: 'remove words' }, ...)
becomes
Model.update({..}, { $set: { title: 'remove words' }}, ...)
Passing an empty object {}
as the doc will result in a no-op. The update operation will be ignored and the callback executed without sending the command to MongoDB so as to prevent accidently overwritting the collection.
obj
)Casts obj for an update command.
show codeQuery.prototype._castUpdate = function _castUpdate (obj) {
var ops = Object.keys(obj)
, i = ops.length
, ret = {}
, hasKeys
, val
while (i--) {
var op = ops[i];
if ('$' !== op[0]) {
// fix up $set sugar
if (!ret.$set) {
if (obj.$set) {
ret.$set = obj.$set;
} else {
ret.$set = {};
}
}
ret.$set[op] = obj[op];
ops.splice(i, 1);
if (!~ops.indexOf('$set')) ops.push('$set');
} else if ('$set' === op) {
if (!ret.$set) {
ret[op] = obj[op];
}
} else {
ret[op] = obj[op];
}
}
// cast each value
i = ops.length;
while (i--) {
op = ops[i];
val = ret[op];
if ('Object' === val.constructor.name) {
hasKeys |= this._walkUpdatePath(val, op);
} else {
var msg = 'Invalid atomic update value for ' + op + '. '
+ 'Expected an object, received ' + typeof val;
throw new Error(msg);
}
}
return hasKeys && ret;
}
obj
<Object> obj
, op
, pref
)Walk each path of obj and cast its values
according to its schema.
Query.prototype._walkUpdatePath = function _walkUpdatePath (obj, op, pref) {
var strict = this.model.schema.options.strict
, prefix = pref ? pref + '.' : ''
, keys = Object.keys(obj)
, i = keys.length
, hasKeys = false
, schema
, key
, val
while (i--) {
key = keys[i];
val = obj[key];
if (val && 'Object' === val.constructor.name) {
// watch for embedded doc schemas
schema = this._getSchema(prefix + key);
if (schema && schema.caster && op in castOps) {
// embedded doc schema
if (strict && !schema) {
// path is not in our strict schema
if ('throw' == strict) {
throw new Error('Field `' + key + '` is not in schema.');
} else {
// ignore paths not specified in schema
delete obj[key];
}
} else {
hasKeys = true;
if ('$each' in val) {
obj[key] = {
$each: this._castUpdateVal(schema, val.$each, op)
}
} else {
obj[key] = this._castUpdateVal(schema, val, op);
}
}
} else {
hasKeys |= this._walkUpdatePath(val, op, prefix + key);
}
} else {
schema = '$each' === key
? this._getSchema(pref)
: this._getSchema(prefix + key);
var skip = strict &&
!schema &&
!/real|nested/.test(this.model.schema.pathType(prefix + key));
if (skip) {
if ('throw' == strict) {
throw new Error('Field `' + prefix + key + '` is not in schema.');
} else {
delete obj[key];
}
} else {
hasKeys = true;
obj[key] = this._castUpdateVal(schema, val, op, key);
}
}
}
return hasKeys;
}
schema
, val
, op
, [$conditional]
)Casts val
according to schema
and atomic op
.
Query.prototype._castUpdateVal = function _castUpdateVal (schema, val, op, $conditional) {
if (!schema) {
// non-existing schema path
return op in numberOps
? Number(val)
: val
}
if (schema.caster && op in castOps &&
('Object' === val.constructor.name || Array.isArray(val))) {
// Cast values for ops that add data to MongoDB.
// Ensures embedded documents get ObjectIds etc.
var tmp = schema.cast(val);
if (Array.isArray(val)) {
val = tmp;
} else {
val = tmp[0];
}
}
if (op in numberOps) return Number(val);
if (/^\$/.test($conditional)) return schema.castForQuery($conditional, val);
return schema.castForQuery(val)
}
path
)Finds the schema for path
. This is different than
calling schema.path
as it also resolves paths with
positional selectors (something.$.another.$.path).
Query.prototype._getSchema = function _getSchema (path) {
var schema = this.model.schema
, pathschema = schema.path(path);
if (pathschema)
return pathschema;
// look for arrays
return (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 Types.Mixed) {
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 (p !== parts.length) {
if ('$' === parts[p]) {
// comments.$.comments.$.title
return search(parts.slice(p+1), foundschema.schema);
} else {
// this is the last path of the selector
return search(parts.slice(p), foundschema.schema);
}
}
}
return foundschema;
}
}
})(path.split('.'), schema)
}
path
<String> callback
)Executes this query as a remove() operation.
show codeQuery.prototype.remove = function (callback) {
this.op = 'remove';
var model = this.model
, options = this._optionsForExec(model)
, cb = 'function' == typeof callback
try {
this.cast(model);
} catch (err) {
if (cb) return callback(err);
throw err;
}
if (!cb) {
delete options.safe;
}
var castQuery = this._conditions;
model.collection.remove(castQuery, options, tick(callback));
return this;
};
callback
<Function> Cassette.where('artist').equals('Anne Murray').remove(callback)
[query]
, [doc]
, [options]
, [callback]
)Issues a mongodb findAndModify update command.
show codeQuery.prototype.findOneAndUpdate = function (query, doc, options, callback) {
this.op = 'findOneAndUpdate';
switch (arguments.length) {
case 3:
if ('function' == typeof options)
callback = options, options = {};
break;
case 2:
if ('function' == typeof doc) {
callback = doc;
doc = query;
query = undefined;
}
options = undefined;
break;
case 1:
if ('function' == typeof query) {
callback = query;
query = options = doc = undefined;
} else {
doc = query;
query = options = undefined;
}
}
// apply query
if (query) {
if ('Object' === query.constructor.name) {
merge(this._conditions, query);
} else if (query instanceof Query) {
merge(this._conditions, query._conditions);
} else if (query instanceof Document) {
merge(this._conditions, query.toObject());
}
}
// apply doc
if (doc) {
merge(this._updateArg, doc);
}
// apply options
options && this.setOptions(options);
if (!callback) return this;
return this._findAndModify('update', 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.
new
: bool - true to return the modified document rather than the original. defaults to trueupsert
: bool - creates the object if it doesn't exist. defaults to false.sort
: if multiple docs are found by the conditions, sets the sort order to choose which doc to updatequery.findOneAndUpdate(conditions, update, options, callback) // executes
query.findOneAndUpdate(conditions, update, options) // returns Query
query.findOneAndUpdate(conditions, update, callback) // executes
query.findOneAndUpdate(conditions, update) // returns Query
query.findOneAndUpdate(callback) // executes
query.findOneAndUpdate() // returns Query
[conditions]
, [options]
, [callback]
)Issues a mongodb findAndModify remove command.
show codeQuery.prototype.findOneAndRemove = function (conditions, options, callback) {
this.op = 'findOneAndRemove';
if ('function' == typeof options) {
callback = options;
options = undefined;
} else if ('function' == typeof conditions) {
callback = conditions;
conditions = undefined;
}
// apply conditions
if (conditions) {
if ('Object' === conditions.constructor.name) {
merge(this._conditions, conditions);
} else if (conditions instanceof Query) {
merge(this._conditions, conditions._conditions);
} else if (conditions instanceof Document) {
merge(this._conditions, conditions.toObject());
}
}
// apply options
options && this.setOptions(options);
if (!callback) return this;
return this._findAndModify('remove', callback);
}
Finds a matching document, removes it, passing the found document (if any) to the callback. Executes immediately if callback
is passed else a Query object is returned.
sort
: if multiple docs are found by the conditions, sets the sort order to choose which doc to updateA.where().findOneAndRemove(conditions, options, callback) // executes
A.where().findOneAndRemove(conditions, options) // return Query
A.where().findOneAndRemove(conditions, callback) // executes
A.where().findOneAndRemove(conditions) // returns Query
A.where().findOneAndRemove(callback) // executes
A.where().findOneAndRemove() // returns Query
type
, callback
)_findAndModify
show codeQuery.prototype._findAndModify = function (type, callback) {
var model = this.model
, promise = new Promise(callback)
, self = this
, castedQuery
, castedDoc
, fields
, sort
, opts
castedQuery = castQuery(this);
if (castedQuery instanceof Error) {
process.nextTick(promise.error.bind(promise, castedQuery));
return promise;
}
opts = this._optionsForExec(model);
if ('remove' == type) {
opts.remove = true;
} else {
if (!('new' in opts)) opts.new = true;
if (!('upsert' in opts)) opts.upsert = false;
castedDoc = castDoc(this);
if (!castedDoc) {
if (opts.upsert) {
// still need to do the upsert to empty doc
castedDoc = { $set: {} };
} else {
return this.findOne(callback);
}
} else if (castedDoc instanceof Error) {
process.nextTick(promise.error.bind(promise, castedDoc));
return promise;
}
}
if (this._fields) {
fields = utils.clone(opts.fields = this._fields);
}
// the driver needs a default
sort = opts.sort || [];
model
.collection
.findAndModify(castedQuery, sort, castedDoc, opts, tick(function (err, doc) {
if (err) return promise.error(err);
if (!doc) return promise.complete(null);
if (true === opts.lean) {
return promise.complete(doc);
}
var casted = new model(undefined, fields, true);
casted.init(doc, self, function (err) {
if (err) return promise.error(err);
promise.complete(casted);
});
}));
return promise;
}
path
, [fields]
, [model]
, [conditions]
, [options]
)Specifies paths which should be populated.
show codeQuery.prototype.populate = function (path, fields, model, conditions, options) {
if ('string' !== typeof model) {
options = conditions;
conditions = model;
model = undefined;
}
// The order of fields/conditions args is opposite Model.find but
// necessary to keep backward compatibility (fields could be
// an array, string, or object literal).
this.options.populate[path] =
new PopulateOptions(fields, conditions, options, model);
return this;
};
Paths are populated after the query executes and a response is received. A separate query is then executed for each path specified for population. After a response for each query has also been returned, the results are passed to the callback.
Kitten.find().populate('owner').exec(callback)
Returns a stream interface
show codeQuery.prototype.stream = function stream () {
return new QueryStream(this);
}
// helpers
// follows the nodejs 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) {
// handle the error
}).on('close', function () {
// the stream is closed
});
Syntax sugar for expressive queries.
query.within.box()
query.within.center()
query
)Returns a stream interface for the query
.
function QueryStream (query) {
Stream.call(this);
this.query = query;
this.readable = true;
this.paused = false;
this._cursor = null;
this._destroyed = null;
this._fields = null;
this._ticks = 0;
this._inline = T_INIT;
// give time to hook up events
var self = this;
process.nextTick(function () {
self._init();
});
}
query
<Query> Initializes the query.
show codeQueryStream.prototype._init = function () {
if (this._destroyed) return;
var query = this.query
, model = query.model
, options = query._optionsForExec(model)
, self = this
try {
query.cast(model);
} catch (err) {
return self.destroy(err);
}
self._fields = utils.clone(options.fields = query._fields);
model.collection.find(query._conditions, options, function (err, cursor) {
if (err) return self.destroy(err);
self._cursor = cursor;
self._next();
});
}
Trampoline for pulling the next doc from cursor.
show codeQueryStream.prototype._next = function () {
// avoid stack overflows with large result sets.
// trampoline instead of recursion.
var fn;
while (fn = this.__next()) fn.call(this);
}
Pulls the next doc from the cursor.
show codeQueryStream.prototype.__next = function () {
if (this.paused || this._destroyed) return;
var self = this;
self._inline = T_INIT;
self._cursor.nextObject(function (err, doc) {
self._onNextObject(err, doc);
});
// if onNextObject() was already called in this tick
// return ourselves to the trampoline.
if (T_CONT === this._inline) {
return this.__next;
} else {
// 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;
}
}
err
, doc
)Transforms raw doc
s returned from the cursor into a model instance.
QueryStream.prototype._onNextObject = function (err, doc) {
if (err) return this.destroy(err);
// when doc is null we hit the end of the cursor
if (!doc) {
return this.destroy();
}
if (this.query.options && this.query.options.lean === true) {
this.emit('data', doc);
this._next();
return;
}
var instance = new this.query.model(undefined, this._fields);
// skip _id for pre-init hooks
delete instance._doc._id;
var self = this;
instance.init(doc, this.query, function (err) {
if (err) return self.destroy(err);
self.emit('data', instance);
// trampoline management
if (T_IDLE === self._inline) {
// no longer in trampoline. restart it.
self._next();
} else
// in a trampoline. tell __next that its
// ok to continue jumping.
self._inline = T_CONT;
});
}
Pauses this stream.
show codeQueryStream.prototype.pause = function () {
this.paused = true;
}
Resumes this stream.
show codeQueryStream.prototype.resume = function () {
this.paused = false;
this._next();
}
[err]
)Destroys the stream, closing the underlying cursor.
No more events will be emitted.
QueryStream.prototype.destroy = function (err) {
if (this._destroyed) return;
this._destroyed = true;
this.readable = false;
if (this._cursor) {
this._cursor.close();
}
if (err) {
this.emit('error', err);
}
this.emit('close');
}
[err]
<Error> Flag stating whether or not this stream is paused.
Flag stating whether or not this stream is readable.
key
, cast
, options
)Array SchemaType constructor
show codefunction SchemaArray (key, cast, options) {
if (cast) {
var castOptions = {};
if ('Object' === cast.constructor.name) {
if (cast.type) {
// support { type: Woot }
castOptions = cast;
cast = cast.type;
delete castOptions.type;
} else {
cast = Mixed;
}
}
var caster = cast.name in Types ? Types[cast.name] : cast;
this.casterConstructor = caster;
this.caster = new caster(null, castOptions);
}
SchemaType.call(this, key, options);
var self = this
, defaultArr
, fn;
if (this.defaultValue) {
defaultArr = this.defaultValue;
fn = 'function' == typeof defaultArr;
}
this.default(function(){
var arr = fn ? defaultArr() : defaultArr || [];
return new MongooseArray(arr, self.path, this);
});
};
key
<String> cast
<SchemaType> options
<Object> value
)Check required
show codeSchemaArray.prototype.checkRequired = function (value) {
return !!(value && value.length);
};
value
<Array> value
, scope
)Overrides the getters application for the population special-case
show codeSchemaArray.prototype.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);
};
value
, doc
, init
)Casts contents
show codeSchemaArray.prototype.cast = function (value, doc, init) {
if (Array.isArray(value)) {
if (!(value instanceof MongooseArray)) {
value = new MongooseArray(value, this.path, doc);
}
if (this.caster) {
try {
for (var i = 0, l = value.length; i < l; i++) {
value[i] = this.caster.cast(value[i], doc, init);
}
} catch (e) {
// rethrow
throw new CastError(e.type, value);
}
}
return value;
} else {
return this.cast([value], doc, init);
}
};
$conditional
, [value]
)Casts contents for queries.
show codeSchemaArray.prototype.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 proto = this.casterConstructor.prototype;
var method = proto.castForQuery || proto.cast;
if (Array.isArray(val)) {
val = val.map(function (v) {
if (method) v = method.call(proto, v);
return isMongooseObject(v)
? v.toObject()
: v;
});
} else if (method) {
val = method.call(proto, val);
}
}
return val && isMongooseObject(val)
? val.toObject()
: val;
};
path
, options
)Boolean SchemaType constructor.
show codefunction SchemaBoolean (path, options) {
SchemaType.call(this, path, options);
};
Required validator
show codeSchemaBoolean.prototype.checkRequired = function (value) {
return value === true || value === false;
};
value
)Casts to boolean
show codeSchemaBoolean.prototype.cast = function (value) {
if (value === null) return value;
if (value === '0') return false;
return !!value;
};
value
<Object> $conditional
, val
)Casts contents for queries.
show codeSchemaBoolean.prototype.castForQuery = function ($conditional, val) {
var handler;
if (2 === arguments.length) {
handler = SchemaBoolean.$conditionalHandlers[$conditional];
if (!handler)
throw new Error("Can't use " + $conditional + " with Boolean.");
return handler.call(this, val);
}
return this.cast($conditional);
};
key
, cast
)Buffer SchemaType constructor
show codefunction SchemaBuffer (key, options) {
SchemaType.call(this, key, options, 'Buffer');
};
key
<String> cast
<SchemaType> Check required
show codeSchemaBuffer.prototype.checkRequired = function (value) {
return !!(value && value.length);
};
value
, doc
, init
)Casts contents
show codeSchemaBuffer.prototype.cast = function (value, doc, init) {
if (SchemaType._isRef(this, value, init)) return value;
if (Buffer.isBuffer(value)) {
if (!(value instanceof MongooseBuffer)) {
value = new MongooseBuffer(value, [this.path, doc]);
}
return value;
} else if (value instanceof Binary) {
return new MongooseBuffer(value.value(true), [this.path, doc]);
}
if ('string' === typeof value || Array.isArray(value)) {
return new MongooseBuffer(value, [this.path, doc]);
}
throw new CastError('buffer', value);
};
$conditional
, [value]
)Casts contents for queries.
show codeSchemaBuffer.prototype.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);
} else {
val = $conditional;
return this.cast(val).toObject();
}
};
key
, options
)Date SchemaType constructor.
show codefunction SchemaDate (key, options) {
SchemaType.call(this, key, options);
};
Required validator for date
show codeSchemaDate.prototype.checkRequired = function (value) {
return value instanceof Date;
};
value
)Casts to date
show codeSchemaDate.prototype.cast = function (value) {
if (value === null || value === '')
return null;
if (value instanceof Date)
return value;
var date;
// support for timestamps
if (value instanceof Number || 'number' == typeof value
|| String(value) == Number(value))
date = new Date(Number(value));
// support for date strings
else if (value.toString)
date = new Date(value.toString());
if (date.toString() != 'Invalid Date')
return date;
throw new CastError('date', value);
};
value
<Object> to cast$conditional
, [value]
)Casts contents for queries.
show codeSchemaDate.prototype.castForQuery = function ($conditional, val) {
var handler;
if (2 !== arguments.length) {
return this.cast($conditional);
}
handler = this.$conditionalHandlers[$conditional];
if (!handler) {
throw new Error("Can't use " + $conditional + " with Date.");
}
return handler.call(this, val);
};
key
, schema
, options
)SubdocsArray SchemaType constructor
show codefunction DocumentArray (key, schema, options) {
// compile an embedded document for this schema
// TODO Move this into parent model compilation for performance improvement?
function EmbeddedDocument () {
Subdocument.apply(this, arguments);
};
EmbeddedDocument.prototype.__proto__ = Subdocument.prototype;
EmbeddedDocument.prototype._setSchema(schema);
EmbeddedDocument.schema = schema;
// apply methods
for (var i in schema.methods) {
EmbeddedDocument.prototype[i] = schema.methods[i];
}
// apply statics
for (var i in schema.statics)
EmbeddedDocument[i] = schema.statics[i];
EmbeddedDocument.options = options;
this.schema = schema;
ArrayType.call(this, key, EmbeddedDocument, options);
this.schema = schema;
var path = this.path;
var fn = this.defaultValue;
this.default(function(){
var arr = fn.call(this);
if (!Array.isArray(arr)) arr = [arr];
return new MongooseDocumentArray(arr, path, this);
});
};
Performs local validations first, then validations on each embedded doc
show codeDocumentArray.prototype.doValidate = function (array, fn, scope) {
var self = this;
SchemaType.prototype.doValidate.call(this, array, function(err){
if (err) return fn(err);
var count = array && array.length
, error = false;
if (!count) return fn();
array.forEach(function(doc, index){
doc.validate(function(err){
if (err && !error){
// rewrite they key
err.key = self.key + '.' + index + '.' + err.key;
fn(err);
error = true;
} else {
--count || fn();
}
});
});
}, scope);
};
value
, document
)Casts contents
show codeDocumentArray.prototype.cast = function (value, doc, init, prev) {
var subdoc
, i
if (Array.isArray(value)) {
if (!(value instanceof MongooseDocumentArray)) {
value = new MongooseDocumentArray(value, this.path, doc);
}
i = value.length;
while (i--) {
if (!(value[i] instanceof Subdocument)) {
if (init) {
subdoc = new this.casterConstructor(null, value, true);
value[i] = subdoc.init(value[i]);
} else {
subdoc = prev && prev.id(value[i]._id) ||
new this.casterConstructor(value[i], value);
// if set() is hooked it will have no return value
// see gh-746
value[i] = subdoc;
}
}
}
return value;
} else {
return this.cast([value], doc, init, prev);
}
throw new CastError('documentarray', value);
};
path
, options
)Mixed SchemaType constructor.
show codefunction Mixed (path, options) {
// make sure empty array defaults are handled
if (options &&
options.default &&
Array.isArray(options.default) &&
0 === options.default.length) {
options.default = Array;
}
SchemaType.call(this, path, options);
};
Required validator
show codeMixed.prototype.checkRequired = function (val) {
return true;
};
value
)Casts val
for Mixed.
Mixed.prototype.cast = function (val) {
return val;
};
value
<Object> to castthis is a no-op
$cond
, [val]
)Casts contents for queries.
show codeMixed.prototype.castForQuery = function ($cond, val) {
if (arguments.length === 2) return val;
return $cond;
};
key
, options
)Number SchemaType constructor.
show codefunction SchemaNumber (key, options) {
SchemaType.call(this, key, options, 'Number');
};
Required validator for number
show codeSchemaNumber.prototype.checkRequired = function checkRequired (value) {
if (SchemaType._isRef(this, value, true)) {
return null != value;
} else {
return typeof value == 'number' || value instanceof Number;
}
};
value
, message
)Sets a maximum number validator
show codeSchemaNumber.prototype.min = function (value, message) {
if (this.minValidator)
this.validators = this.validators.filter(function(v){
return v[1] != 'min';
});
if (value != null)
this.validators.push([function(v){
return v === null || v >= value;
}, 'min']);
return this;
};
maximum
, message
)Sets a maximum number validator
show codeSchemaNumber.prototype.max = function (value, message) {
if (this.maxValidator)
this.validators = this.validators.filter(function(v){
return v[1] != 'max';
});
if (value != null)
this.validators.push([this.maxValidator = function(v){
return v === null || v <= value;
}, 'max']);
return this;
};
value
, doc
, init
)Casts to number
show codeSchemaNumber.prototype.cast = function (value, doc, init) {
if (SchemaType._isRef(this, value, init)) return value;
if (!isNaN(value)){
if (null === value) return value;
if ('' === value) return null;
if ('string' == typeof value) value = Number(value);
if (value instanceof Number) return value
if ('number' == typeof value) return value;
if (value.toString && !Array.isArray(value) &&
value.toString() == Number(value)) {
return new Number(value)
}
}
throw new CastError('number', value);
};
$conditional
, [value]
)Casts contents for queries.
show codeSchemaNumber.prototype.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);
} else {
val = this.cast($conditional);
return val == null ? val : val
}
};
key
, options
)ObjectId SchemaType constructor.
show codefunction ObjectId (key, options) {
SchemaType.call(this, key, options, 'ObjectID');
};
Check required
show codeObjectId.prototype.checkRequired = function checkRequired (value) {
if (SchemaType._isRef(this, value, true)) {
return null != value;
} else {
return value instanceof oid;
}
};
value
, scope
, init
)Casts to ObjectId
show codeObjectId.prototype.cast = function (value, scope, init) {
if (SchemaType._isRef(this, value, init)) return value;
if (value === null) return value;
if (value instanceof oid)
return value;
if (value._id && value._id instanceof oid)
return value._id;
if (value.toString)
return oid.fromString(value.toString());
throw new CastError('object id', value);
};
$conditional
, [val]
)Casts contents for queries.
show codeObjectId.prototype.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);
} else {
return this.cast($conditional);
}
};
turnOn
)Adds an auto-generated ObjectId default if turnOn is true.
show codeObjectId.prototype.auto = function (turnOn) {
if (turnOn) {
this.default(defaultId);
this.set(resetId)
}
};
turnOn
<Boolean> auto generated ObjectId defaultskey
, options
)String SchemaType constructor.
show codefunction SchemaString (key, options) {
this.enumValues = [];
this.regExp = null;
SchemaType.call(this, key, options, 'String');
};
[args...]
)Adds enumeration values
show codeSchemaString.prototype.enum = function () {
var len = arguments.length;
if (!len || undefined === arguments[0] || false === arguments[0]) {
if (this.enumValidator){
this.enumValidator = false;
this.validators = this.validators.filter(function(v){
return v[1] != 'enum';
});
}
return;
}
for (var i = 0; i < len; i++) {
if (undefined !== arguments[i]) {
this.enumValues.push(this.cast(arguments[i]));
}
}
if (!this.enumValidator) {
var values = this.enumValues;
this.enumValidator = function(v){
return undefined === v || ~values.indexOf(v);
};
this.validators.push([this.enumValidator, 'enum']);
}
};
[args...]
<String> enumeration valuesAdds a lowercase setter
show codeSchemaString.prototype.lowercase = function () {
return this.set(function (v) {
return v.toLowerCase();
});
};
Adds an uppercase setter
show codeSchemaString.prototype.uppercase = function () {
return this.set(function (v) {
return v.toUpperCase();
});
};
Adds a trim setter
show codeSchemaString.prototype.trim = function () {
return this.set(function (v) {
return v.trim();
});
};
regExp
)Sets a regexp test
show codeSchemaString.prototype.match = function match (regExp) {
this.validators.push([function(v){
return null != v && '' !== v
? regExp.test(v)
: true
}, 'regexp']);
};
regExp
<RegExp> regular expression to test againstvalue
)Check required
show codeSchemaString.prototype.checkRequired = function checkRequired (value) {
if (SchemaType._isRef(this, value, true)) {
return null != value;
} else {
return (value instanceof String || typeof value == 'string') && value.length;
}
};
Casts to String
show codeSchemaString.prototype.cast = function (value, scope, init) {
if (SchemaType._isRef(this, value, init)) return value;
if (value === null) return value;
if ('undefined' !== typeof value && value.toString) return value.toString();
throw new CastError('string', value);
};
$conditional
, [val]
)Casts contents for queries.
show codeSchemaString.prototype.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);
} else {
val = $conditional;
if (val instanceof RegExp) return val;
return this.cast(val);
}
};
definition
)Schema constructor.
show codefunction Schema (obj, options) {
if (!(this instanceof Schema))
return new Schema(obj, options);
this.paths = {};
this.subpaths = {};
this.virtuals = {};
this.nested = {};
this.inherits = {};
this.callQueue = [];
this._indexes = [];
this.methods = {};
this.statics = {};
this.tree = {};
this._requiredpaths = undefined;
// set options
this.options = utils.options({
safe: true
, strict: true
, capped: false // { size, max, autoIndexId }
, versionKey: '__v'
, minimize: true
, autoIndex: true
}, options);
// build paths
if (obj) {
this.add(obj);
}
if (!this.paths['_id'] && !this.options.noId) {
this.add({ _id: {type: ObjectId, auto: true} });
}
if (!this.paths['id'] && !this.options.noVirtualId) {
this.virtual('id').get(function () {
if (this.__id) {
return this.__id;
}
return this.__id = null == this._id
? null
: this._id.toString();
});
}
delete this.options.noVirtualId;
// versioning not directly added to schema b/c we only want
// it in the top level document, not embedded ones.
};
definition
<Object> var child = new Schema({ name: String });
var schema = new Schema({ name: String, age: Number, children: [child] });
var Tree = mongoose.model('Tree', schema);
When nesting schemas, (children
in the example above), always declare the child schema first before passing it into is parent.
obj
, prefix
)Adds key path / schema type pairs to this schema.
show codeSchema.prototype.add = function add (obj, prefix) {
prefix = prefix || '';
for (var i in obj) {
if (null == obj[i]) {
throw new TypeError('Invalid value for schema path `'+ prefix + i +'`');
}
if (obj[i].constructor.name == 'Object' && (!obj[i].type || obj[i].type.type)) {
if (Object.keys(obj[i]).length) {
// nested object { last: { name: String }}
this.nested[prefix + i] = true;
this.add(obj[i], prefix + i + '.');
}
else
this.path(prefix + i, obj[i]); // mixed type
} else
this.path(prefix + i, obj[i]);
}
};
var ToySchema = new Schema;
ToySchema.add({ name: 'string', color: 'string', price: 'number' });
path
, constructor
)Gets/sets schema paths.
show codeSchema.prototype.path = function (path, obj) {
if (obj == undefined) {
if (this.paths[path]) return this.paths[path];
if (this.subpaths[path]) return this.subpaths[path];
// subpaths?
return /\.\d+\.?/.test(path)
? getPositionalPath(this, path)
: undefined;
}
// some path names conflict with document methods
if (reserved[path]) {
throw new Error("`" + path + "` may not be used as a schema pathname");
}
// update the tree
var subpaths = path.split(/\./)
, last = subpaths.pop()
, branch = this.tree;
subpaths.forEach(function(path) {
if (!branch[path]) branch[path] = {};
branch = branch[path];
});
branch[last] = utils.clone(obj);
this.paths[path] = Schema.interpretAsType(path, obj);
return this;
};
Sets a path (if arity 2)
Gets a path (if arity 1)
schema.path('name') // returns a SchemaType
schema.path('name', Number) // changes the schemaType of `name` to Number
fn
)Iterates the schemas paths similar to Array#forEach.
show codeSchema.prototype.eachPath = function (fn) {
var keys = Object.keys(this.paths)
, len = keys.length;
for (var i = 0; i < len; ++i) {
fn(keys[i], this.paths[keys[i]]);
}
return this;
};
fn
<Function> callback functionThe callback is passed the pathname and schemaType as arguments on each iteration.
Returns an Array of path strings that are required by this schema.
show codeSchema.prototype.requiredPaths = function requiredPaths () {
if (this._requiredpaths) return this._requiredpaths;
var paths = Object.keys(this.paths)
, i = paths.length
, ret = [];
while (i--) {
var path = paths[i];
if (this.paths[path].isRequired) ret.push(path);
}
return this._requiredpaths = ret;
}
path
)Returns the pathType of path
for this schema.
Schema.prototype.pathType = function (path) {
if (path in this.paths) return 'real';
if (path in this.virtuals) return 'virtual';
if (path in this.nested) return 'nested';
if (path in this.subpaths) return 'real';
if (/\.\d+\.?/.test(path) && getPositionalPath(this, path)) {
return 'real';
} else {
return 'adhocOrUndefined'
}
};
path
<String> Given a path, returns whether it is a real, virtual, nested, or ad-hoc/undefined path.
name
, args
)Adds a method call to the queue.
show codeSchema.prototype.queue = function(name, args){
this.callQueue.push([name, args]);
return this;
};
method
, callback
)Defines a pre hook for the document.
show codeSchema.prototype.pre = function(){
return this.queue('pre', arguments);
};
var toySchema = new Schema(..);
toySchema.pre('save', function (next) {
if (!this.created) this.created = new Date;
next();
})
toySchema.pre('validate', function (next) {
if (this.name != 'Woody') this.name = 'Woody';
next();
})
method
, fn
)Defines a post for the document
show codeSchema.prototype.post = function(method, fn){
return this.queue('on', arguments);
};
Post hooks fire on
the event emitted from document instances of Models compiled from this schema.
var schema = new Schema(..);
schema.post('save', function () {
console.log('this fired after a document was saved');
});
var Model = mongoose.model('Model', schema);
var m = new Model(..);
m.save(function (err) {
console.log('this fires after the `post` hook');
});
plugin
, opts
)Registers a plugin for this schema.
show codeSchema.prototype.plugin = function (fn, opts) {
fn(this, opts);
return this;
};
method
, [fn]
)Adds an instance method to documents constructed from Models compiled from this schema.
show codeSchema.prototype.method = function (name, fn) {
if ('string' != typeof name)
for (var i in name)
this.methods[i] = name[i];
else
this.methods[name] = fn;
return this;
};
var schema = kittySchema = new Schema(..);
schema.methods.meow = function () {
console.log('meeeeeoooooooooooow');
})
var Kitty = mongoose.model('Kitty', schema);
var fizz = new Kitty;
fizz.meow(); // meeeeeooooooooooooow
If a hash of name/fn pairs is passed as the only argument, each name/fn pair will be added as methods.
schema.method({
purr: function () {}
, scratch: function () {}
});
// later
fizz.purr();
fizz.scratch();
name
, fn
)Adds static "class" methods to Models compiled from this schema.
show codeSchema.prototype.static = function(name, fn) {
if ('string' != typeof name)
for (var i in name)
this.statics[i] = name[i];
else
this.statics[name] = fn;
return this;
};
var schema = new Schema(..);
schema.static('findByName', function (name, callback) {
return this.find({ name: name }, callback);
});
var Drink = mongoose.model('Drink', schema);
Drink.findByName('sanpellegrino', function (err, drinks) {
//
});
If a hash of name/fn pairs is passed as the only argument, each name/fn pair will be added as statics.
fields
, [options]
)Defines an index (most likely compound) for this schema.
show codeSchema.prototype.index = function (fields, options) {
this._indexes.push([fields, options || {}]);
return this;
};
schema.index({ first: 1, last: -1 })
key
, [value]
)Sets/gets a schema option.
show codeSchema.prototype.set = function (key, value) {
if (arguments.length == 1)
return this.options[key];
this.options[key] = value;
return this;
};
Compiles indexes from fields and schema-level indexes
show codeSchema.prototype.indexes = function () {
var indexes = []
, seenSchemas = [];
collectIndexes(this);
return indexes;
function collectIndexes (schema, prefix) {
if (~seenSchemas.indexOf(schema)) return;
seenSchemas.push(schema);
var index;
var paths = schema.paths;
prefix = prefix || '';
for (var i in paths) {
if (paths[i]) {
if (paths[i] instanceof Types.DocumentArray) {
collectIndexes(paths[i].schema, i + '.');
} else {
index = paths[i]._index;
if (index !== false && index !== null){
var field = {};
field[prefix + i] = '2d' === index ? index : 1;
var options = 'Object' === index.constructor.name ? index : {};
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);
}
}
name
, options
)Creates a virtual type with the given name.
show codeSchema.prototype.virtual = function (name, options) {
var virtuals = this.virtuals;
var parts = name.split('.');
return virtuals[name] = parts.reduce(function (mem, part, i) {
mem[part] || (mem[part] = (i === parts.length-1)
? new VirtualType(options)
: {});
return mem[part];
}, this.tree);
};
name
)Returns the virtual type with the given name
.
Schema.prototype.virtualpath = function (name) {
return this.virtuals[name];
};
name
<String> These still haven't been fixed. Once they're working we'll make them public again.
show codeSchema.prototype.namedScope = function (name, fn) {
var namedScopes = this.namedScopes || (this.namedScopes = new NamedScope)
, newScope = Object.create(namedScopes)
, allScopes = namedScopes.scopesByName || (namedScopes.scopesByName = {});
allScopes[name] = newScope;
newScope.name = name;
newScope.block = fn;
newScope.query = new Query();
newScope.decorate(namedScopes, {
block0: function (block) {
return function () {
block.call(this.query);
return this;
};
},
blockN: function (block) {
return function () {
block.apply(this.query, arguments);
return this;
};
},
basic: function (query) {
return function () {
this.query.find(query);
return this;
};
}
});
return newScope;
};
ObjectId schema identifier. Not an actual ObjectId, only used for Schemas.
show codefunction ObjectId () {
throw new Error('This is an abstract interface. Its only purpose is to mark '
+ 'fields as ObjectId in the schema creation.');
}
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
new Schema({ _user: Schema.ObjectId })
Reserved document keys.
show codeSchema.reserved = Object.create(null);
var reserved = Schema.reserved;
reserved.on =
reserved.db =
reserved.init =
reserved.isNew =
reserved.errors =
reserved.schema =
reserved.options =
reserved.modelName =
reserved.collection = 1;
Keys in this object are names that are rejected in schema declarations b/c they conflict with mongoose functionality. Using these key name will throw an error.
on, db, init, isNew, errors, schema, options, modelName, collection
path
, obj
)Converts type arguments into Mongoose Types.
show codeSchema.interpretAsType = function (path, obj) {
if (obj.constructor.name != 'Object')
obj = { type: obj };
// Get the type making sure to allow keys named "type"
// and default to mixed if not specified.
// { type: { type: String, default: 'freshcut' } }
var type = obj.type && !obj.type.type
? obj.type
: {};
if ('Object' == type.constructor.name || 'mixed' == type) {
return new Types.Mixed(path, obj);
}
if (Array.isArray(type) || Array == type || 'array' == type) {
// if it was specified through { type } look for `cast`
var cast = (Array == type || 'array' == type)
? obj.cast
: type[0];
if (cast instanceof Schema) {
return new Types.DocumentArray(path, cast, obj);
}
if ('string' == typeof cast) {
cast = Types[cast.charAt(0).toUpperCase() + cast.substring(1)];
} else if (cast && (!cast.type || cast.type.type)
&& 'Object' == cast.constructor.name
&& Object.keys(cast).length) {
return new Types.DocumentArray(path, new Schema(cast), obj);
}
return new Types.Array(path, cast || Types.Mixed, obj);
}
var name = 'string' == typeof type
? type
: type.name;
if (name) {
name = name.charAt(0).toUpperCase() + name.substring(1);
}
if (undefined == Types[name]) {
throw new TypeError('Undefined type at `' + path +
'`
Did you try nesting Schemas? ' +
'You can only nest using refs or arrays.');
}
return new Types[name](path, obj);
};
Default model for querying the system.profiles collection.
path
, [options]
, [instance]
)SchemaType constructor
show codefunction 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] && 'function' == typeof this[i]) {
// { unique: true, index: true }
if ('index' == i && this._index) continue;
var opts = Array.isArray(options[i])
? options[i]
: [options[i]];
this[i].apply(this, opts);
}
};
val
)Sets a default value for this schematype.
show codeSchemaType.prototype.default = function (val) {
if (1 === arguments.length) {
this.defaultValue = typeof val === 'function'
? val
: this.cast(val);
return this;
} else if (arguments.length > 1) {
this.defaultValue = utils.args(arguments);
}
return this.defaultValue;
};
val
<any> default valueindex
)Declares an index for this schematype.
show codeSchemaType.prototype.index = function (index) {
this._index = index;
return this;
};
Schema.path('my.path').index(true);
Schema.path('my.path').index({ unique: true });
bool
)Declares an unique index.
show codeSchemaType.prototype.unique = function (bool) {
if (!this._index || 'Object' !== this._index.constructor.name) {
this._index = {};
}
this._index.unique = bool;
return this;
};
bool
<Boolean> bool
)Declares a sparse index.
show codeSchemaType.prototype.sparse = function (bool) {
if (!this._index || 'Object' !== this._index.constructor.name) {
this._index = {};
}
this._index.sparse = bool;
return this;
};
bool
<Boolean> fn
)Adds a setter to this schematype.
show codeSchemaType.prototype.set = function (fn) {
if ('function' != typeof fn)
throw new Error('A setter must be a function.');
this.setters.push(fn);
return this;
};
fn
<Function> fn
)Adds a getter to this schematype.
show codeSchemaType.prototype.get = function (fn) {
if ('function' != typeof fn)
throw new Error('A getter must be a function.');
this.getters.push(fn);
return this;
};
fn
<Function> obj
, [error]
)Adds validators to this schematype.
show codeSchemaType.prototype.validate = function (obj, error) {
if ('function' == typeof obj || obj && 'RegExp' === obj.constructor.name) {
this.validators.push([obj, error]);
return this;
}
var i = arguments.length
, arg
while (i--) {
arg = arguments[i];
this.validators.push([arg.validator, arg.msg]);
}
return this;
};
function validator () { ... }
var single = [validator, 'failed']
new Schema({ name: { type: String, validate: single }});
var many = [
{ validator: validator, msg: 'uh oh' }
, { validator: fn, msg: 'failed' }
]
new Schema({ name: { type: String, validate: many }});
required
)Adds a required validator to this schematype.
show codeSchemaType.prototype.required = function (required) {
var self = this;
function __checkRequired (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(self.path) &&
!this.isModified(self.path)) return true;
return self.checkRequired(v);
}
if (false === required) {
this.isRequired = false;
this.validators = this.validators.filter(function (v) {
return v[0].name !== '__checkRequired';
});
} else {
this.isRequired = true;
this.validators.push([__checkRequired, 'required']);
}
return this;
};
required
<Boolean> enable/disable the validatorscope
, init
)Gets the default value
show codeSchemaType.prototype.getDefault = function (scope, init) {
var ret = 'function' === typeof this.defaultValue
? this.defaultValue.call(scope)
: this.defaultValue;
if (null !== ret && undefined !== ret) {
return this.cast(ret, scope, init);
} else {
return ret;
}
};
value
, scope
, init
)Applies setters
show codeSchemaType.prototype.applySetters = function (value, scope, init) {
if (SchemaType._isRef(this, value, init)) return value;
var v = value
, setters = this.setters
, len = setters.length
if (!len) {
if (null === v || undefined === v) return v;
return init
? v // if we just initialized we dont recast
: this.cast(v, scope, init)
}
while (len--) {
v = setters[len].call(scope, v, this);
}
if (null === v || undefined === v) return v;
// do not cast until all setters are applied #665
v = this.cast(v, scope);
return v;
};
value
, scope
)Applies getters to a value
show codeSchemaType.prototype.applyGetters = function (value, scope) {
if (SchemaType._isRef(this, value, true)) return value;
var v = value
, getters = this.getters
, len = getters.length;
if (!len) {
return v;
}
while (len--) {
v = getters[len].call(scope, v, this);
}
return v;
};
val
)Sets default select()
behavior for this path.
SchemaType.prototype.select = function select (val) {
this.selected = !! val;
}
val
<Boolean> Set to true
if this path should always be included in the results, false
if it should be excluded by default. This setting can be overridden at the query level.
T = db.model('T', new Schema({ x: { type: String, select: true }}));
T.find(..); // field x will always be selected ..
// .. unless overridden;
T.find().select({ x: 0 }).exec();
value
, callback
, scope
)Performs a validation of value
using the validators declared for this SchemaType.
SchemaType.prototype.doValidate = function (value, fn, scope) {
var err = false
, path = this.path
, count = this.validators.length;
if (!count) return fn(null);
function validate (val, msg) {
if (err) return;
if (val === undefined || val) {
--count || fn(null);
} else {
fn(err = new ValidatorError(path, msg));
}
}
this.validators.forEach(function (v) {
var validator = v[0]
, message = v[1];
if (validator instanceof RegExp) {
validate(validator.test(value), message);
} else if ('function' === typeof validator) {
if (2 === validator.length) {
validator.call(scope, value, function (val) {
validate(val, message);
});
} else {
validate(validator.call(scope, value), message);
}
}
});
};
self
, value
, init
)Determines if value is a valid Reference.
show codeSchemaType._isRef = function (self, value, init) {
if (init && self.options && self.options.ref) {
if (null == value) return true;
if (value._id && value._id.constructor.name === self.instance) return true;
}
return false;
}
self
<SchemaType> value
<Object> init
<Boolean> values
, path
, doc
)Mongoose Array constructor.
show codefunction MongooseArray (values, path, doc) {
var arr = [];
arr.push.apply(arr, values);
arr.__proto__ = MongooseArray.prototype;
arr._atomics = {};
arr.validators = [];
arr._path = path;
if (doc) {
arr._parent = doc;
arr._schema = doc.schema.path(path);
}
return arr;
};
Values always have to be passed to the constructor to initialize, otherwise MongooseArray#push
will mark the array as modified.
value
)Casts a member based on this arrays schema.
show codeMongooseArray.prototype._cast = function (value) {
var cast = this._schema.caster.cast
, doc = this._parent;
return cast.call(null, value, doc);
};
value
<any> embeddedDoc
, embeddedPath
)Marks this array as modified.
show codeMongooseArray.prototype._markModified = function (embeddedDoc, embeddedPath) {
var parent = this._parent
, dirtyPath;
if (parent) {
if (arguments.length) {
// If an embedded doc bubbled up the change
dirtyPath = [this._path, this.indexOf(embeddedDoc), embeddedPath].join('.');
} else {
dirtyPath = this._path;
}
parent.markModified(dirtyPath);
}
return this;
};
embeddedDoc
<EmbeddedDocument> the embedded doc that invoked this method on the ArrayembeddedPath
<String> the path which changed in the embeddedDocIf it bubbles up from an embedded document change, then it takes the following arguments (otherwise, takes 0 arguments)
op
, val
)Register an atomic operation with the parent.
show codeMongooseArray.prototype._registerAtomic = function (op, val) {
if ('$set' == op) {
// $set takes precedence over all other ops.
// mark entire array modified.
this._atomics = { $set: val };
this._markModified();
return this;
}
var atomics = this._atomics;
// reset pop/shift after save
if ('$pop' == op && !('$pop' in atomics)) {
var self = this;
this._parent.once('save', function () {
self._popped = self._shifted = null;
});
}
if (this._atomics.$set) {
return this;
}
// check for impossible $atomic combos (Mongo denies more than one
// $atomic op on a single path
if (Object.keys(atomics).length && !(op in atomics)) {
// a different op was previously registered.
// save the entire thing.
this._atomics = { $set: this };
this._markModified();
return this;
}
if (op === '$pullAll' || op === '$pushAll' || op === '$addToSet') {
atomics[op] || (atomics[op] = []);
atomics[op] = atomics[op].concat(val);
} else if (op === '$pullDocs') {
var pullOp = atomics['$pull'] || (atomics['$pull'] = {})
, selector = pullOp['_id'] || (pullOp['_id'] = {'$in' : [] });
selector['$in'] = selector['$in'].concat(val);
} else {
atomics[op] = val;
}
this._markModified();
return this;
};
Returns the number of pending atomic operations to send to the db for this array.
show codeMongooseArray.prototype.hasAtomics = function hasAtomics () {
if (!(this._atomics && 'Object' === this._atomics.constructor.name)) {
return 0;
}
return Object.keys(this._atomics).length;
}
[args...]
)Wraps Array#push
with proper change tracking.
MongooseArray.prototype.push = function () {
var values = [].map.call(arguments, this._cast, this)
, ret = [].push.apply(this, values);
// $pushAll might be fibbed (could be $push). But it makes it easier to
// handle what could have been $push, $pushAll combos
this._registerAtomic('$pushAll', values);
return ret;
};
[args...]
<Object> [args...]
)Pushes items to the array non-atomically.
show codeMongooseArray.prototype.nonAtomicPush = function () {
var values = [].map.call(arguments, this._cast, this)
, ret = [].push.apply(this, values);
this._registerAtomic('$set', this);
return ret;
};
[args...]
<any> marks the entire array as modified, which if saved, will store it as a $set
operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
Pops the array atomically at most one time per document save()
.
Calling this mulitple times on an array before saving sends the same command as calling it once.
This update is implemented using the MongoDB $pop method.
Wraps Array#pop
with proper change tracking.
MongooseArray.prototype.pop = function () {
var ret = [].pop.call(this);
this._registerAtomic('$set', this);
return ret;
};
marks the entire array as modified which will pass the entire thing to $set potentially overwritting any changes that happen between when you retrieved the object and when you save it.
Atomically shifts the array at most one time per document save()
.
Calling this mulitple times on an array before saving sends the same command as calling it once.
This update is implemented using the MongoDB $pop method.
Wraps Array#shift
with proper change tracking.
MongooseArray.prototype.shift = function () {
var ret = [].shift.call(this);
this._registerAtomic('$set', this);
return ret;
};
marks the entire array as modified, which if saved, will store it as a $set
operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it.
[args...]
)Removes items from an array atomically
show codeMongooseArray.prototype.remove = function () {
var args = [].map.call(arguments, this._cast, this);
if (args.length == 1)
this.pull(args[0]);
else
this.pull.apply(this, args);
return args;
};
[args...]
<Object> values to removedoc.array.remove(ObjectId)
doc.array.remove('tag 1', 'tag 2')
[args...]
)Pulls items from the array atomically.
show codeMongooseArray.prototype.pull = function () {
var values = [].map.call(arguments, this._cast, this)
, cur = this._parent.get(this._path)
, i = cur.length
, mem;
while (i--) {
mem = cur[i];
if (mem instanceof EmbeddedDocument) {
if (values.some(function (v) { return v.equals(mem); } )) {
[].splice.call(cur, i, 1);
}
} else if (~cur.indexOf.call(values, mem)) {
[].splice.call(cur, i, 1);
}
}
if (values[0] instanceof EmbeddedDocument) {
this._registerAtomic('$pullDocs', values.map( function (v) { return v._id; } ));
} else {
this._registerAtomic('$pullAll', values);
}
return this;
};
[args...]
<any> Wraps Array#splice
with proper change tracking.
MongooseArray.prototype.splice = function () {
if (arguments.length) {
var ret = [].splice.apply(this, arguments);
this._registerAtomic('$set', this);
}
return ret;
};
marks the entire array as modified, which if saved, will store it as a $set
operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it.
Wraps Array#unshift
with proper change tracking.
MongooseArray.prototype.unshift = function () {
var values = [].map.call(arguments, this._cast, this);
[].unshift.apply(this, values);
this._registerAtomic('$set', this);
return this.length;
};
marks the entire array as modified, which if saved, will store it as a $set
operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it.
Wraps Array#sort
with proper change tracking.
MongooseArray.prototype.sort = function () {
var ret = [].sort.apply(this, arguments);
this._registerAtomic('$set', this);
return ret;
}
marks the entire array as modified, which if saved, will store it as a $set
operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it.
[args...]
)Adds values to the array if not already present.
show codeMongooseArray.prototype.addToSet = function addToSet () {
var values = [].map.call(arguments, this._cast, this)
, added = []
, type = values[0] instanceof EmbeddedDocument ? 'doc' :
values[0] instanceof Date ? 'date' :
'';
values.forEach(function (v) {
var found;
switch (type) {
case 'doc':
found = this.some(function(doc){ return doc.equals(v) });
break;
case 'date':
var val = +v;
found = this.some(function(d){ return +d === val });
break;
default:
found = ~this.indexOf(v);
}
if (!found) {
[].push.call(this, v);
this._registerAtomic('$addToSet', v);
[].push.call(added, v);
}
}, this);
return added;
};
[args...]
<any> console.log(doc.array) // [2,3,4]
var added = doc.array.addToSet(4,5);
console.log(doc.array) // [2,3,4,5]
console.log(added) // [5]
options
)Returns a native js Array.
show codeMongooseArray.prototype.toObject = function (options) {
if (options && options.depopulate && this[0] instanceof Document) {
return this.map(function (doc) {
return doc._id;
});
}
// return this.slice()?
return this.map(function (doc) {
return doc;
});
};
options
<Object> Helper for console.log
show codeMongooseArray.prototype.inspect = function () {
return '[' + this.map(function (doc) {
return ' ' + doc;
}) + ' ]';
};
obj
)Return the index of obj
or -1
if not found.
MongooseArray.prototype.indexOf = function indexOf (obj) {
if (obj instanceof ObjectId) obj = obj.toString();
for (var i = 0, len = this.length; i < len; ++i) {
if (obj == this[i])
return i;
}
return -1;
};
obj
<Object> the item to look forParent owner document
Stores a queue of atomic operations to perform
value
, encode
, offset
)Mongoose Buffer constructor.
show codefunction MongooseBuffer (value, encode, offset) {
var length = arguments.length;
var val;
if (0 === length || null === arguments[0] || undefined === arguments[0]) {
val = 0;
} else {
val = value;
}
var encoding;
var path;
var doc;
if (Array.isArray(encode)) {
// internal casting
path = encode[0];
doc = encode[1];
} else {
encoding = encode;
}
var buf = new Buffer(val, encoding, offset);
buf.__proto__ = MongooseBuffer.prototype;
// make sure these internal props don't show up in Object.keys()
Object.defineProperties(buf, {
validators: { value: [] }
, _path: { value: path }
, _parent: { value: doc }
});
if (doc && "string" === typeof path) {
Object.defineProperty(buf, '_schema', {
value: doc.schema.path(path)
});
}
return buf;
};
Values always have to be passed to the constructor to initialize.
Marks this buffer as modified.
show codeMongooseBuffer.prototype._markModified = function () {
var parent = this._parent;
if (parent) {
parent.markModified(this._path);
}
return this;
};
Writes the buffer.
show codeMongooseBuffer.prototype.write = function () {
var written = Buffer.prototype.write.apply(this, arguments);
if (written > 0) {
this._markModified();
}
return written;
};
target
)Copies the buffer.
show codeMongooseBuffer.prototype.copy = function (target) {
var ret = Buffer.prototype.copy.apply(this, arguments);
if (target instanceof MongooseBuffer) {
target._markModified();
}
return ret;
};
target
<Buffer> Buffer#copy
does not mark target
as modified so you must copy from a MongooseBuffer
for it to work as expected. This is a work around since copy
modifies the target, not this.
[subtype]
)Converts this buffer to its Binary type representation.
show codeMongooseBuffer.prototype.toObject = function (subtype) {
subtype = typeof subtype !== 'undefined' ? subtype : 0x00
return new Binary(this, subtype);
};
[subtype]
<Hex> Parent owner document
values
, path
, doc
)DocumentArray constructor
show codefunction MongooseDocumentArray (values, path, doc) {
var arr = [];
// Values always have to be passed to the constructor to initialize, since
// otherwise MongooseArray#push will mark the array as modified to the parent.
arr.push.apply(arr, values);
arr.__proto__ = MongooseDocumentArray.prototype;
arr._atomics = {};
arr.validators = [];
arr._path = path;
if (doc) {
arr._parent = doc;
arr._schema = doc.schema.path(path);
doc.on('save', arr.notify('save'));
doc.on('isNew', arr.notify('isNew'));
}
return arr;
};
Overrides MongooseArray#cast
show codeMongooseDocumentArray.prototype._cast = function (value) {
var doc = new this._schema.casterConstructor(value, this);
return doc;
};
id
)Searches array items for the first document with a matching id.
show codeMongooseDocumentArray.prototype.id = function (id) {
var casted
, _id;
try {
casted = ObjectId.toString(ObjectIdSchema.prototype.cast.call({}, id));
} catch (e) {
casted = null;
}
for (var i = 0, l = this.length; i < l; i++) {
_id = this[i].get('_id');
if (!(_id instanceof ObjectId)) {
if (String(id) == _id)
return this[i];
} else {
if (casted == _id)
return this[i];
}
}
return null;
};
Returns a native js Array of plain js objects
show codeMongooseDocumentArray.prototype.toObject = function () {
return this.map(function (doc) {
return doc && doc.toObject() || null;
});
};
Each sub-document is converted to a plain object by calling its #toObject
method.
Helper for console.log
show codeMongooseDocumentArray.prototype.inspect = function () {
return '[' + this.map(function (doc) {
if (doc) {
return doc.inspect
? doc.inspect()
: util.inspect(doc)
}
return 'null'
}).join('
') + ']';
};
obj
)Creates a subdocument casted to this schema.
show codeMongooseDocumentArray.prototype.create = function (obj) {
return new this._schema.casterConstructor(obj);
}
obj
<Object> the value to cast to this arrays SubDocument schemaThis is the same subdocument constructor used for casting.
event
)Creates a fn that notifies all child docs of event
.
MongooseDocumentArray.prototype.notify = function notify (event) {
var self = this;
return function notify (val) {
var i = self.length;
while (i--) {
self[i].emit(event, val);
}
}
}
event
<String> obj
, parentArr
, skipId
)EmbeddedDocument constructor.
show codefunction EmbeddedDocument (obj, parentArr, skipId) {
if (parentArr) {
this.__parentArray = parentArr;
this.__parent = parentArr._parent;
} else {
this.__parentArray = undefined;
this.__parent = undefined;
}
Document.call(this, obj, undefined, skipId);
var self = this;
this.on('isNew', function (val) {
self.isNew = val;
});
};
obj
<Object> js object returned from the dbparentArr
<MongooseDocumentArray> the parent array of this documentskipId
<Boolean> path
)Marks the embedded doc modified.
show codeEmbeddedDocument.prototype.markModified = function (path) {
if (!this.__parentArray) return;
this._activePaths.modify(path);
if (this.isNew) {
// Mark the WHOLE parent array as modified
// if this is a new document (i.e., we are initializing
// a document),
this.__parentArray._markModified();
} else
this.__parentArray._markModified(this, path);
};
// TODO remove this
EmbeddedDocument.prototype.commit = EmbeddedDocument.markModified
path
<String> the path which changedvar doc = blogpost.comments.id(hexstring);
doc.mixed.type = 'changed';
doc.markModified('mixed.type');
[fn]
)Used as a stub for hooks.js
show codeEmbeddedDocument.prototype.save = function(fn) {
if (fn)
fn(null);
return this;
};
[fn]
<Function> This is a no-op. Does not actually save the doc to the db.
[fn]
)Removes the subdocument from its parent array.
show codeEmbeddedDocument.prototype.remove = function (fn) {
if (!this.__parentArray) return this;
var _id;
if (!this.willRemove) {
_id = this._doc._id;
if (!_id) {
throw new Error('For your own good, Mongoose does not know ' +
'how to remove an EmbeddedDocument that has no _id');
}
this.__parentArray.pull({ _id: _id });
this.willRemove = true;
}
if (fn)
fn(null);
return this;
};
[fn]
<Function> Helper for console.log
show codeEmbeddedDocument.prototype.inspect = function () {
return inspect(this.toObject());
};
path
, err
)Marks a path as invalid, causing validation to fail.
show codeEmbeddedDocument.prototype.invalidate = function (path, err) {
if (!this.__parent) return false;
var index = this.__parentArray.indexOf(this);
var parentPath = this.__parentArray._path;
var fullPath = [parentPath, index, path].join('.');
this.__parent.invalidate(fullPath, err);
return true;
}
str
)Creates an ObjectId from str
ObjectId.fromString;
oid
)Converts oid
to a string.
ObjectId.toString;
oid
<ObjectId> ObjectId instancename
)Produces a collection name from model name
.
exports.toCollectionName = function (name) {
if ('system.profile' === name) return name;
if ('system.indexes' === name) return name;
return pluralize(name.toLowerCase());
};
name
<String> a model namePluralization rules.
show codeexports.pluralization = [
[/(m)an$/gi, '$1en'],
[/(pe)rson$/gi, '$1ople'],
[/(child)$/gi, '$1ren'],
[/^(ox)$/gi, '$1en'],
[/(ax|test)is$/gi, '$1es'],
[/(octop|vir)us$/gi, '$1i'],
[/(alias|status)$/gi, '$1es'],
[/(bu)s$/gi, '$1ses'],
[/(buffal|tomat|potat)o$/gi, '$1oes'],
[/([ti])um$/gi, '$1a'],
[/sis$/gi, 'ses'],
[/(?:([^f])fe|([lr])f)$/gi, '$1$2ves'],
[/(hive)$/gi, '$1s'],
[/([^aeiouy]|qu)y$/gi, '$1ies'],
[/(x|ch|ss|sh)$/gi, '$1es'],
[/(matr|vert|ind)ix|ex$/gi, '$1ices'],
[/([m|l])ouse$/gi, '$1ice'],
[/(quiz)$/gi, '$1zes'],
[/s$/gi, 's'],
[/$/gi, 's']
];
var rules = exports.pluralization;
These rules are applied while processing the argument to toCollectionName
.
Uncountable words.
show codeexports.uncountables = [
'advice',
'energy',
'excretion',
'digestion',
'cooperation',
'health',
'justice',
'labour',
'machinery',
'equipment',
'information',
'pollution',
'sewage',
'paper',
'money',
'species',
'series',
'rain',
'rice',
'fish',
'sheep',
'moose',
'deer',
'news'
];
var uncountables = exports.uncountables;
These words are applied while processing the argument to toCollectionName
.
a
, b
)Determines if a
and b
are deep equal.
exports.deepEqual = 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) {
return a.toString() === b.toString();
}
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)) {
if (!Buffer.isBuffer(b)) return false;
if (a.length !== b.length) return false;
for (var i = 0, len = a.length; i < len; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
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;
};
Modified from node/lib/assert.js
obj
, options
)Object clone with Mongoose natives support.
show codeexports.clone = 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 && 'function' === typeof obj.toJSON) {
return obj.toJSON(options);
} else {
return obj.toObject(options);
}
}
if ('Object' === obj.constructor.name)
return cloneObject(obj, options);
if ('Date' === obj.constructor.name || 'Function' === obj.constructor.name)
return new obj.constructor(+obj);
if ('RegExp' === obj.constructor.name)
return new RegExp(obj.source);
if (obj instanceof ObjectId) {
return new ObjectId(obj.id);
}
if (obj.valueOf)
return obj.valueOf();
};
var clone = exports.clone;
Creates a minimal data Object.
It does not clone empty Arrays, empty Objects, and undefined values.
This makes the data payload sent to MongoDB as minimal as possible.
defaults
, options
)Copies and merges options with defaults.
show codeexports.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;
};
Generates a random string
show codeexports.random = function () {
return Math.random().toString().substr(3);
};
to
, from
)Merges from
into to
without overwriting existing properties.
exports.merge = function merge (to, from) {
var keys = Object.keys(from)
, i = keys.length
, key
while (i--) {
key = keys[i];
if ('undefined' === typeof to[key]) {
to[key] = from[key];
} else {
merge(to[key], from[key]);
}
}
};
A faster Array.prototype.slice.call(arguments) alternative
show codeexports.args = function (args, slice, sliceEnd) {
var ret = [];
var start = slice || 0;
var end = 3 === arguments.length
? sliceEnd
: args.length;
for (var i = start; i < end; ++i) {
ret[i - start] = args[i];
}
return ret;
}
callback
)process.nextTick helper.
show codeexports.tick = function tick (callback) {
if ('function' !== typeof callback) 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;
});
}
}
}
callback
<Function> Wraps callback
in a try/catch + nextTick.
node-mongodb-native has a habit of state corruption when an error is immediately thrown from within a collection callback.
v
)Returns if v
is a mongoose object that has a toObject()
method we can use.
exports.isMongooseObject = function (v) {
Document || (Document = require('./document'));
MongooseArray || (MongooseArray = require('./types').Array);
MongooseBuffer || (MongooseBuffer = require('./types').Buffer);
return v instanceof Document ||
v instanceof MongooseArray ||
v instanceof MongooseBuffer
}
var isMongooseObject = exports.isMongooseObject;
v
<any> This is for compatibility with libs like Date.js which do foolish things to Natives.
VirtualType constructor
show codefunction VirtualType (options) {
this.getters = [];
this.setters = [];
this.options = options || {};
}
This is what mongoose uses to define virtual attributes via Schema.prototype.virtual
.
var fullname = schema.virtual('fullname');
fullname instanceof mongoose.VirtualType // true
fn
)Defines a getter.
show codeVirtualType.prototype.get = function (fn) {
this.getters.push(fn);
return this;
};
fn
<Function> var virtual = schema.virtual('fullname');
virtual.get(function () {
return this.name.first + ' ' + this.name.last;
});
fn
)Defines a setter.
show codeVirtualType.prototype.set = function (fn) {
this.setters.push(fn);
return this;
};
fn
<Function> var virtual = schema.virtual('fullname');
virtual.set(function (v) {
var parts = v.split(' ');
this.name.first = parts[0];
this.name.last = parts[1];
});
value
, scope
)Applies getters to value
using optional scope
.
VirtualType.prototype.applyGetters = function (value, scope) {
var v = value;
for (var l = this.getters.length - 1; l >= 0; l--){
v = this.getters[l].call(scope, v);
}
return v;
};
value
, scope
)Applies setters to value
using optional scope
.
VirtualType.prototype.applySetters = function (value, scope) {
var v = value;
for (var l = this.setters.length - 1; l >= 0; l--){
this.setters[l].call(scope, v);
}
return v;
};