Fork me on GitHub Mongoose

Mongoose

Expressive MongoDB for Node.JS

collection

lib/collection.js

Collection constructor

  • param: String collection name

  • param: Collection connection object

  • api: public

function Collection (name, conn) {
  this.name = name;
  this.conn = conn;
  this.buffer = true;
  this.queue = [];
  if (this.conn.readyState == 1) this.onOpen();
};

The collection name

  • api: public

Collection.prototype.name;

The Connection instance

  • api: public

Collection.prototype.conn;

Module exports.

module.exports = Collection;

connection

lib/connection.js

Module dependencies.

var url = require('url')
  , utils = require('./utils')
  , EventEmitter = utils.EventEmitter
  , driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native'
  , Model = require('./model')
  , Schema = require('./schema')
  , Collection  = require(driver + '/collection');

Connection constructor. For practical reasons, a Connection equals a Db

  • param: Mongoose mongoose base

  • api: public

function Connection (base) {
  this.base = base;
  this.collections = {};
  this.models = {};
};

Inherit from EventEmitter.

Connection.prototype.__proto__ = EventEmitter.prototype;

Connection ready state: 0 = Disconnected 1 = Connected 2 = Connecting 3 = Disconnecting

  • api: public

Connection.prototype.readyState = 0;

A hash of the collections associated with this connection

Connection.prototype.collections;

The mongodb.Db instance, set when the connection is opened

  • api: public

Connection.prototype.db;

Establishes the connection

options is a hash with the following optional properties:

options.db - passed to the connection db instance options.server - passed to the connection server instance(s) options.replset - passed to the connection ReplSetServer instance options.user - username for authentication options.pass - password for authentication

Notes:

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.

  • param: String mongodb://uri

  • return: Connection self

  • see: https ://github.com/christkv/node-mongodb-native

  • api: public

Connection.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;
    }

    uri = url.parse(host);
    host = uri.hostname;
    port = uri.port || 27017;
    database = uri.pathname && uri.pathname.replace(/\//g, '');
  }

  callback = callback || noop;
  this.options = this.defaultOptions(options);

  // make sure we can open
  if (0 !== this.readyState) {
    var err = new Error('Trying to open unclosed connection.');
    err.state = this.readyState;
    callback(err);
    return this;
  }

  if (!host) {
    callback(new Error('Missing connection hostname.'));
    return this;
  }

  if (!database) {
    callback(new Error('Missing connection database.'));
    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;

  // signal connecting
  this.readyState = 2;
  this.emit('opening');

  // open connection
  this.doOpen(function (err) {
    if (err) {
      self.readyState = 0;
      if (self._events && self._events.error &&
         ('function' == typeof self._events.error || self._events.error.length)) {
        self.emit("error", err);
      }
    } else {
      self.onOpen();
    }

    callback(err || null);
  });

  return this;
};

Connects to a replica set.

Supply a comma-separted list of mongodb:// URIs. You only need to specify the database name and/or auth to one of them.

The options parameter is passed to the low level connection. See the node-mongodb-native driver instance for detail.

  • param: String comma-separated mongodb:// URIs

  • param: String optional database name

  • param: Object optional options

  • param: Function optional callback

Connection.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);
  callback = callback || noop;

  if (uris.length < 2) {
    callback(new Error('Please provide comma-separated URIs'));
    return this;
  }

  this.host = [];
  this.port = [];

  uris.forEach(function (uri) {
    var uri = url.parse(uri);

    self.host.push(uri.hostname);
    self.port.push(uri.port || 27017);

    if (!self.name && 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) {
    callback(new Error('No database name provided for replica set'));
    return this;
  }

  this.readyState = 2;
  this.emit('opening');

  // open connection
  this.doOpenSet(function (err) {
    if (err) {
      if (self._events && self._events.error && self._events.error.length) {
        self.emit("error", err);
      }
      self.readyState = 0;
    } else {
      self.onOpen();
    }

    callback(err || null);
  });
};

Closes the connection

  • param: Function optional callback

  • return: Connection self

  • api: public

Connection.prototype.close = function (callback) {
  var self = this
    , callback = callback || function(){};

  switch (this.readyState){
    case 0: // disconnected
      callback(null);
      break;

    case 1: // connected
      this.readyState = 3;
      this.doClose(function(err){
        if (err){
          callback(err);
        } else {
          self.onClose();
          callback(null);
        }
      });
      break;

    case 2: // connecting
      this.once('open', function(){
        self.close(callback);
      });
      break;

    case 3: // disconnecting
      this.once('close', function () {
        callback(null);
      });
      break;
  }

  return this;
};

Retrieves a collection, creating it if not cached.

  • param: String collection name

  • return: Collection collection instance

  • api: public

Connection.prototype.collection = function (name) {
  if (!(name in this.collections))
    this.collections[name] = new Collection(name, this);
  return this.collections[name];
};

Defines a model or retrieves it

  • param: String model name

  • param: Schema schema object

  • param: String collection name (optional, induced from model name)

  • api: public

Connection.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 () {
        model.apply(this, arguments);
      };

      Model.__proto__ = model;
      Model.prototype.__proto__ = model.prototype;
      Model.prototype.db = this;

      // collection name discovery
      if ('string' === typeof schema) {
        collection = schema;
      }

      if (!collection) {
        collection = model.prototype.schema.set('collection') || utils.toCollectionName(name);
      }

      Model.prototype.collection = this.collection(collection);
      Model.init();
    }

    this.models[name] = Model || model;
  }

  return this.models[name];
};

Set profiling level.

  • param: Int | String level - Either off (0), slow (1), or all (2)

  • param: Int [ms] If profiling level is set to 1, this determines

               the threshold in milliseconds above which queries
               will be logged. Defaults to 100. (optional)
  • param: Function callback

  • api: public

Connection.prototype.setProfiling = function (level, ms, callback) {
  if (1 !== 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);
  });
};

Noop.

function noop () {}

Module exports.

module.exports = Connection;

document

lib/document.js

Module dependencies.

var EventEmitter = require('events').EventEmitter
  , MongooseError = require('./error')
  , MixedSchema = require('./schema/mixed')
  , Schema = require('./schema')
  , ValidatorError = require('./schematype').ValidatorError
  , utils = require('./utils')
  , clone = utils.clone
  , isMongooseObject = utils.isMongooseObject
  , inspect = require('util').inspect
  , StateMachine = require('./statemachine')
  , ActiveRoster = StateMachine.ctor('require', 'modify', 'init', 'default')
  , deepEqual = utils.deepEqual
  , hooks = require('hooks')
  , DocumentArray

Inherit from EventEmitter.

Document.prototype.__proto__ = EventEmitter.prototype;

Base Mongoose instance for the model. Set by the Mongoose instance upon pre-compilation.

  • api: public

Document.prototype.base;

Document schema as a nested structure.

  • api: public

Document.prototype.schema;

Whether the document is new.

  • api: public

Document.prototype.isNew;

Validation errors.

  • api: public

Document.prototype.errors;

Sets a path, or many paths

Examples

// path, value
doc.set(path, value)

// object
doc.set({
    path  : value
  , path2 : {
       path  : value
    }
}

  • param: String | Object key path, or object

  • param: Object value, or undefined or a prefix if first parameter is an object

  • para: m @optional {Schema|String|...} specify a type if this is an on-the-fly attribute

  • api: public

Document.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 (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('.')
    , obj = this._doc
    , self = this
    , pathToMark
    , subpaths
    , subpath

  // 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 {
    subpaths = parts.map(function (part, i) {
      return parts.slice(0, i).concat(part).join('.');
    });

    for (var i = 0, l = subpaths.length; i < l; i++) {
      subpath = subpaths[i];
      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.try(function(){
      // if this doc is being constructed we should not
      // trigger getters.
      var cur = constructing ? undefined : self.get(path);
      var casted = schema.cast(val, self, false, cur);
      val = schema.applySetters(casted, self);
    })) {

    if (this.isNew) {
      this.markModified(pathToMark);
    } else {
      var 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);
        } else if (!deepEqual(val, priorVal)) {
          this.markModified(pathToMark);
        } 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);
        }
      }
    }

    for (var i = 0, l = parts.length; 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]] = {};
        }
      }
    }
  }

  return this;
};

Triggers casting on a specific path

  • tod: o
  • deprecate? not used anywhere ##

  • param: String path

  • api: public

Document.prototype.doCast = function (path) {
  var schema = this.schema.path(path);
  if (schema)
    this.setValue(path, this.getValue(path));
};

Gets a path

  • param: String key path

  • para: m @optional {Schema|String|...} specify a type if this is an on-the-fly attribute

  • api: public

Document.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;
};

Commits a path, marking as modified if needed. Useful for mixed keys

  • api: public

Document.prototype.markModified = function (path) {
  this._activePaths.modify(path);
};

commit

Alias on markModified

  • deprecate: d

Document.prototype.commit =
  utils.dep('Document#commit'
          , 'Document#markModified'
          , Document.prototype.markModified);

Captures an exception that will be bubbled to save

  • param: Function function to execute

  • param: Object scope

Document.prototype.try = function (fn, scope) {
  var res;
  try {
    fn.call(scope);
    res = true;
  } catch (e) {
    this.error(e);
    res = false;
  }
  return res;
};

modifiedPaths

Returns the list of paths that have been modified.

If we set documents.0.title to 'newTitle' then documents, documents.0, and documents.0.title are modified.

  • api: public

  • returns: Boolean

Document.prototype.__defineGetter__('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('.'));
    }, []));
  }, []);
});

Checks if a path or any full path containing path as part of its path chain has been directly modified.

e.g., if we set documents.0.title to 'newTitle' then we have directly modified documents.0.title but not directly modified documents or documents.0. Nonetheless, we still say documents and documents.0 are modified. They just are not considered direct modified. The distinction is important because we need to distinguish between what has been directly modified and what hasn't so that we can determine the MINIMUM set of dirty data that we want to send to MongoDB on a Document save.

  • param: String path

  • returns: Boolean

  • api: public

Document.prototype.isModified = function (path) {
  return !!~this.modifiedPaths.indexOf(path);
};

Checks if a path has been directly set and modified. False if the path is only part of a larger path that was directly set.

e.g., if we set documents.0.title to 'newTitle' then we have directly modified documents.0.title but not directly modified documents or documents.0. Nonetheless, we still say documents and documents.0 are modified. They just are not considered direct modified. The distinction is important because we need to distinguish between what has been directly modified and what hasn't so that we can determine the MINIMUM set of dirty data that we want to send to MongoDB on a Document save.

  • param: String path

  • returns: Boolean

  • api: public

Document.prototype.isDirectModified = function (path) {
  return (path in this._activePaths.states.modify);
};

Checks if a certain path was initialized

  • param: String path

  • returns: Boolean

  • api: public

Document.prototype.isInit = function (path) {
  return (path in this._activePaths.states.init);
};

Checks if a path was selected. ##

  • param: String path

  • return: Boolean

  • api: public

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;

    while (i--) {
      cur = paths[i];
      if ('_id' == cur) continue;

      if (0 === cur.indexOf(path + '.')) {
        return inclusive;
      }

      if (0 === (path + '.').indexOf(cur)) {
        return inclusive;
      }
    }

    return ! inclusive;
  }

  return true;
}

Validation middleware

  • param: Function next

  • api: public

Document.prototype.validate = function (next) {
  var total = 0
    , self = this
    , validating = {}

  if (!this._activePaths.some('require', 'init', 'modify')) {
    return complete();
  }

  function complete () {
    next(self._validationError);
    self._validationError = null;
  }

  this._activePaths.forEach('require', 'init', 'modify', 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);
    });
  });

  return this;
};

Marks a path as invalid, causing a subsequent validation to fail.

  • param: String path of the field to invalidate

  • param: String | Error error of the path.

  • api: public

Document.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;
}

Returns if the document has been modified

  • return: Boolean

  • api: public

Document.prototype.__defineGetter__('modified', function () {
  return this._activePaths.some('modify');
});

Gets the document

Available options:

  • getters: apply all getters (path and virtual getters)
  • virtuals: apply virtual getters (can override getters option)

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 })

  • return: Object plain object

  • api: public

Document.prototype.toObject = function (options) {
  options || (options = {});
  options.minimize = true;

  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;
};

JSON.stringify helper.

Implicitly called when a document is passed to JSON.stringify()

  • return: Object

  • api: public

Document.prototype.toJSON = function (options) {
  if ('undefined' === typeof options) options = {};
  options.json = true;
  return this.toObject(options);
};

Helper for console.log

  • api: public

Document.prototype.toString =
Document.prototype.inspect = function (options) {
  return inspect(this.toObject(options));
};

Returns true if the Document stores the same data as doc. ##

  • param: Document doc to compare to

  • return: Boolean

  • api: public

Document.prototype.equals = function (doc) {
  return this.get('_id') === doc.get('_id');
};

Module exports.

module.exports = Document;

Document Validation Error

function ValidationError (instance) {
  MongooseError.call(this, "Validation failed");
  Error.captureStackTrace(this, arguments.callee);
  this.name = 'ValidationError';
  this.errors = instance.errors = {};
};

ValidationError.prototype.toString = function () {
  return this.name + ': ' + Object.keys(this.errors).map(function (key) {
    return String(this.errors[key]);
  }, this).join(', ');
};

Inherits from MongooseError.

ValidationError.prototype.__proto__ = MongooseError.prototype;

Document.ValidationError = ValidationError;

Document Error

  • param: text

function DocumentError () {
  MongooseError.call(this, msg);
  Error.captureStackTrace(this, arguments.callee);
  this.name = 'DocumentError';
};

Inherits from MongooseError.

DocumentError.prototype.__proto__ = MongooseError.prototype;

exports.Error = DocumentError;

binary

lib/drivers/node-mongodb-native/binary.js

Module dependencies.

var Binary = require('mongodb').BSONPure.Binary;

module.exports = exports = Binary;

collection

lib/drivers/node-mongodb-native/collection.js

Module dependencies.

var Collection = require('../../collection')
  , NativeCollection = require('mongodb').Collection
  , utils = require('../../utils')

Inherit from abstract Collection.

MongooseCollection.prototype.__proto__ = Collection.prototype;

Copy the collection methods and make them subject to queues

for (var i in NativeCollection.prototype)
  (function(i){
    MongooseCollection.prototype[i] = function () {
      // BENCHMARKME: is it worth caching the prototype methods? probably
      if (!this.buffer) {
        var collection = this.collection
          , args = arguments
          , self = this;

        process.nextTick(function(){
          var debug = self.conn.base.options.debug;

          if (debug) {
            if ('function' === typeof debug) {
              debug.apply(debug
                , [self.name, i].concat(utils.args(args, 0, args.length-1)));
            } else {
              console.error('\x1B[0;36mMongoose:\x1B[0m %s.%s(%s) %s %s %s'
                , self.name
                , i
                , print(args[0])
                , print(args[1])
                , print(args[2])
                , print(args[3]))
            }
          }

          collection[i].apply(collection, args);
        });
      } else {
        this.addQueue(i, arguments);
      }
    };
  })(i);

Override signature of ensureIndex. -native one is not standard.

  • param: Object spec

  • param: Object options

  • param: Function callback

  • api: public

var oldEnsureIndex = NativeCollection.prototype.ensureIndex;

function noop () {};

NativeCollection.prototype.ensureIndex = function(fields, options, fn){
  if (!this.buffer) {
    return oldEnsureIndex.apply(this, arguments);
  }
};

Module exports.

module.exports = MongooseCollection;

connection

lib/drivers/node-mongodb-native/connection.js

Module dependencies.

var Connection = require('../../connection')
  , mongo = require('mongodb')
  , Server = mongo.Server
  , ReplSetServers = mongo.ReplSetServers;

Inherits from Connection.

NativeConnection.prototype.__proto__ = Connection.prototype;

Module exports.

module.exports = NativeConnection;

objectid

lib/drivers/node-mongodb-native/objectid.js

Module dependencies.

var ObjectId = require('mongodb').BSONPure.ObjectID;

error

lib/error.js

Inherits from Error.

MongooseError.prototype.__proto__ = Error.prototype;

Module exports.

module.exports = MongooseError;

index

lib/index.js

Module dependencies.

var Schema = require('./schema')
  , SchemaType = require('./schematype')
  , VirtualType = require('./virtualtype')
  , SchemaTypes = Schema.Types
  , SchemaDefaults = require('./schemadefault')
  , Types = require('./types')
  , Query = require('./query')
  , Promise = require('./promise')
  , Model = require('./model')
  , Document = require('./document')
  , utils = require('./utils');

Mongoose constructor. Most apps will only use one instance.

  • api: public

function Mongoose () {
  this.connections = [];
  this.plugins = [];
  this.models = {};
  this.modelSchemas = {};
  this.options = {};
  this.createConnection(); // default connection
};

Sets/gets mongoose options

Examples

mongoose.set('test') // returns the 'test' value mongoose.set('test', value) // sets the 'test' value

  • param: String key

  • param: String value

  • api: public

Mongoose.prototype.set =
Mongoose.prototype.get = function (key, value) {
  if (arguments.length == 1)
    return this.options[key];
  this.options[key] = value;
  return this;
};

Creates a Connection instance.

Examples

// 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);

  • param: String mongodb:// URI

  • return: Connection the created Connection object

  • api: public

Mongoose.prototype.createConnection = function () {
  var conn = new Connection(this);
  this.connections.push(conn);
  if (arguments.length)
    conn.open.apply(conn, arguments);
  return conn;
};

Creates a replica set connection

  • see: Mongoose#createConnection

  • api: public

  • deprecate: d

function createSetConnection () {
  var conn = new Connection(this);
  this.connections.push(conn);
  if (arguments.length)
    conn.openSet.apply(conn, arguments);
  return conn;
};

Mongoose.prototype.createSetConnection =
  utils.dep('Mongoose#createSetConnection'
          , 'Mongoose#createConnection in v3'
          , createSetConnection);

Connects the default mongoose connection

  • see: Mongoose#createConnection

  • api: public

Mongoose.prototype.connect = function (){
  this.connection.open.apply(this.connection, arguments);
  return this;
};

Connects the default mongoose connection to a replica set

  • see: Mongoose#createSetConnection

  • api: public

  • deprecate: d

function connectSet () {
  this.connection.openSet.apply(this.connection, arguments);
  return this;
};
Mongoose.prototype.connectSet =
  utils.dep('Mongoose#connectSet', 'Mongoose#connect in v3', connectSet);

Disconnects from all connections.

  • param: Function optional callback

  • api: public

Mongoose.prototype.disconnect = function (fn) {
  var count = this.connections.length;
  this.connections.forEach(function(conn){
    conn.close(function(err){
      if (err) return fn(err);
      if (fn)
        --count || fn();
    });
  });
  return this;
};

Defines a model or retrieves it

  • param: String model name

  • param: Schema schema object

  • param: String collection name (optional, induced from model name)

  • param: Boolean whether to skip initialization (defaults to false)

  • api: public

Mongoose.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(t been registered for model "' + name + '".\n'
                    + 'Use mongoose.model(name, schema)');
    }
  }

  if (!schema) {
    schema = this.modelSchemas[name];
  }

  if (!collection) {
    collection = schema.set('collection') || utils.toCollectionName(name);
  }

  if (!this.models[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];
};

Declares a plugin executed on Schemas. Equivalent to calling .plugin(fn) on each Schema you create.

  • param: Function plugin callback

  • api: public

Mongoose.prototype.plugin = function (fn, opts) {
  this.plugins.push([fn, opts]);
  return this;
};

Default connection

  • api: public

Mongoose.prototype.__defineGetter__('connection', function(){
  return this.connections[0];
});

Driver depentend APIs

var driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native';

Connection

  • api: public

var Connection = require(driver + '/connection');

Collection

  • api: public

var Collection = require(driver + '/collection');

Export default singleton.

  • api: public

module.exports = exports = new Mongoose();

Collection

  • api: public

exports.Collection = Collection;

Connection

  • api: public

exports.Connection = Connection;

Exports Mongoose version

  • param: version

exports.version = JSON.parse(
  require('fs').readFileSync(__dirname + '/../package.json', 'utf8')
).version;

Export Mongoose constructor

  • api: public

exports.Mongoose = Mongoose;

Export Schema constructor

  • api: public

exports.Schema = Schema;

Export SchemaType constructor.

  • api: public

exports.SchemaType = SchemaType;

Export VirtualType constructor.

  • api: public

exports.VirtualType = VirtualType;

Export Schema types

  • api: public

exports.SchemaTypes = SchemaTypes;

Export types

  • api: public

exports.Types = Types;

Export Query

  • api: public

exports.Query = Query;

Export Promise

  • api: public

exports.Promise = Promise;

Export Model constructor

  • api: public

exports.Model = Model;

Export Document constructor

  • api: public

exports.Document = Document;

Export MongooseError

  • api: public

exports.Error = require('./error');

exports.mongo = require('mongodb');

model

lib/model.js

Module dependencies.

var Document = require('./document')
  , MongooseArray = require('./types/array')
  , MongooseBuffer = require('./types/buffer')
  , MongooseError = require('./error')
  , Query = require('./query')
  , utils = require('./utils')
  , isMongooseObject = utils.isMongooseObject
  , EventEmitter = utils.EventEmitter
  , merge = utils.merge
  , Promise = require('./promise')
  , tick = utils.tick

Model constructor

  • param: Object values to set

  • api: public

function Model (doc, fields) {
  Document.call(this, doc, fields);
};

Inherits from Document.

Model.prototype.__proto__ = Document.prototype;

Connection the model uses. Set by the Connection or if absent set to the default mongoose connection;

  • api: public

Model.prototype.db;

Collection the model uses. Set by Mongoose instance

  • api: public

Model.prototype.collection;

Model name.

  • api: public

Model.prototype.modelName;

Saves this document.

  • see: Model #registerHooks ##

  • param: Function fn

  • api: public

Model.prototype.save = function save (fn) {
  var promise = new Promise(fn)
    , complete = handleSave(promise, this)
    , options = {}

  if (this.options.safe) {
    options.safe = this.options.safe;
  }

  if (this.isNew) {
    // send entire doc
    this.collection.insert(this.toObject({ depopulate: 1 }), 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();
    this._reset();

    if (delta) {
      var where = this._where();
      this.collection.update(where, delta, options, complete);
    } else {
      complete(null);
    }

    this.emit('isNew', false);
  }
};

Remove the document

  • param: Function callback

  • api: public

Model.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.options.safe) {
    options.safe = this.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;
};

Shortcut to access another model.

  • param: String model name

  • api: public

Model.prototype.model = function model (name) {
  return this.db.model(name);
};

Give the constructor the ability to emit events.

for (var i in EventEmitter.prototype)
  Model[i] = EventEmitter.prototype[i];

Document schema

  • api: public

Model.schema;

Database instance the model uses.

  • api: public

Model.db;

Collection the model uses.

  • api: public

Model.collection;

Define properties that access the prototype.

['db', 'collection', 'schema', 'options', 'model'].forEach(function(prop){
  Model.__defineGetter__(prop, function(){
    return this.prototype[prop];
  });
});

Module exports.

module.exports = exports = Model;

Model.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);
};

Finds documents

Examples

// retrieve only certain keys MyModel.find({ name: /john/i }, ['name', 'friends'], function () { })

// pass options MyModel.find({ name: /john/i }, [], { skip: 10 } )

  • param: Object conditions

  • param: Object | Function (optional) fields to hydrate or callback

  • param: Function callback

  • api: public

Model.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);
};

Finds by id

  • param: ObjectId | Object objectid, or a value that can be casted to it

  • api: public

Model.findById = function findById (id, fields, options, callback) {
  return this.findOne({ _id: id }, fields, options, callback);
};

Finds one document

  • param: Object conditions

  • param: Object | Function (optional) fields to hydrate or callback

  • param: Function callback

  • api: public

Model.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);
};

Counts documents

  • param: Object conditions

  • param: Function optional callback

  • api: public

Model.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);
};

Model.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);
};

where enables a very nice sugary api for doing your queries. For example, instead of writing: User.find({age: {$gte: 21, $lte: 65}}, callback); we can instead write more readably: User.where('age').gte(21).lte(65); Moreover, you can also chain a bunch of these together like: User .where('age').gte(21).lte(65) .where('name', /^b/i) // All names that begin where b or B .where('friends').slice(10); ##

  • param: String path

  • param: Object val (optional)

  • return: Query

  • api: public

Model.where = function where (path, val) {
  var q = new Query().bind(this, 'find');
  return q.where.apply(q, arguments);
};

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.

  • param: String | Function js is a javascript string or anonymous function

  • return: Query

  • api: public

Model.$where = function $where () {
  var q = new Query().bind(this, 'find');
  return q.$where.apply(q, arguments);
};

Shortcut for creating a new Document that is automatically saved to the db if valid.

  • param: Object doc

  • param: Function callback

  • api: public

Model.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?
};

Updates documents.

Examples

MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn);
MyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, fn);

Valid options:

  • safe (boolean) safe mode (defaults to value set in schema (true))
  • upsert (boolean) whether to create the doc if it doesn't match (false)
  • multi (boolean) whether multiple documents should be updated (false)

  • param: Object conditions

  • param: Object doc

  • param: Object options

  • param: Function callback

  • return: Query

  • api: public

Model.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);
};

namedscope

lib/namedscope.js

var Query = require('./query'); function NamedScope () {}

NamedScope.prototype.query;

NamedScope.prototype.where = function () { var q = this.query || (this.query = new Query()); q.where.apply(q, arguments); return q; };

*
 * Decorate
 *
 * @param {NamedScope} target
 * @param {Object} getters
 * @api private
 

NamedScope.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;

promise

lib/promise.js

Module dependencies.

var util = require('./utils');
var EventEmitter = util.EventEmitter;

Promise constructor.

  • param: Function a callback+errback that takes err, ... as signature

  • api: public

function Promise (back) {
  this.emitted = {};
  if ('function' == typeof back)
    this.addBack(back);
};

Inherits from EventEmitter.

Promise.prototype.__proto__ = EventEmitter.prototype;

Adds an event or fires the callback right away.

  • return: promise

  • api: public

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;
};

Shortcut for emitting complete event

  • api: public

Promise.prototype.complete = function () {
  var args = util.args(arguments);
  return this.emit.apply(this, ['complete'].concat(args));
};

Shortcut for emitting err event

  • api: public

Promise.prototype.error = function (err) {
  if (!(err instanceof Error)) err = new Error(err);
  return this.emit('err', err);
};

Shortcut for .on('complete', fn)

  • return: promise

  • api: public

Promise.prototype.addCallback = function (fn) {
  return this.on('complete', fn);
};

Shortcut for .on('err', fn)

  • return: promise

  • api: public

Promise.prototype.addErrback = function (fn) {
  return this.on('err', fn);
};

Sugar for handling cases where you may be resolving to either an error condition or a success condition.

  • param: Error optional error or null

  • param: Object value to complete the promise with

  • api: public

Promise.prototype.resolve = function (err, val) {
  if (err) return this.error(err);
  return this.complete(val);
};

Module exports.

module.exports = Promise;

querystream

lib/querystream.js

Module dependencies.

var Stream = require('stream').Stream
var utils = require('./utils')

QueryStream

Returns a stream interface for the query.

  • param: Query query

  • return: Stream

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();
  });
}

Flag stating whether or not this stream is readable.

QueryStream.prototype.readable;

Flag stating whether or not this stream is paused.

QueryStream.prototype.paused;

// trampoline flags
var T_INIT = 0;
var T_IDLE = 1;
var T_CONT = 2;

Pauses this stream.

QueryStream.prototype.pause = function () {
  this.paused = true;
}

Resumes this stream.

QueryStream.prototype.resume = function () {
  this.paused = false;
  this._next();
}

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');
}

// TODO - maybe implement the -native raw option to pass binary?
//QueryStream.prototype.setEncoding = function () {
//}

module.exports = exports = QueryStream;

query

lib/query.js

Module dependencies.

var utils = require('./utils')
  , merge = utils.merge
  , Promise = require('./promise')
  , Document = require('./document')
  , inGroupsOf = utils.inGroupsOf
  , tick = utils.tick
  , QueryStream = require('./querystream')

Binds this query to a model. ##

  • param: Function param

  • return: Query

  • api: public

Query.prototype.bind = function bind (model, op, updateArg) {
  this.model = model;
  this.op = op;
  if (op === 'update') this._updateArg = updateArg;
  return this;
};

Executes the query returning a promise.

Examples

query.exec(); query.exec(callback); query.exec('update'); query.exec('find', callback);

  • param: String | Function op (optional)

  • param: Function callback (optional)

  • return: Promise

  • api: public

Query.prototype.exec = function (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.update(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;
};

Finds documents.

  • param: Object criteria

  • param: Function callback

  • api: public

Query.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);
};

Casts obj, or if obj is not present, then this._conditions, based on the model's schema.

  • param: Function model

  • param: Object obj (optional)

  • api: public

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);
      }
    }
  }
};

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.

  • param: String | Function js is a javascript string or anonymous function

  • return: Query

  • api: public

Query.prototype.$where = function (js) {
  this._conditions['$where'] = js;
  return this;
};

where enables a very nice sugary api for doing your queries. For example, instead of writing:

User.find({age: {$gte: 21, $lte: 65}}, callback);

we can instead write more readably:

User.where('age').gte(21).lte(65);

Moreover, you can also chain a bunch of these together like:

User
  .where('age').gte(21).lte(65)
  .where('name', /^b/i)        // All names that begin where b or B
  .where('friends').slice(10);

  • param: String path

  • param: Object val (optional)

  • return: Query

  • api: public

Query.prototype.where = function (path, val) {
  if (2 === arguments.length) {
    this._conditions[path] = val;
  }
  this._currPath = path;
  return this;
};

equals sugar.

User.where('age').equals(49);

Same as

User.where('age', 49);

  • param: object val

  • return: Query

  • api: public

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;
}

or

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;
}

nor

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;
}

gt, gte, lt, lte, ne, in, nin, all, regex, size, maxDistance

Can be used on Numbers or Dates.

Thing.where('type').nin(array)

'gt gte lt lte ne in nin all regex size maxDistance'.split(' ').forEach(function ($conditional) {
  Query.prototype[$conditional] = function (path, val) {
    if (arguments.length === 1) {
      val = path;
      path = this._currPath
    }
    var conds = this._conditions[path] || (this._conditions[path] = {});
    conds['$' + $conditional] = val;
    return this;
  };

  // deprecationed
  Query.prototype['$' + $conditional] = utils.dep('Query#$'+ $conditional, 'Query#' + $conditional, Query.prototype[$conditional]);
});

mod, near

;['mod', 'near'].forEach( function ($conditional) {
  Query.prototype[$conditional] = 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['$' + $conditional] = val;
    return this;
  };
});

exists

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;
};

elemMatch

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

Object.defineProperty(Query.prototype, 'within', {
  get: function () { return this }
});

Query.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;
};

Query.prototype.center = function (path, val) {
  if (arguments.length === 1) {
    val = path;
    path = this._currPath;
  }
  var conds = this._conditions[path] || (this._conditions[path] = {});
  conds['$within'] = { '$center': [val.center, val.radius]  };
  return this;
};

Query.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;
};

select

Chainable method for specifying which fields to include or exclude from the document that is returned from MongoDB.

Examples

query.fields({a: 1, b: 1, c: 1, _id: 0});
query.fields('a b c');

  • param: Object

Query.prototype.select = function () {
  var arg0 = arguments[0];
  if (!arg0) return this;
  if ('Object' === arg0.constructor.name || Array.isArray(arg0)) {
    this._applyFields(arg0);
  } else if (arguments.length === 1 && typeof arg0 === 'string') {
    this._applyFields({only: arg0});
  } else {
    this._applyFields({only: this._parseOnlyExcludeFields.apply(this, arguments)});
  }
  return this;
};

slice()

Query.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;
};

sort

Sets the sort

Examples

query.sort('test', 1)
query.sort('field', -1)
query.sort('field', -1, 'test', 1)
  • api: public

Query.prototype.sort = function () {
  var sort = this.options.sort || (this.options.sort = []);

  inGroupsOf(2, arguments, function (field, value) {
    sort.push([field, value]);
  });

  return this;
};

;['limit', 'skip', 'maxscan', 'snapshot', 'batchSize', 'comment'].forEach( function (method) {
  Query.prototype[method] = function (v) {
    this.options[method] = v;
    return this;
  };
});

hint

Sets query hints.

Examples

new Query().hint({ indexA: 1, indexB: -1})
new Query().hint("indexA", 1, "indexB", -1)

  • param: Object | String v

  • param: Int [multi]

  • return: Query

  • api: public

Query.prototype.hint = function (v, multi) {
  var hint = this.options.hint || (this.options.hint = {})
    , k

  if (multi) {
    inGroupsOf(2, arguments, function (field, val) {
      hint[field] = val;
    });
  } else if ('Object' === v.constructor.name) {
    // must keep object keys in order so don't use Object.keys()
    for (k in v) {
      hint[k] = v[k];
    }
  }

  return this;
};

slaveOk

Sets slaveOk option.

new Query().slaveOk() <== true
new Query().slaveOk(true)
new Query().slaveOk(false)

  • param: Boolean v (defaults to true)

  • api: public

Query.prototype.slaveOk = function (v) {
  this.options.slaveOk = arguments.length ? !!v : true;
  return this;
};

tailable

Sets tailable option.

new Query().tailable() <== true
new Query().tailable(true)
new Query().tailable(false)

  • param: Boolean v (defaults to true)

  • api: public

Query.prototype.tailable = function (v) {
  this.options.tailable = arguments.length ? !!v : true;
  return this;
};

findOne

Casts the query, sends the findOne command to mongodb. Upon receiving the document, we initialize a mongoose document based on the returned document from mongodb, and then we invoke a callback on our mongoose document.

  • param: Function callback function (err, found)

  • api: public

Query.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);

    var casted = new model(undefined, fields);

    // skip _id for pre-init hooks
    delete casted._doc._id;

    casted.init(doc, self, function (err) {
      if (err) return promise.error(err);
      promise.complete(casted);
    });
  }));

  return this;
};

count

Casts this._conditions and sends a count command to mongodb. Invokes a callback upon receiving results

  • param: Function callback fn(err, cardinality)

  • api: public

Query.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;
};

distinct

Casts this._conditions and sends a distinct command to mongodb. Invokes a callback upon receiving results

  • param: Function callback fn(err, cardinality)

  • api: public

Query.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;
};

update

Casts the doc according to the model Schema and sends an update command to MongoDB.

All paths passed that are not $atomic operations will become $set ops so we retain backwards compatibility.

Example

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.

  • param: Object doc - the update

  • param: Function callback - fn(err)

  • api: public

Query.prototype.update = function update (doc, callback) {
  this.op = 'update';
  this._updateArg = doc;

  var model = this.model
    , options = this._optionsForExec(model)
    , fn = 'function' == typeof callback
    , castQuery
    , castDoc

  try {
    this.cast(model);
    castQuery = this._conditions;
  } catch (err) {
    if (fn) return callback(err);
    throw err;
  }

  try {
    castDoc = this._castUpdate(doc);
  } catch (err) {
    if (fn) return callback(err);
    throw err;
  }

  if (!fn) {
    delete options.safe;
  }

  if (castDoc) {
    model.collection.update(castQuery, castDoc, options, tick(callback));
  } else {
    process.nextTick(function () {
      callback(null, 0);
    });
  }

  return this;
};

remove

Casts the query, sends the remove command to mongodb where the query contents, and then invokes a callback upon receiving the command result.

  • param: Function callback

  • api: public

Query.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;
};

populate

Sets population options. - api: public

Query.prototype.populate = function (path, fields, conditions, options) {
  // 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);

  return this;
};

stream

Returns a stream interface

Example

Thing.find({ name: /^hello/ }).stream().pipe(res)
  • api: public

Query.prototype.stream = function stream () {
  return new QueryStream(this);
}

Deprecated methods.

Query.prototype.$or = utils.dep('Query#$or', 'Query#or', Query.prototype.or);
Query.prototype.$nor =utils.dep('Query#$nor', 'Query#nor', Query.prototype.nor);
Query.prototype.run = utils.dep('Query#run', 'Query#exec', Query.prototype.exec);
Query.prototype.$mod = utils.dep('Query#$mod', 'Query#mod', Query.prototype.mod);
Query.prototype.$box = utils.dep('Query#$box', 'Query#box', Query.prototype.box);
Query.prototype.$near = utils.dep('Query#$near', 'Query#near', Query.prototype.near);
Query.prototype.$slice = utils.dep('Query#$slice', 'Query#slice', Query.prototype.slice);
Query.prototype.notEqualTo = utils.dep('Query#notEqualTo', 'Query#ne', Query.prototype.ne);
Query.prototype.fields = utils.dep('Query#fields', 'Query#select', Query.prototype.select);
Query.prototype.$exists =utils.dep('Query#$exists', 'Query#exists', Query.prototype.exists);
Query.prototype.$center = utils.dep('Query#$center', 'Query#center', Query.prototype.center);
Query.prototype.$elemMatch =utils.dep('Query#$elemMatch', 'Query#elemMatch', Query.prototype.elemMatch);
Query.prototype.$centerSphere = utils.dep('Query#$centerSphere', 'Query#centerSphere', Query.prototype.centerSphere);

asc

Sorts ascending.

query.asc('name', 'age');
  • deprecate: d

function asc () {
  var sort = this.options.sort || (this.options.sort = []);
  for (var i = 0, l = arguments.length; i &lt; l; i++) {
    sort.push([arguments[i], 1]);
  }
  return this;
};
Query.prototype.asc = utils.dep('Query#asc', 'Query#sort', asc);

desc

Sorts descending.

query.desc('name', 'age');
  • deprecate: d

function desc () {
  var sort = this.options.sort || (this.options.sort = []);
  for (var i = 0, l = arguments.length; i &lt; l; i++) {
    sort.push([arguments[i], -1]);
  }
  return this;
};
Query.prototype.desc = utils.dep('Query#desc', 'Query#sort', desc);

limit, skip, maxscan, snapshot, batchSize, comment

Sets these associated options.

query.comment('feed query');
  • deprecate: d

'$within wherein $wherein'.split(' ').forEach(function (getter) {
  var withinDep = utils.dep('Query#' + getter, 'Query#within')
  Object.defineProperty(Query.prototype, getter, {
    get: function () {
      withinDep();
      return this;
    }
  });
});

only

Chainable method for adding the specified fields to the object of fields to only include.

Examples

query.only('a b c');
query.only('a', 'b', 'c');
query.only(['a', 'b', 'c']);

  • param: String | Array space separated list of fields OR

    an array of field names We can also take arguments as the "array" of field names

  • api: public

  • deprecate: d

function only (fields) {
  fields = this._parseOnlyExcludeFields.apply(this, arguments);
  this._applyFields({ only: fields });
  return this;
};
Query.prototype.only = utils.dep('Query#only', 'Query#select', only);

exclude

Chainable method for adding the specified fields to the object of fields to exclude.

Examples

query.exclude('a b c');
query.exclude('a', 'b', 'c');
query.exclude(['a', 'b', 'c']);

  • param: String | Array space separated list of fields OR

    an array of field names We can also take arguments as the "array" of field names

  • api: public

  • deprecate: d

function exclude (fields) {
  fields = this._parseOnlyExcludeFields.apply(this, arguments);
  this._applyFields({ exclude: fields });
  return this;
};
Query.prototype.exclude = utils.dep('Query#exclude', 'Query#select', exclude);

each()

Streaming cursors.

The callback is called repeatedly for each document found in the collection as it's streamed. If an error occurs streaming stops.

Example

query.each(function (err, user) {
  if (err) return res.end("aww, received an error. all done.");
  if (user) {
    res.write(user.name + '\n')
  } else {
    res.end("reached end of cursor. all done.");
  }
});

A third parameter may also be used in the callback which allows you to iterate the cursor manually.

Example

query.each(function (err, user, next) {
  if (err) return res.end("aww, received an error. all done.");
  if (user) {
    res.write(user.name + '\n')
    doSomethingAsync(next);
  } else {
    res.end("reached end of cursor. all done.");
  }
});

  • param: Function callback

  • return: Query

  • deprecate: d

  • api: public

function each (callback) {
  var model = this.model
    , options = this._optionsForExec(model)
    , manual = 3 == callback.length
    , self = this

  try {
    this.cast(model);
  } catch (err) {
    return callback(err);
  }

  var fields = utils.clone(options.fields = this._fields);

  function complete (err, val) {
    if (complete.ran) return;
    complete.ran = true;
    callback(err, val);
  }

  model.collection.find(this._conditions, options, function (err, cursor) {
    if (err) return complete(err);

    var ticks = 0;
    next();

    function next () {
      // nextTick is necessary to avoid stack overflows when
      // dealing with large result sets. yield occasionally.
      if (!(++ticks % 20)) {
        process.nextTick(function () {
          cursor.nextObject(onNextObject);
        });
      } else {
        cursor.nextObject(onNextObject);
      }
    }

    function onNextObject (err, doc) {
      if (err) return complete(err);

      // when doc is null we hit the end of the cursor
      if (!doc) return complete(null, null);

      var instance = new model(undefined, fields);

      // skip _id for pre-init hooks
      delete instance._doc._id;

      instance.init(doc, self, function (err) {
        if (err) return complete(err);

        if (manual) {
          callback(null, instance, next);
        } else {
          callback(null, instance);
          next();
        }
      });
    }

  });

  return this;
}
Query.prototype.each = utils.dep('Query#each', 'Query#stream', each);

Exports.

module.exports = Query;
module.exports.QueryStream = QueryStream;

array

lib/schema/array.js

Module dependencies.

var SchemaType = require('../schematype')
  , CastError = SchemaType.CastError
  , NumberSchema = require('./number')
  , Types = {
        Boolean: require('./boolean')
      , Date: require('./date')
      , Number: ArrayNumberSchema
      , String: require('./string')
      , ObjectId: require('./objectid')
      , Buffer: require('./buffer')
    }
  , MongooseArray = require('../types').Array
  , Mixed = require('./mixed')
  , Query = require('../query')
  , isMongooseObject = require('../utils').isMongooseObject

Inherits from SchemaType.

SchemaArray.prototype.__proto__ = SchemaType.prototype;

Module exports.

module.exports = SchemaArray;

boolean

lib/schema/boolean.js

Module dependencies.

var SchemaType = require('../schematype');

Inherits from SchemaType.

SchemaBoolean.prototype.__proto__ = SchemaType.prototype;

Module exports.

module.exports = SchemaBoolean;

buffer

lib/schema/buffer.js

Module dependencies.

var SchemaType = require('../schematype')
  , CastError = SchemaType.CastError
  , BufferNumberSchema = function () {}
  , MongooseBuffer = require('../types').Buffer
  , Binary = MongooseBuffer.Binary
  , Query = require('../query');

Inherits from SchemaType.

SchemaBuffer.prototype.__proto__ = SchemaType.prototype;

Module exports.

module.exports = SchemaBuffer;

date

lib/schema/date.js

Module requirements.

var SchemaType = require('../schematype')
  , CastError = SchemaType.CastError;

Inherits from SchemaType.

SchemaDate.prototype.__proto__ = SchemaType.prototype;

Module exports.

module.exports = SchemaDate;

documentarray

lib/schema/documentarray.js

Module dependencies.

var SchemaType = require('../schematype')
  , ArrayType = require('./array')
  , MongooseDocumentArray = require('../types/documentarray')
  , Subdocument = require('../types/embedded')
  , CastError = SchemaType.CastError
  , Document = require('../document');

Inherits from ArrayType.

DocumentArray.prototype.__proto__ = ArrayType.prototype;

Module exports.

module.exports = DocumentArray;

index

lib/schema/index.js

Module exports.

exports.String = require('./string');

exports.Number = require('./number');

exports.Boolean = require('./boolean');

exports.DocumentArray = require('./documentarray');

exports.Array = require('./array');

exports.Buffer = require('./buffer');

exports.Date = require('./date');

exports.ObjectId = require('./objectid');

exports.Mixed = require('./mixed');

mixed

lib/schema/mixed.js

Module dependencies.

var SchemaType = require('../schematype');

Inherits from SchemaType.

Mixed.prototype.__proto__ = SchemaType.prototype;

Module exports.

module.exports = Mixed;

number

lib/schema/number.js

Module requirements.

var SchemaType = require('../schematype')
  , CastError = SchemaType.CastError
  , MongooseNumber = require('../types/number');

Inherits from SchemaType.

SchemaNumber.prototype.__proto__ = SchemaType.prototype;

Sets a maximum number validator

  • param: Number minimum number

  • api: public

SchemaNumber.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 &gt;= value;
    }, 'min']);
  return this;
};

Sets a maximum number validator

  • param: Number maximum number

  • api: public

SchemaNumber.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 &lt;= value;
    }, 'max']);
  return this;
};

Module exports.

module.exports = SchemaNumber;

objectid

lib/schema/objectid.js

Module dependencies.

var SchemaType = require('../schematype')
  , CastError = SchemaType.CastError
  , driver = global.MONGOOSE_DRIVER_PATH || './../drivers/node-mongodb-native'
  , oid = require('../types/objectid');

Inherits from SchemaType.

ObjectId.prototype.__proto__ = SchemaType.prototype;

Module exports.

module.exports = ObjectId;

string

lib/schema/string.js

Module dependencies.

var SchemaType = require('../schematype')
  , CastError = SchemaType.CastError;

Inherits from SchemaType.

SchemaString.prototype.__proto__ = SchemaType.prototype;

Adds enumeration values

  • param: multiple enumeration values

  • api: public

SchemaString.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 &lt; 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 ~values.indexOf(v);
    };
    this.validators.push([this.enumValidator, 'enum']);
  }
};

Adds a lowercase setter

  • api: public

SchemaString.prototype.lowercase = function () {
  return this.set(function (v) {
    return v.toLowerCase();
  });
};

Adds an uppercase setter

  • api: public

SchemaString.prototype.uppercase = function () {
  return this.set(function (v) {
    return v.toUpperCase();
  });
};

Adds a trim setter

  • api: public

SchemaString.prototype.trim = function () {
  return this.set(function (v) {
    return v.trim();
  });
};

Sets a regexp test

  • param: RegExp regular expression to test against

  • param: String optional validator message

  • api: public

SchemaString.prototype.match = function(regExp){
  this.validators.push([function(v){
    return regExp.test(v);
  }, 'regexp']);
};

Module exports.

module.exports = SchemaString;

schema

lib/schema.js

Module dependencies.

var EventEmitter = require('events').EventEmitter
  , VirtualType = require('./virtualtype')
  , utils = require('./utils')
  , NamedScope
  , Query
  , Types

Schema constructor.

  • param: Object definition

  • api: public

function Schema (obj, options) {
  this.paths = {};
  this.virtuals = {};
  this.nested = {};
  this.inherits = {};
  this.callQueue = [];
  this._indexes = [];
  this.methods = {};
  this.statics = {};
  this.tree = {};

  // set options
  this.options = utils.options({
      safe: true
    , strict: false
  }, options);

  // build paths
  if (obj)
    this.add(obj);

  if (!this.paths['_id'] &amp;&amp; !this.options.noId) {
    this.add({ _id: {type: ObjectId, auto: true} });
  }

  if (!this.paths['id'] &amp;&amp; !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;
};

Inherit from EventEmitter.

Schema.prototype.__proto__ = EventEmitter.prototype;

Sets the keys

  • param: Object keys

  • param: String prefix

  • api: public

Schema.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' &amp;&amp; (!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]);
  }
};

Sets a path (if arity 2) Gets a path (if arity 1)

  • param: String path

  • param: Object constructor

  • api: public

Schema.prototype.path = function (path, obj) {
  if (obj == undefined) {
    if (this.paths[path]) return this.paths[path];

    // Sometimes path will come in as
    // pathNameA.4.pathNameB where 4 means the index
    // of an embedded document in an embedded array.
    // In this case, we need to jump to the Array's
    // schema and call path() from there to resolve to
    // the correct path type

    var last
      , self = this
      , subpaths = path.split(/\.(\d+)\.?/)
                       .filter(Boolean) // removes empty strings

    if (subpaths.length &gt; 1) {
      last = subpaths.length - 1;
      return subpaths.reduce(function (val, subpath, i) {
        if (val &amp;&amp; !val.schema) {
          if (i === last &amp;&amp; !/\D/.test(subpath) &amp;&amp; val instanceof Types.Array) {
            return val.caster; // StringSchema, NumberSchema, etc
          } else {
            return val;
          }
        }

        if (!/\D/.test(subpath)) { // 'path.0.subpath'  on path 0
          return val;
        }

        return val ? val.schema.path(subpath)
                   : self.path(subpath);
      }, null);
    }

    return this.paths[subpaths[0]];
  }

  // 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;
};

Converts -- e.g., Number, [SomeSchema], { type: String, enum: ['m', 'f'] } -- into the appropriate Mongoose Type, which we use later for casting, validation, etc. ##

  • param: String path

  • param: Object constructor

Schema.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 &amp;&amp; !obj.type.type
    ? obj.type
    : {};

  if (type.constructor.name == 'Object') {
    return new Types.Mixed(path, obj);
  }

  if (Array.isArray(type) || type == Array) {
    // if it was specified through { type } look for `cast`
    var cast = type == Array
      ? obj.cast
      : type[0];

    if (cast instanceof Schema) {
      return new Types.DocumentArray(path, cast, obj);
    }

    return new Types.Array(path, cast || Types.Mixed, obj);
  }

  if (undefined == Types[type.name]) {
    throw new TypeError('Undefined type at `' + path +
        '`\n  Did you try nesting Schemas? ' +
        'You can only nest using refs or arrays.');
  }

  return new Types[type.name](path, obj);
};

Iterates through the schema's paths, passing the path string and type object to the callback.

  • param: Function callback function - fn(pathstring, type)

  • return: Schema this for chaining

  • api: public

Schema.prototype.eachPath = function (fn) {
  var keys = Object.keys(this.paths)
    , len = keys.length;

  for (var i = 0; i &lt; len; ++i) {
    fn(keys[i], this.paths[keys[i]]);
  }

  return this;
};

Returns an Array of path strings that are required. - api: public

Object.defineProperty(Schema.prototype, 'requiredPaths', {
  get: function () {
    var paths = this.paths
      , pathnames = Object.keys(paths)
      , i = pathnames.length
      , pathname, path
      , requiredPaths = [];
    while (i--) {
      pathname = pathnames[i];
      path = paths[pathname];
      if (path.isRequired) requiredPaths.push(pathname);
    }
    return requiredPaths;
  }
});

Given a path, returns whether it is a real, virtual, nested, or ad-hoc/undefined path.

  • param: String path

  • return: String

  • api: public

Schema.prototype.pathType = function (path) {
  if (path in this.paths) return 'real';
  if (path in this.virtuals) return 'virtual';
  if (path in this.nested) return 'nested';
  return 'adhocOrUndefined';
};

Defines a pre for the document

  • param: String method

  • param: Function callback

  • api: public

Schema.prototype.pre = function(){
  return this.queue('pre', arguments);
};

Defines a post hook for the document.

  • param: String method

  • param: Function callback

  • api: public

Schema.prototype.post = function(method, fn){
  return this.queue('on', arguments);
};

Registers a plugin for this schema

  • param: Function plugin callback

  • api: public

Schema.prototype.plugin = function (fn, opts) {
  fn(this, opts);
  return this;
};

Adds a method

  • param: String method name

  • param: Function handler

  • api: public

Schema.prototype.method = function (name, fn) {
  if ('string' != typeof name)
    for (var i in name)
      this.methods[i] = name[i];
  else
    this.methods[name] = fn;
  return this;
};

Defines a static method

  • param: String name

  • param: Function handler

  • api: public

Schema.prototype.static = function(name, fn) {
  if ('string' != typeof name)
    for (var i in name)
      this.statics[i] = name[i];
  else
    this.statics[name] = fn;
  return this;
};

Defines an index (most likely compound) ## Example schema.index({ first: 1, last: -1 })

  • param: Object field

  • param: Object optional options object

  • api: public

Schema.prototype.index = function (fields, options) {
  this._indexes.push([fields, options || {}]);
  return this;
};

Sets/gets an option

  • param: String key

  • param: Object optional value

  • api: public

Schema.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

  • api: public

Schema.prototype.__defineGetter__('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 &amp;&amp; index !== null){
            var field = {};
            field[prefix + i] = '2d' === index ? index : 1;
            indexes.push([field, 'Object' === index.constructor.name ? index : {} ]);
          }
        }
      }
    }

    if (prefix) {
      fixSubIndexPaths(schema, prefix);
    } else {
      indexes = indexes.concat(schema._indexes);
    }
  }

Checks for indexes added to subdocs using Schema.index(). These indexes need their paths prefixed properly.

schema._indexes = [ [indexObj, options], [indexObj, options] ..]

function fixSubIndexPaths (schema, prefix) {
    var subindexes = schema._indexes
      , len = subindexes.length
      , indexObj
      , newindex
      , klen
      , keys
      , key
      , i = 0
      , j

    for (i = 0; i &lt; len; ++i) {
      indexObj = subindexes[i][0];
      keys = Object.keys(indexObj);
      klen = keys.length;
      newindex = {};

      // use forward iteration, order matters
      for (j = 0; j &lt; klen; ++j) {
        key = keys[j];
        newindex[prefix + key] = indexObj[key];
      }

      indexes.push([newindex, subindexes[i][1]]);
    }
  }

});

Retrieves or creates the virtual type with the given name.

  • param: String name

  • return: VirtualType

Schema.prototype.virtual = function (name, options) {
  var virtuals = this.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);
};

Fetches the virtual type with the given name. Should be distinct from virtual because virtual auto-defines a new VirtualType if the path doesn't exist.

  • param: String name

  • return: VirtualType

Schema.prototype.virtualpath = function (name) {
  return this.virtuals[name];
};

Schema.prototype.namedScope = function (name, fn) {
  var namedScopes = this.namedScopes || (this.namedScopes = new NamedScope)
    , newScope = Object.create(namedScopes)
    , allScopes = namedScopes.scopesByName || (namedScopes.scopesByName = {});
  allScopes[name] = newScope;
  newScope.name = name;
  newScope.block = fn;
  newScope.query = new Query();
  newScope.decorate(namedScopes, {
    block0: function (block) {
      return function () {
        block.call(this.query);
        return this;
      };
    },
    blockN: function (block) {
      return function () {
        block.apply(this.query, arguments);
        return this;
      };
    },
    basic: function (query) {
      return function () {
        this.query.find(query);
        return this;
      };
    }
  });
  return newScope;
};

ObjectId schema identifier. Not an actual ObjectId, only used for Schemas.

  • api: public

function ObjectId () {
  throw new Error('This is an abstract interface. Its only purpose is to mark '
                + 'fields as ObjectId in the schema creation.');
}

Module exports.

module.exports = exports = Schema;

// require down here because of reference issues
exports.Types = Types = require('./schema/index');
NamedScope = require('./namedscope')
Query = require('./query');

exports.ObjectId = ObjectId;

schemadefault

lib/schemadefault.js

Module dependencies.

var Schema = require('./schema')

Default model for querying the system.profiles collection (it only exists when profiling is enabled.

exports['system.profile'] = new Schema({
    ts: Date
  , info: String // deprecated
  , millis: Number
  , op: String
  , ns: String
  , query: Schema.Types.Mixed
  , updateobj: Schema.Types.Mixed
  , ntoreturn: Number
  , nreturned: Number
  , nscanned: Number
  , responseLength: Number
  , client: String
  , user: String
  , idhack: Boolean
  , scanAndOrder: Boolean
  , keyUpdates: Number
  , cursorid: Number
}, { noVirtualId: true, noId: true });

schematype

lib/schematype.js

Module dependencies.

var MongooseError = require('./error');
var utils = require('./utils');

SchemaType constructor

  • param: String path

  • api: public

function SchemaType (path, options, instance) {
  this.path = path;
  this.instance = instance;
  this.validators = [];
  this.setters = [];
  this.getters = [];
  this.options = options;
  this._index = null;
  this.selected;

  for (var i in options) if (this[i] &amp;&amp; 'function' == typeof this[i]) {
    var opts = Array.isArray(options[i])
      ? options[i]
      : [options[i]];

    this[i].apply(this, opts);
  }
};

Sets a default

  • param: Object default value

  • api: public

SchemaType.prototype.default = function (val) {
  if (1 === arguments.length) {
    this.defaultValue = typeof val === 'function'
      ? val
      : this.cast(val);
    return this;
  } else if (arguments.length &gt; 1) {
    this.defaultValue = utils.args(arguments);
  }
  return this.defaultValue;
};

Sets index. It can be a boolean or a hash of options ## Example Schema.path('my.path').index(true); Schema.path('my.path').index({ unique: true });

"Direction doesn't matter for single key indexes" http://www.mongodb.org/display/DOCS/Indexes#Indexes-CompoundKeysIndexes

  • param: Object true/

  • api: public

SchemaType.prototype.index = function (index) {
  this._index = index;
  return this;
};

Adds a setter

  • param: Function setter

  • api: public

SchemaType.prototype.set = function (fn) {
  if ('function' != typeof fn)
    throw new Error('A setter must be a function.');
  this.setters.push(fn);
  return this;
};

Adds a getter

  • param: Function getter

  • api: public

SchemaType.prototype.get = function (fn) {
  if ('function' != typeof fn)
    throw new Error('A getter must be a function.');
  this.getters.push(fn);
  return this;
};

validate

Adds validators.

Examples

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 }});

  • param: Object validator

  • param: String optional error message

  • api: public

SchemaType.prototype.validate = function (obj, error) {
  if ('function' == typeof obj || obj &amp;&amp; '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;
};

Adds a required validator

  • param: Boolean enable/disable the validator

  • api: public

SchemaType.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 &amp;&amp;
        !this.isSelected(self.path) &amp;&amp;
        !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;
};

select

Set default select() behavior for this path. 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(..); // x will always be selected ..
// .. unless overridden;
T.find().select({ x: 0 }).exec();

SchemaType.prototype.select = function select (val) {
  this.selected = !! val;
}

Inherits from MongooseError

ValidatorError.prototype.__proto__ = MongooseError.prototype;

Inherits from MongooseError.

CastError.prototype.__proto__ = MongooseError.prototype;

Module exports.

module.exports = exports = SchemaType;

exports.CastError = CastError;

exports.ValidatorError = ValidatorError;

statemachine

lib/statemachine.js

Module dependencies.

var utils = require('./utils');

array

lib/types/array.js

Module dependencies.

var EmbeddedDocument = require('./embedded');
var Document = require('../document');
var ObjectId = require('./objectid');
var utils = require('../utils')

Inherit from Array

MongooseArray.prototype = new Array;

Marks this array as modified. It is called during a nonAtomicPush, an atomic opteration, or by an existing embedded document that is modified.

If it bubbles up from an embedded document change, then it takes the following arguments (otherwise, takes 0 arguments) ##

  • param: EmbeddedDocument embeddedDoc that invokes this method on the Array

  • param: String embeddedPath is what changed inthe embeddedDoc

  • api: public

MongooseArray.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;
};

Returns true if we have to perform atomics for this, and no normal operations

  • api: public

MongooseArray.prototype.__defineGetter__('doAtomics', function () {
  if (!(this._atomics &amp;&amp; 'Object' === this._atomics.constructor.name)) {
    return 0;
  }

  return Object.keys(this._atomics).length;
});

Pushes item/s to the array atomically. Overrides Array#push

  • param: Object value

  • api: public

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;
};
MongooseArray.prototype.$push =
  utils.dep('MongooseArray#$push'
          , 'MongooseArray#push', MongooseArray.prototype.push);

Pushes item/s to the array non-atomically

  • param: Object value

  • api: public

MongooseArray.prototype.nonAtomicPush = function () {
  var values = [].map.call(arguments, this._cast, this)
    , ret = [].push.apply(this, values);
  this._markModified();
  return ret;
};

Pushes several items at once to the array atomically

  • param: Array values

  • api: public

  • deprecate: d

function $pushAll (value) {
  var length = this.length;
  this.nonAtomicPush.apply(this, value);
  // make sure we access the casted elements
  this._registerAtomic('$pushAll', this.slice(length));
  return this;
};
MongooseArray.prototype.$pushAll =
  utils.dep('MongooseArray#$pushAll'
          , 'MongooseArray#push in v3', $pushAll);

Pops the array atomically

  • api: public

MongooseArray.prototype.pop =
MongooseArray.prototype.$pop = function () {
  this._registerAtomic('$pop', 1);
  return [].pop.call(this)
};

Atomically shifts the array.

Only works once per doc.save(). Calling this mulitple times on an array before saving is the same as calling it once.

  • api: public

MongooseArray.prototype.shift =
MongooseArray.prototype.$shift = function $shift () {
  this._registerAtomic('$pop', -1);
  return [].shift.call(this)
};

Removes items from an array atomically

Examples

doc.array.remove(ObjectId)
doc.array.remove('tag 1', 'tag 2')

  • param: Object value to remove

  • api: public

MongooseArray.prototype.remove = function () {
  var args = [].map.call(arguments, this._cast, this);
  if (args.length == 1)
    this.$pull(args[0]);
  else
    this.$pullAll(args);
  return args;
};

Pulls from the array

  • api: public

MongooseArray.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;
};
MongooseArray.prototype.$pull =
  utils.dep('MongooseArray#$pull'
          , 'MongooseArray#pull', MongooseArray.prototype.pull);

Pulls many items from an array

  • api: public

  • deprecate: d

function $pullAll (values) {
  if (values &amp;&amp; values.length) {
    var oldArr = this._parent.get(this._path)
      , i = oldArr.length, mem;
    while (i--) {
      mem = oldArr[i];
      if (mem instanceof EmbeddedDocument) {
        if (values.some( function (v) { return v.equals(mem); } )) {
          oldArr.splice(i, 1);
        }
      } else if (~values.indexOf(mem)) oldArr.splice(i, 1);
    }
    if (values[0] instanceof EmbeddedDocument) {
      this._registerAtomic('$pullDocs', values.map( function (v) { return v._id; } ));
    } else {
      this._registerAtomic('$pullAll', values);
    }
  }
  return this;
};
MongooseArray.prototype.$pullAll =
  utils.dep('MongooseArray#$pullAll', 'MongooseArray#pull', $pullAll);

Splices the array.

Note 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.

  • api: public

MongooseArray.prototype.splice = function () {
  var result = [].splice.apply(this, arguments);
  this._markModified();
  return result;
};

Non-atomically unshifts onto the array.

Note 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.

  • api: public

MongooseArray.prototype.unshift = function () {
  var values = [].map.call(arguments, this._cast, this);
  [].unshift.apply(this, values);
  this._markModified();
  return this.length;
};
MongooseArray.prototype.$unshift =
  utils.dep('MongooseArray#$unshift'
          , 'MongooseArray#unshift', MongooseArray.prototype.unshift);

Adds values to the array if not already present. - api: public

MongooseArray.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;
};
MongooseArray.prototype.$addToSet =
  utils.dep('MongooseArray#$addToSet'
          , 'MongooseArray#addToSet', MongooseArray.prototype.addToSet);

Returns an Array

  • return: Array

  • api: public

MongooseArray.prototype.toObject = function (options) {
  if (options &amp;&amp; options.depopulate &amp;&amp; this[0] instanceof Document) {
    return this.map(function (doc) {
      return doc._id;
    });
  }

  return this.map(function (doc) {
    return doc;
  });
};

Helper for console.log

  • api: public

MongooseArray.prototype.inspect = function () {
  return '[' + this.map(function (doc) {
    return ' ' + doc;
  }) + ' ]';
};

Return the index of obj or -1.

  • param: Object obj

  • return: Number

  • api: public

MongooseArray.prototype.indexOf = function indexOf (obj) {
  if (obj instanceof ObjectId) obj = obj.toString();
  for (var i = 0, len = this.length; i &lt; len; ++i) {
    if (obj == this[i])
      return i;
  }
  return -1;
};

Module exports.

module.exports = exports = MongooseArray;

buffer

lib/types/buffer.js

Access driver.

var driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native';

Module dependencies.

var Binary = require(driver + '/binary');

Inherit from Buffer.

MongooseBuffer.prototype = new Buffer(0);

Marks this buffer as modified.

  • api: public
MongooseBuffer.prototype._markModified = function () {
  var parent = this._parent;

  if (parent) {
    parent.markModified(this._path);
  }
  return this;
};

Writes the buffer.

MongooseBuffer.prototype.write = function () {
  var written = Buffer.prototype.write.apply(this, arguments);

  if (written &gt; 0) {
    this._markModified();
  }

  return written;
};

Copy the buffer.

Note Buffer#copy will not mark target as modified so

you must copy from a MongooseBuffer for it to work as expected.

Work around since copy modifies the target, not this.

MongooseBuffer.prototype.copy = function (target) {
  var ret = Buffer.prototype.copy.apply(this, arguments);

  if (target instanceof MongooseBuffer) {
    target._markModified();
  }

  return ret;
};

Compile other Buffer methods marking this buffer as modified.

;(
// node < 0.5
'writeUInt8 writeUInt16 writeUInt32 writeInt8 writeInt16 writeInt32 ' +
'writeFloat writeDouble fill ' +
'utf8Write binaryWrite asciiWrite set ' +

// node >= 0.5
'writeUInt16LE writeUInt16BE writeUInt32LE writeUInt32BE ' +
'writeInt16LE writeInt16BE writeInt32LE writeInt32BE ' +
'writeFloatLE writeFloatBE writeDoubleLE writeDoubleBE'
).split(' ').forEach(function (method) {
  if (!Buffer.prototype[method]) return;
  MongooseBuffer.prototype[method] = new Function(
    'var ret = Buffer.prototype.'+method+'.apply(this, arguments);' +
    'this._markModified();' +
    'return ret;'
  )
});

Returns a Binary.

  • return: Buffer

  • api: public

MongooseBuffer.prototype.toObject = function () {
  return new Binary(this, x00);
};

Module exports.

MongooseBuffer.Binary = Binary;

module.exports = MongooseBuffer;

document

lib/types/document.js

Module dependencies.

var Document = require('../document')
  , inspect = require('util').inspect;

Inherit from Document

EmbeddedDocument.prototype.__proto__ = Document.prototype;

Override set to mark the parent as modified

  • api: public

var oldSet = Document.prototype.set;

EmbeddedDocument.prototype.set = function (path) {
  this.markModified(path);
  return oldSet.apply(this, arguments);
};

Save the subdocument

  • api: public

EmbeddedDocument.prototype.save = function(fn) {
  if (fn)
    fn(null);
  this.emit('save', this);
  return this;
};

Remove the subdocument

  • api: public

EmbeddedDocument.prototype.remove = function (fn) {
  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;
};

Helper for console.log

  • api: public

EmbeddedDocument.prototype.inspect = function () {
  return inspect(this.toObject());
};

Module exxports.

module.exports = EmbeddedDocument;

documentarray

lib/types/documentarray.js

Module dependencies.

var MongooseArray = require('./array')
  , driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native'
  , ObjectId = require(driver + '/objectid')
  , ObjectIdSchema = require('../schema/objectid');

Inherits from MongooseArray

MongooseDocumentArray.prototype.__proto__ = MongooseArray.prototype;

Filters items by id

  • param: Object id

  • api: public

MongooseDocumentArray.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 &lt; 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 an Array and converts any Document members toObject.

  • return: Array

  • api: public

MongooseDocumentArray.prototype.toObject = function () {
  return this.map(function (doc) {
    return doc &amp;&amp; doc.toObject() || null;
  });
};

Helper for console.log

  • api: public

MongooseDocumentArray.prototype.inspect = function () {
  return '[' + this.map(function (doc) {
    return doc &amp;&amp; doc.inspect() || 'null';
  }).join('\n') + ']';
};

Module exports.

module.exports = MongooseDocumentArray;

embedded

lib/types/embedded.js

Module dependencies.

var Document = require('../document')
  , inspect = require('util').inspect;

Inherit from Document

EmbeddedDocument.prototype.__proto__ = Document.prototype;

Noop. Does not actually save the doc to the db.

  • api: public

EmbeddedDocument.prototype.save = function(fn) {
  if (fn)
    fn(null);
  return this;
};

Remove the subdocument

  • api: public

EmbeddedDocument.prototype.remove = function (fn) {
  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;
};

Helper for console.log

  • api: public

EmbeddedDocument.prototype.inspect = function () {
  return inspect(this.toObject());
};

Module exports.

module.exports = EmbeddedDocument;

index

lib/types/index.js

Module exports.

exports.Array = require('./array');
exports.Buffer = require('./buffer');

exports.Document = // @deprecate
exports.Embedded = require('./embedded');

exports.DocumentArray = require('./documentarray');
exports.Number = require('./number');
exports.ObjectId = require('./objectid');

number

lib/types/number.js

Module dependencies.

var utils = require('./../utils')

Inherits from Number.

MongooseNumber.prototype = new Number();

var depInc = utils.dep('MongooseNumber#$inc / MongooseNumber#increment', 'Model.update() to get incrementation in v3');
var depDec = utils.dep('MongooseNumber#decrement', 'Model.update() to get decrementation in v3');

Atomic increment

  • api: public

function increment (value) {
  depInc();
  var schema = this._parent.schema.path(this._path)
    , value = Number(value) || 1;
  if (isNaN(value)) value = 1;
  this._parent.setValue(this._path, schema.cast(this + value));
  this._parent.getValue(this._path)._atomics['$inc'] = value || 1;
  this._parent._activePaths.modify(this._path);
  return this;
};
MongooseNumber.prototype.$inc =
MongooseNumber.prototype.increment = increment;

Returns true if we have to perform atomics for this, and no normal operations

  • api: public

MongooseNumber.prototype.__defineGetter__('doAtomics', function () {
  return Object.keys(this._atomics).length;
});

Atomic decrement

  • api: public

MongooseNumber.prototype.decrement = function(){
  depDec();
  this.increment(-1);
};

Re-declare toString (for console.log)

  • api: public

MongooseNumber.prototype.inspect =
MongooseNumber.prototype.toString = function () {
  return String(this.valueOf());
};

Module exports

module.exports = MongooseNumber;

objectid

lib/types/objectid.js

Access driver.

var driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native';

Module exports.

module.exports = require(driver + '/objectid');

utils

lib/utils.js

Module dependencies.

var EventEmitter = require('events').EventEmitter
  , ObjectId = require('./types/objectid')
  , MongooseBuffer
  , MongooseArray
  , Document

Pluralization rules.

var rules = [
  [/(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']
];

Uncountable words.

var 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'
];

Inherit from EventEmitter.

Events.prototype.__proto__ = EventEmitter.prototype;

Add once.

Events.prototype.once = function (type, listener) {
    var self = this;
    self.on(type, function g(){
      self.removeListener(type, g);
      listener.apply(this, arguments);
    });
  };

}

exports.EventEmitter = Events;

// Modified from node/lib/assert.js
exports.deepEqual = function deepEqual (a, b) {
  if (a === b) return true;

  if (a instanceof Date &amp;&amp; b instanceof Date)
    return a.getTime() === b.getTime();

  if (a instanceof ObjectId &amp;&amp; b instanceof ObjectId) {
    return a.toString() === b.toString();
  }

  if (typeof a !== 'object' &amp;&amp; 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 &amp;&amp; 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 &lt; 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 &gt;= 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 &gt;= 0; i--) {
    key = ka[i];
    if (!deepEqual(a[key], b[key])) return false;
  }

  return true;
};

Merges from into to without overwriting existing properties of to.

  • param: Object to

  • param: Object from

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

exports.args = function (args, slice, sliceEnd) {
  var ret = [];
  var start = slice || 0;
  var end = 3 === arguments.length
    ? sliceEnd
    : args.length;

  for (var i = start; i &lt; end; ++i) {
    ret[i - start] = args[i];
  }

  return ret;
}

Returns if v is a mongoose object that has a toObject() method we can use. This is for compatibility with libs like Date.js which do foolish things to Natives.

var isMongooseObject = 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
}

API deprecation helper.

Disable deprecation warnings by setting the MONGOOSEDEPWARNINGS environment variable to false, or mongoose.set('dep warnings', false);

var warn =false;
;(exports.dep = function deprecate (old, rec, fn, scope) {
  return function () {
    if (warn &amp;&amp; !exports.dep.cache[old])  {
      exports.dep.cache[old] = 1;
      console.warn('*** Mongoose %s is deprecated. Use %s instead.', old, rec);
    }
    return fn
      ? fn.apply(scope || this, arguments)
      : undefined
  }
}).cache = {};

process.nextTick(function () {
  var mongoose = require('./index');
  var val = mongoose.set('dep warnings');
  if ('boolean' == typeof val) {
    warn = val;
  } else if ('false' !== process.env.MONGOOSE_DEP_WARNINGS) {
    warn = true;
  }
});

virtualtype

lib/virtualtype.js

VirtualType constructor

This is what mongoose uses to define virtual attributes via Schema.prototype.virtual

  • api: public

function VirtualType (options) {
  this.getters = [];
  this.setters = [];
  this.options = options || {};
}

Adds a getter

  • param: Function fn

  • return: VirtualType this

  • api: public

VirtualType.prototype.get = function (fn) {
  this.getters.push(fn);
  return this;
};

Adds a setter

  • param: Function fn

  • return: VirtualType this

  • api: public

VirtualType.prototype.set = function (fn) {
  this.setters.push(fn);
  return this;
};

Applies getters

  • param: Object value

  • param: Object scope

  • api: public

VirtualType.prototype.applyGetters = function (value, scope) {
  var v = value;
  for (var l = this.getters.length - 1; l &gt;= 0; l--){
    v = this.getters[l].call(scope, v);
  }
  return v;
};

Applies setters

  • param: Object value

  • param: Object scope

  • api: public

VirtualType.prototype.applySetters = function (value, scope) {
  var v = value;
  for (var l = this.setters.length - 1; l &gt;= 0; l--){
    this.setters[l].call(scope, v);
  }
  return v;
};

module.exports = VirtualType;