arnaucube
arnaucube/thoughtsPublic Self-hosted repository
thoughts/node_modules/mongoose/lib/model.js
node_modules/mongoose/lib/model.jsRaw
   1/*!
   2 * Module dependencies.
   3 */
   4
   5var Document = require('./document')
   6  , MongooseArray = require('./types/array')
   7  , MongooseBuffer = require('./types/buffer')
   8  , MongooseError = require('./error')
   9  , VersionError = MongooseError.VersionError
  10  , DivergentArrayError = MongooseError.DivergentArrayError
  11  , Query = require('./query')
  12  , Schema = require('./schema')
  13  , Types = require('./schema/index')
  14  , utils = require('./utils')
  15  , hasOwnProperty = utils.object.hasOwnProperty
  16  , isMongooseObject = utils.isMongooseObject
  17  , EventEmitter = require('events').EventEmitter
  18  , merge = utils.merge
  19  , Promise = require('./promise')
  20  , assert = require('assert')
  21  , tick = utils.tick
  22
  23var VERSION_WHERE = 1
  24  , VERSION_INC = 2
  25  , VERSION_ALL = VERSION_WHERE | VERSION_INC;
  26
  27/**
  28 * Model constructor
  29 *
  30 * Provides the interface to MongoDB collections as well as creates document instances.
  31 *
  32 * @param {Object} doc values with which to create the document
  33 * @inherits Document
  34 * @event `error`: If listening to this event, it is emitted when a document was saved without passing a callback and an `error` occurred. If not listening, the event bubbles to the connection used to create this Model.
  35 * @event `index`: Emitted after `Model#ensureIndexes` completes. If an error occurred it is passed with the event.
  36 * @api public
  37 */
  38
  39function Model (doc, fields, skipId) {
  40  Document.call(this, doc, fields, skipId);
  41};
  42
  43/*!
  44 * Inherits from Document.
  45 *
  46 * All Model.prototype features are available on
  47 * top level (non-sub) documents.
  48 */
  49
  50Model.prototype.__proto__ = Document.prototype;
  51
  52/**
  53 * Connection the model uses.
  54 *
  55 * @api public
  56 * @property db
  57 */
  58
  59Model.prototype.db;
  60
  61/**
  62 * Collection the model uses.
  63 *
  64 * @api public
  65 * @property collection
  66 */
  67
  68Model.prototype.collection;
  69
  70/**
  71 * The name of the model
  72 *
  73 * @api public
  74 * @property modelName
  75 */
  76
  77Model.prototype.modelName;
  78
  79/*!
  80 * Handles doc.save() callbacks
  81 */
  82
  83function handleSave (promise, self) {
  84  return tick(function handleSave (err, result) {
  85    if (err) {
  86      // If the initial insert fails provide a second chance.
  87      // (If we did this all the time we would break updates)
  88      if (self.$__.inserting) {
  89        self.isNew = true;
  90        self.emit('isNew', true);
  91      }
  92      promise.error(err);
  93      promise = self = null;
  94      return;
  95    }
  96
  97    self.$__storeShard();
  98
  99    var numAffected;
 100    if (result) {
 101      // when inserting, the array of created docs is returned
 102      numAffected = result.length
 103        ? result.length
 104        : result;
 105    } else {
 106      numAffected = 0;
 107    }
 108
 109    // was this an update that required a version bump?
 110    if (self.$__.version && !self.$__.inserting) {
 111      var doIncrement = VERSION_INC === (VERSION_INC & self.$__.version);
 112      self.$__.version = undefined;
 113
 114      // increment version if was successful
 115      if (numAffected > 0) {
 116        if (doIncrement) {
 117          var key = self.schema.options.versionKey;
 118          var version = self.getValue(key) | 0;
 119          self.setValue(key, version + 1);
 120        }
 121      } else {
 122        // the update failed. pass an error back
 123        promise.error(new VersionError);
 124        promise = self = null;
 125        return;
 126      }
 127    }
 128
 129    self.emit('save', self, numAffected);
 130    promise.complete(self, numAffected);
 131    promise = self = null;
 132  });
 133}
 134
 135/**
 136 * Saves this document.
 137 *
 138 * ####Example:
 139 *
 140 *     product.sold = Date.now();
 141 *     product.save(function (err, product, numberAffected) {
 142 *       if (err) ..
 143 *     })
 144 *
 145 * The callback will receive three parameters, `err` if an error occurred, `product` which is the saved `product`, and `numberAffected` which will be 1 when the document was found and updated in the database, otherwise 0.
 146 *
 147 * The `fn` callback is optional. If no `fn` is passed and validation fails, the validation error will be emitted on the connection used to create this model.
 148 *
 149 *     var db = mongoose.createConnection(..);
 150 *     var schema = new Schema(..);
 151 *     var Product = db.model('Product', schema);
 152 *
 153 *     db.on('error', handleError);
 154 *
 155 * However, if you desire more local error handling you can add an `error` listener to the model and handle errors there instead.
 156 *
 157 *     Product.on('error', handleError);
 158 *
 159 * @param {Function} [fn] optional callback
 160 * @api public
 161 * @see middleware http://mongoosejs.com/docs/middleware.html
 162 */
 163
 164Model.prototype.save = function save (fn) {
 165  var promise = new Promise(fn)
 166    , complete = handleSave(promise, this)
 167    , options = {}
 168
 169  if (this.schema.options.safe) {
 170    options.safe = this.schema.options.safe;
 171  }
 172
 173  if (this.isNew) {
 174    // send entire doc
 175    var obj = this.toObject({ depopulate: 1 });
 176    this.$__version(true, obj);
 177    this.collection.insert(obj, options, complete);
 178    this.$__reset();
 179    this.isNew = false;
 180    this.emit('isNew', false);
 181    // Make it possible to retry the insert
 182    this.$__.inserting = true;
 183
 184  } else {
 185    // Make sure we don't treat it as a new object on error,
 186    // since it already exists
 187    this.$__.inserting = false;
 188
 189    var delta = this.$__delta();
 190    if (delta) {
 191      if (delta instanceof Error) return complete(delta);
 192      var where = this.$__where(delta[0]);
 193      this.$__reset();
 194      this.collection.update(where, delta[1], options, complete);
 195    } else {
 196      this.$__reset();
 197      complete(null);
 198    }
 199
 200    this.emit('isNew', false);
 201  }
 202};
 203
 204/*!
 205 * Apply the operation to the delta (update) clause as
 206 * well as track versioning for our where clause.
 207 *
 208 * @param {Document} self
 209 * @param {Object} where
 210 * @param {Object} delta
 211 * @param {Object} data
 212 * @param {Mixed} val
 213 * @param {String} [operation]
 214 */
 215
 216function operand (self, where, delta, data, val, op) {
 217  // delta
 218  op || (op = '$set');
 219  if (!delta[op]) delta[op] = {};
 220  delta[op][data.path] = val;
 221
 222  // disabled versioning?
 223  if (false === self.schema.options.versionKey) return;
 224
 225  // already marked for versioning?
 226  if (VERSION_ALL === (VERSION_ALL & self.$__.version)) return;
 227
 228  switch (op) {
 229    case '$set':
 230    case '$unset':
 231    case '$pop':
 232    case '$pull':
 233    case '$pullAll':
 234    case '$push':
 235    case '$pushAll':
 236    case '$addToSet':
 237      break;
 238    default:
 239      // nothing to do
 240      return;
 241  }
 242
 243  // ensure updates sent with positional notation are
 244  // editing the correct array element.
 245  // only increment the version if an array position changes.
 246  // modifying elements of an array is ok if position does not change.
 247
 248  if ('$push' == op || '$pushAll' == op || '$addToSet' == op) {
 249    self.$__.version = VERSION_INC;
 250  }
 251  else if (/^\$p/.test(op)) {
 252    // potentially changing array positions
 253    self.increment();
 254  }
 255  else if (Array.isArray(val)) {
 256    // $set an array
 257    self.increment();
 258  }
 259  // now handling $set, $unset
 260  else if (/\.\d+\.|\.\d+$/.test(data.path)) {
 261    // subpath of array
 262    self.$__.version = VERSION_WHERE;
 263  }
 264}
 265
 266/*!
 267 * Compiles an update and where clause for a `val` with _atomics.
 268 *
 269 * @param {Document} self
 270 * @param {Object} where
 271 * @param {Object} delta
 272 * @param {Object} data
 273 * @param {Array} value
 274 */
 275
 276function handleAtomics (self, where, delta, data, value) {
 277  if (delta.$set && delta.$set[data.path]) {
 278    // $set has precedence over other atomics
 279    return;
 280  }
 281
 282  if ('function' == typeof value.$__getAtomics) {
 283    value.$__getAtomics().forEach(function (atomic) {
 284      var op = atomic[0];
 285      var val = atomic[1];
 286      operand(self, where, delta, data, val, op);
 287    })
 288    return;
 289  }
 290
 291  // legacy support for plugins
 292
 293  var atomics = value._atomics
 294    , ops = Object.keys(atomics)
 295    , i = ops.length
 296    , val
 297    , op;
 298
 299  if (0 === i) {
 300    // $set
 301
 302    if (isMongooseObject(value)) {
 303      value = value.toObject({ depopulate: 1 });
 304    } else if (value.valueOf) {
 305      value = value.valueOf();
 306    }
 307
 308    return operand(self, where, delta, data, value);
 309  }
 310
 311  while (i--) {
 312    op = ops[i];
 313    val = atomics[op];
 314
 315    if (isMongooseObject(val)) {
 316      val = val.toObject({ depopulate: 1 })
 317    } else if (Array.isArray(val)) {
 318      val = val.map(function (mem) {
 319        return isMongooseObject(mem)
 320          ? mem.toObject({ depopulate: 1 })
 321          : mem;
 322      })
 323    } else if (val.valueOf) {
 324      val = val.valueOf()
 325    }
 326
 327    if ('$addToSet' === op)
 328      val = { $each: val };
 329
 330    operand(self, where, delta, data, val, op);
 331  }
 332}
 333
 334/**
 335 * Produces a special query document of the modified properties used in updates.
 336 *
 337 * @api private
 338 * @method $__delta
 339 * @memberOf Model
 340 */
 341
 342Model.prototype.$__delta = function () {
 343  var dirty = this.$__dirty();
 344  if (!dirty.length && VERSION_ALL != this.$__.version) return;
 345
 346  var where = {}
 347    , delta = {}
 348    , len = dirty.length
 349    , divergent = []
 350    , d = 0
 351    , val
 352    , obj
 353
 354  for (; d < len; ++d) {
 355    var data = dirty[d]
 356    var value = data.value
 357    var schema = data.schema
 358
 359    var match = checkDivergentArray(this, data.path, value);
 360    if (match) {
 361      divergent.push(match);
 362      continue;
 363    }
 364
 365    if (divergent.length) continue;
 366
 367    if (undefined === value) {
 368      operand(this, where, delta, data, 1, '$unset');
 369
 370    } else if (null === value) {
 371      operand(this, where, delta, data, null);
 372
 373    } else if (value._path && value._atomics) {
 374      // arrays and other custom types (support plugins etc)
 375      handleAtomics(this, where, delta, data, value);
 376
 377    } else if (value._path && Buffer.isBuffer(value)) {
 378      // MongooseBuffer
 379      value = value.toObject();
 380      operand(this, where, delta, data, value);
 381
 382    } else {
 383      value = utils.clone(value, { depopulate: 1 });
 384      operand(this, where, delta, data, value);
 385    }
 386  }
 387
 388  if (divergent.length) {
 389    return new DivergentArrayError(divergent);
 390  }
 391
 392  if (this.$__.version) {
 393    this.$__version(where, delta);
 394  }
 395
 396  return [where, delta];
 397}
 398
 399/*!
 400 * Determine if array was populated with some form of filter and is now
 401 * being updated in a manner which could overwrite data unintentionally.
 402 *
 403 * @see https://github.com/LearnBoost/mongoose/issues/1334
 404 * @param {Document} doc
 405 * @param {String} path
 406 * @return {String|undefined}
 407 */
 408
 409function checkDivergentArray (doc, path, array) {
 410  // see if we populated this path
 411  var pop = doc.populated(path, true);
 412
 413  if (!pop && doc.$__.selected) {
 414    // If any array was selected using an $elemMatch projection, we deny the update.
 415    // NOTE: MongoDB only supports projected $elemMatch on top level array.
 416    var top = path.split('.')[0];
 417    if (doc.$__.selected[top] && doc.$__.selected[top].$elemMatch) {
 418      return top;
 419    }
 420  }
 421
 422  if (!(pop && array instanceof MongooseArray)) return;
 423
 424  // If the array was populated using options that prevented all
 425  // documents from being returned (match, skip, limit) or they
 426  // deselected the _id field, $pop and $set of the array are
 427  // not safe operations. If _id was deselected, we do not know
 428  // how to remove elements. $pop will pop off the _id from the end
 429  // of the array in the db which is not guaranteed to be the
 430  // same as the last element we have here. $set of the entire array
 431  // would be similarily destructive as we never received all
 432  // elements of the array and potentially would overwrite data.
 433  var check = pop.options.match ||
 434              pop.options.options && hasOwnProperty(pop.options.options, 'limit') || // 0 is not permitted
 435              pop.options.options && pop.options.options.skip || // 0 is permitted
 436              pop.options.select && // deselected _id?
 437                (0 === pop.options.select._id ||
 438                /\s?-_id\s?/.test(pop.options.select))
 439
 440  if (check) {
 441    var atomics = array._atomics;
 442    if (0 === Object.keys(atomics).length || atomics.$set || atomics.$pop) {
 443      return path;
 444    }
 445  }
 446}
 447
 448/**
 449 * Appends versioning to the where and update clauses.
 450 *
 451 * @api private
 452 * @method $__version
 453 * @memberOf Model
 454 */
 455
 456Model.prototype.$__version = function (where, delta) {
 457  var key = this.schema.options.versionKey;
 458
 459  if (true === where) {
 460    // this is an insert
 461    if (key) this.setValue(key, delta[key] = 0);
 462    return;
 463  }
 464
 465  // updates
 466
 467  // only apply versioning if our versionKey was selected. else
 468  // there is no way to select the correct version. we could fail
 469  // fast here and force them to include the versionKey but
 470  // thats a bit intrusive. can we do this automatically?
 471  if (!this.isSelected(key)) {
 472    return;
 473  }
 474
 475  // $push $addToSet don't need the where clause set
 476  if (VERSION_WHERE === (VERSION_WHERE & this.$__.version)) {
 477    where[key] = this.getValue(key);
 478  }
 479
 480  if (VERSION_INC === (VERSION_INC & this.$__.version)) {
 481    delta.$inc || (delta.$inc = {});
 482    delta.$inc[key] = 1;
 483  }
 484}
 485
 486/**
 487 * Signal that we desire an increment of this documents version.
 488 *
 489 * ####Example:
 490 *
 491 *     Model.findById(id, function (err, doc) {
 492 *       doc.increment();
 493 *       doc.save(function (err) { .. })
 494 *     })
 495 *
 496 * @see versionKeys http://mongoosejs.com/docs/guide.html#versionKey
 497 * @api public
 498 */
 499
 500Model.prototype.increment = function increment () {
 501  this.$__.version = VERSION_ALL;
 502  return this;
 503}
 504
 505/**
 506 * Returns a query object which applies shardkeys if they exist.
 507 *
 508 * @api private
 509 * @method $__where
 510 * @memberOf Model
 511 */
 512
 513Model.prototype.$__where = function _where (where) {
 514  where || (where = {});
 515
 516  var paths
 517    , len
 518
 519  if (this.$__.shardval) {
 520    paths = Object.keys(this.$__.shardval)
 521    len = paths.length
 522
 523    for (var i = 0; i < len; ++i) {
 524      where[paths[i]] = this.$__.shardval[paths[i]];
 525    }
 526  }
 527
 528  where._id = this._doc._id;
 529  return where;
 530}
 531
 532/**
 533 * Removes this document from the db.
 534 *
 535 * ####Example:
 536 *
 537 *     product.remove(function (err) {
 538 *       if (err) return handleError(err);
 539 *       Product.findById(product._id, function (err, product) {
 540 *         console.log(product) // null
 541 *       })
 542 *     })
 543 *
 544 * @param {Function} [fn] optional callback
 545 * @api public
 546 */
 547
 548Model.prototype.remove = function remove (fn) {
 549  if (this.$__.removing) {
 550    this.$__.removing.addBack(fn);
 551    return this;
 552  }
 553
 554  var promise = this.$__.removing = new Promise(fn)
 555    , where = this.$__where()
 556    , self = this
 557    , options = {}
 558
 559  if (this.schema.options.safe) {
 560    options.safe = this.schema.options.safe;
 561  }
 562
 563  this.collection.remove(where, options, tick(function (err) {
 564    if (err) {
 565      promise.error(err);
 566      promise = self = self.$__.removing = where = options = null;
 567      return;
 568    }
 569    self.emit('remove', self);
 570    promise.complete();
 571    promise = self = where = options = null;
 572  }));
 573
 574  return this;
 575};
 576
 577/**
 578 * Returns another Model instance.
 579 *
 580 * ####Example:
 581 *
 582 *     var doc = new Tank;
 583 *     doc.model('User').findById(id, callback);
 584 *
 585 * @param {String} name model name
 586 * @api public
 587 */
 588
 589Model.prototype.model = function model (name) {
 590  return this.db.model(name);
 591};
 592
 593// Model (class) features
 594
 595/*!
 596 * Give the constructor the ability to emit events.
 597 */
 598
 599for (var i in EventEmitter.prototype)
 600  Model[i] = EventEmitter.prototype[i];
 601
 602/**
 603 * Called when the model compiles.
 604 *
 605 * @api private
 606 */
 607
 608Model.init = function init () {
 609  if (this.schema.options.autoIndex) {
 610    this.ensureIndexes();
 611  }
 612
 613  this.schema.emit('init', this);
 614};
 615
 616/**
 617 * Sends `ensureIndex` commands to mongo for each index declared in the schema.
 618 *
 619 * ####Example:
 620 *
 621 *     Event.ensureIndexes(function (err) {
 622 *       if (err) return handleError(err);
 623 *     });
 624 *
 625 * After completion, an `index` event is emitted on this `Model` passing an error if one occurred.
 626 *
 627 * ####Example:
 628 *
 629 *     var eventSchema = new Schema({ thing: { type: 'string', unique: true }})
 630 *     var Event = mongoose.model('Event', eventSchema);
 631 *
 632 *     Event.on('index', function (err) {
 633 *       if (err) console.error(err); // error occurred during index creation
 634 *     })
 635 *
 636 * _NOTE: It is not recommended that you run this in production. Index creation may impact database performance depending on your load. Use with caution._
 637 *
 638 * The `ensureIndex` commands are not sent in parallel. This is to avoid the `MongoError: cannot add index with a background operation in progress` error. See [this ticket](https://github.com/LearnBoost/mongoose/issues/1365) for more information.
 639 *
 640 * @param {Function} [cb] optional callback
 641 * @api public
 642 */
 643
 644Model.ensureIndexes = function ensureIndexes (cb) {
 645  var indexes = this.schema.indexes();
 646  if (!indexes.length) {
 647    return cb && process.nextTick(cb);
 648  }
 649
 650  // Indexes are created one-by-one to support how MongoDB < 2.4 deals
 651  // with background indexes.
 652
 653  var self = this
 654    , safe = self.schema.options.safe
 655
 656  function done (err) {
 657    self.emit('index', err);
 658    cb && cb(err);
 659  }
 660
 661  function create () {
 662    var index = indexes.shift();
 663    if (!index) return done();
 664
 665    var options = index[1];
 666    options.safe = safe;
 667    self.collection.ensureIndex(index[0], options, tick(function (err) {
 668      if (err) return done(err);
 669      create();
 670    }));
 671  }
 672
 673  create();
 674}
 675
 676/**
 677 * Schema the model uses.
 678 *
 679 * @property schema
 680 * @receiver Model
 681 * @api public
 682 */
 683
 684Model.schema;
 685
 686/*!
 687 * Connection instance the model uses.
 688 *
 689 * @property db
 690 * @receiver Model
 691 * @api public
 692 */
 693
 694Model.db;
 695
 696/*!
 697 * Collection the model uses.
 698 *
 699 * @property collection
 700 * @receiver Model
 701 * @api public
 702 */
 703
 704Model.collection;
 705
 706/**
 707 * Base Mongoose instance the model uses.
 708 *
 709 * @property base
 710 * @receiver Model
 711 * @api public
 712 */
 713
 714Model.base;
 715
 716/**
 717 * Removes documents from the collection.
 718 *
 719 * ####Example:
 720 *
 721 *     Comment.remove({ title: 'baby born from alien father' }, function (err) {
 722 *
 723 *     });
 724 *
 725 * ####Note:
 726 *
 727 * To remove documents without waiting for a response from MongoDB, do not pass a `callback`, then call `exec` on the returned [Query](#query-js):
 728 *
 729 *     var query = Comment.remove({ _id: id });
 730 *     query.exec();
 731 *
 732 * ####Note:
 733 *
 734 * This method sends a remove command directly to MongoDB, no Mongoose documents are involved. Because no Mongoose documents are involved, _no middleware (hooks) are executed_.
 735 *
 736 * @param {Object} conditions
 737 * @param {Function} [callback]
 738 * @return {Query}
 739 * @api public
 740 */
 741
 742Model.remove = function remove (conditions, callback) {
 743  if ('function' === typeof conditions) {
 744    callback = conditions;
 745    conditions = {};
 746  }
 747
 748  var query = new Query(conditions).bind(this, 'remove');
 749
 750  if ('undefined' === typeof callback)
 751    return query;
 752
 753  this._applyNamedScope(query);
 754  return query.remove(callback);
 755};
 756
 757/**
 758 * Finds documents
 759 *
 760 * The `conditions` are cast to their respective SchemaTypes before the command is sent.
 761 *
 762 * ####Examples:
 763 *
 764 *     // named john and at least 18
 765 *     MyModel.find({ name: 'john', age: { $gte: 18 }});
 766 *
 767 *     // executes immediately, passing results to callback
 768 *     MyModel.find({ name: 'john', age: { $gte: 18 }}, function (err, docs) {});
 769 *
 770 *     // name LIKE john and only selecting the "name" and "friends" fields, executing immediately
 771 *     MyModel.find({ name: /john/i }, 'name friends', function (err, docs) { })
 772 *
 773 *     // passing options
 774 *     MyModel.find({ name: /john/i }, null, { skip: 10 })
 775 *
 776 *     // passing options and executing immediately
 777 *     MyModel.find({ name: /john/i }, null, { skip: 10 }, function (err, docs) {});
 778 *
 779 *     // executing a query explicitly
 780 *     var query = MyModel.find({ name: /john/i }, null, { skip: 10 })
 781 *     query.exec(function (err, docs) {});
 782 *
 783 *     // using the promise returned from executing a query
 784 *     var query = MyModel.find({ name: /john/i }, null, { skip: 10 });
 785 *     var promise = query.exec();
 786 *     promise.addBack(function (err, docs) {});
 787 *
 788 * @param {Object} conditions
 789 * @param {Object} [fields] optional fields to select
 790 * @param {Object} [options] optional
 791 * @param {Function} [callback]
 792 * @return {Query}
 793 * @see field selection #query_Query-select
 794 * @see promise #promise-js
 795 * @api public
 796 */
 797
 798Model.find = function find (conditions, fields, options, callback) {
 799  if ('function' == typeof conditions) {
 800    callback = conditions;
 801    conditions = {};
 802    fields = null;
 803    options = null;
 804  } else if ('function' == typeof fields) {
 805    callback = fields;
 806    fields = null;
 807    options = null;
 808  } else if ('function' == typeof options) {
 809    callback = options;
 810    options = null;
 811  }
 812
 813  var query = new Query(conditions, options);
 814  query.bind(this, 'find');
 815  query.select(fields);
 816
 817  if ('undefined' === typeof callback)
 818    return query;
 819
 820  this._applyNamedScope(query);
 821  return query.find(callback);
 822};
 823
 824/**
 825 * Merges the current named scope query into `query`.
 826 *
 827 * @param {Query} query
 828 * @return {Query}
 829 * @api private
 830 */
 831
 832Model._applyNamedScope = function _applyNamedScope (query) {
 833  var cQuery = this._cumulativeQuery;
 834
 835  if (cQuery) {
 836    merge(query._conditions, cQuery._conditions);
 837    if (query._fields && cQuery._fields)
 838      merge(query._fields, cQuery._fields);
 839    if (query.options && cQuery.options)
 840      merge(query.options, cQuery.options);
 841    delete this._cumulativeQuery;
 842  }
 843
 844  return query;
 845}
 846
 847/**
 848 * Finds a single document by id.
 849 *
 850 * The `id` is cast based on the Schema before sending the command.
 851 *
 852 * ####Example:
 853 *
 854 *     // find adventure by id and execute immediately
 855 *     Adventure.findById(id, function (err, adventure) {});
 856 *
 857 *     // same as above
 858 *     Adventure.findById(id).exec(callback);
 859 *
 860 *     // select only the adventures name and length
 861 *     Adventure.findById(id, 'name length', function (err, adventure) {});
 862 *
 863 *     // same as above
 864 *     Adventure.findById(id, 'name length').exec(callback);
 865 *
 866 *     // include all properties except for `length`
 867 *     Adventure.findById(id, '-length').exec(function (err, adventure) {});
 868 *
 869 *     // passing options (in this case return the raw js objects, not mongoose documents by passing `lean`
 870 *     Adventure.findById(id, 'name', { lean: true }, function (err, doc) {});
 871 *
 872 *     // same as above
 873 *     Adventure.findById(id, 'name').lean().exec(function (err, doc) {});
 874 *
 875 * @param {ObjectId|HexId} id objectid, or a value that can be casted to one
 876 * @param {Object} [fields] optional fields to select
 877 * @param {Object} [options] optional
 878 * @param {Function} [callback]
 879 * @return {Query}
 880 * @see field selection #query_Query-select
 881 * @see lean queries #query_Query-lean
 882 * @api public
 883 */
 884
 885Model.findById = function findById (id, fields, options, callback) {
 886  return this.findOne({ _id: id }, fields, options, callback);
 887};
 888
 889/**
 890 * Finds one document.
 891 *
 892 * The `conditions` are cast to their respective SchemaTypes before the command is sent.
 893 *
 894 * ####Example:
 895 *
 896 *     // find one iphone adventures - iphone adventures??
 897 *     Adventure.findOne({ type: 'iphone' }, function (err, adventure) {});
 898 *
 899 *     // same as above
 900 *     Adventure.findOne({ type: 'iphone' }).exec(function (err, adventure) {});
 901 *
 902 *     // select only the adventures name
 903 *     Adventure.findOne({ type: 'iphone' }, 'name', function (err, adventure) {});
 904 *
 905 *     // same as above
 906 *     Adventure.findOne({ type: 'iphone' }, 'name').exec(function (err, adventure) {});
 907 *
 908 *     // specify options, in this case lean
 909 *     Adventure.findOne({ type: 'iphone' }, 'name', { lean: true }, callback);
 910 *
 911 *     // same as above
 912 *     Adventure.findOne({ type: 'iphone' }, 'name', { lean: true }).exec(callback);
 913 *
 914 *     // chaining findOne queries (same as above)
 915 *     Adventure.findOne({ type: 'iphone' }).select('name').lean().exec(callback);
 916 *
 917 * @param {Object} conditions
 918 * @param {Object} [fields] optional fields to select
 919 * @param {Object} [options] optional
 920 * @param {Function} [callback]
 921 * @return {Query}
 922 * @see field selection #query_Query-select
 923 * @see lean queries #query_Query-lean
 924 * @api public
 925 */
 926
 927Model.findOne = function findOne (conditions, fields, options, callback) {
 928  if ('function' == typeof options) {
 929    callback = options;
 930    options = null;
 931  } else if ('function' == typeof fields) {
 932    callback = fields;
 933    fields = null;
 934    options = null;
 935  } else if ('function' == typeof conditions) {
 936    callback = conditions;
 937    conditions = {};
 938    fields = null;
 939    options = null;
 940  }
 941
 942  var query = new Query(conditions, options).select(fields).bind(this, 'findOne');
 943
 944  if ('undefined' == typeof callback)
 945    return query;
 946
 947  this._applyNamedScope(query);
 948  return query.findOne(callback);
 949};
 950
 951/**
 952 * Counts number of matching documents in a database collection.
 953 *
 954 * ####Example:
 955 *
 956 *     Adventure.count({ type: 'jungle' }, function (err, count) {
 957 *       if (err) ..
 958 *       console.log('there are %d jungle adventures', count);
 959 *     });
 960 *
 961 * @param {Object} conditions
 962 * @param {Function} [callback]
 963 * @return {Query}
 964 * @api public
 965 */
 966
 967Model.count = function count (conditions, callback) {
 968  if ('function' === typeof conditions)
 969    callback = conditions, conditions = {};
 970
 971  var query = new Query(conditions).bind(this, 'count');
 972  if ('undefined' == typeof callback)
 973    return query;
 974
 975  this._applyNamedScope(query);
 976  return query.count(callback);
 977};
 978
 979/**
 980 * Executes a DISTINCT command
 981 *
 982 * ####Example
 983 *
 984 *     Link.distinct('url', { clicks: {$gt: 100}}, function (err, result) {
 985 *       if (err) return handleError(err);
 986 *
 987 *       assert(Array.isArray(result));
 988 *       console.log('unique urls with more than 100 clicks', result);
 989 *     })
 990 *
 991 * @param {String} field
 992 * @param {Object} [conditions] optional
 993 * @param {Function} [callback]
 994 * @return {Query}
 995 * @api public
 996 */
 997
 998Model.distinct = function distinct (field, conditions, callback) {
 999  if ('function' == typeof conditions) {
1000    callback = conditions;
1001    conditions = {};
1002  }
1003
1004  var query = new Query(conditions).bind(this, 'distinct');
1005  if ('undefined' == typeof callback) {
1006    query._distinctArg = field;
1007    return query;
1008  }
1009
1010  this._applyNamedScope(query);
1011  return query.distinct(field, callback);
1012};
1013
1014/**
1015 * Creates a Query, applies the passed conditions, and returns the Query.
1016 *
1017 * For example, instead of writing:
1018 *
1019 *     User.find({age: {$gte: 21, $lte: 65}}, callback);
1020 *
1021 * we can instead write:
1022 *
1023 *     User.where('age').gte(21).lte(65).exec(callback);
1024 *
1025 * Since the Query class also supports `where` you can continue chaining
1026 *
1027 *     User
1028 *     .where('age').gte(21).lte(65)
1029 *     .where('name', /^b/i)
1030 *     ... etc
1031 *
1032 * @param {String} path
1033 * @param {Object} [val] optional value
1034 * @return {Query}
1035 * @api public
1036 */
1037
1038Model.where = function where (path, val) {
1039  var q = new Query().bind(this, 'find');
1040  return q.where.apply(q, arguments);
1041};
1042
1043/**
1044 * Creates a `Query` and specifies a `$where` condition.
1045 *
1046 * 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.
1047 *
1048 *     Blog.$where('this.comments.length > 5').exec(function (err, docs) {});
1049 *
1050 * @param {String|Function} argument is a javascript string or anonymous function
1051 * @method $where
1052 * @memberOf Model
1053 * @return {Query}
1054 * @see Query.$where #query_Query-%24where
1055 * @api public
1056 */
1057
1058Model.$where = function $where () {
1059  var q = new Query().bind(this, 'find');
1060  return q.$where.apply(q, arguments);
1061};
1062
1063/**
1064 * Issues a mongodb findAndModify update command.
1065 *
1066 * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned.
1067 *
1068 * ####Options:
1069 *
1070 * - `new`: bool - true to return the modified document rather than the original. defaults to true
1071 * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
1072 * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
1073 * - `select`: sets the document fields to return
1074 *
1075 * ####Examples:
1076 *
1077 *     A.findOneAndUpdate(conditions, update, options, callback) // executes
1078 *     A.findOneAndUpdate(conditions, update, options)  // returns Query
1079 *     A.findOneAndUpdate(conditions, update, callback) // executes
1080 *     A.findOneAndUpdate(conditions, update)           // returns Query
1081 *     A.findOneAndUpdate()                             // returns Query
1082 *
1083 * ####Note:
1084 *
1085 * All top level update keys which are not `atomic` operation names are treated as set operations:
1086 *
1087 * ####Example:
1088 *
1089 *     var query = { name: 'borne' };
1090 *     Model.findOneAndUpdate(query, { name: 'jason borne' }, options, callback)
1091 *
1092 *     // is sent as
1093 *     Model.findOneAndUpdate(query, { $set: { name: 'jason borne' }}, options, callback)
1094 *
1095 * This helps prevent accidentally overwriting your document with `{ name: 'jason borne' }`.
1096 *
1097 * ####Note:
1098 *
1099 * Although values are cast to their appropriate types when using the findAndModify helpers, the following are *not* applied:
1100 *
1101 * - defaults
1102 * - setters
1103 * - validators
1104 * - middleware
1105 *
1106 * If you need those features, use the traditional approach of first retrieving the document.
1107 *
1108 *     Model.findOne({ name: 'borne' }, function (err, doc) {
1109 *       if (err) ..
1110 *       doc.name = 'jason borne';
1111 *       doc.save(callback);
1112 *     })
1113 *
1114 * @param {Object} [conditions]
1115 * @param {Object} [update]
1116 * @param {Object} [options]
1117 * @param {Function} [callback]
1118 * @return {Query}
1119 * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
1120 * @api public
1121 */
1122
1123Model.findOneAndUpdate = function (conditions, update, options, callback) {
1124  if ('function' == typeof options) {
1125    callback = options;
1126    options = null;
1127  }
1128  else if (1 === arguments.length) {
1129    if ('function' == typeof conditions) {
1130      var msg = 'Model.findOneAndUpdate(): First argument must not be a function.\n\n'
1131              + '  ' + this.modelName + '.findOneAndUpdate(conditions, update, options, callback)\n'
1132              + '  ' + this.modelName + '.findOneAndUpdate(conditions, update, options)\n'
1133              + '  ' + this.modelName + '.findOneAndUpdate(conditions, update)\n'
1134              + '  ' + this.modelName + '.findOneAndUpdate(update)\n'
1135              + '  ' + this.modelName + '.findOneAndUpdate()\n';
1136      throw new TypeError(msg)
1137    }
1138    update = conditions;
1139    conditions = undefined;
1140  }
1141
1142  var fields;
1143  if (options && options.fields) {
1144    fields = options.fields;
1145    options.fields = undefined;
1146  }
1147
1148  var query = new Query(conditions);
1149  query.setOptions(options);
1150  query.select(fields);
1151  query.bind(this, 'findOneAndUpdate', update);
1152
1153  if ('undefined' == typeof callback)
1154    return query;
1155
1156  this._applyNamedScope(query);
1157  return query.findOneAndUpdate(callback);
1158}
1159
1160/**
1161 * Issues a mongodb findAndModify update command by a documents id.
1162 *
1163 * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned.
1164 *
1165 * ####Options:
1166 *
1167 * - `new`: bool - true to return the modified document rather than the original. defaults to true
1168 * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
1169 * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
1170 * - `select`: sets the document fields to return
1171 *
1172 * ####Examples:
1173 *
1174 *     A.findByIdAndUpdate(id, update, options, callback) // executes
1175 *     A.findByIdAndUpdate(id, update, options)  // returns Query
1176 *     A.findByIdAndUpdate(id, update, callback) // executes
1177 *     A.findByIdAndUpdate(id, update)           // returns Query
1178 *     A.findByIdAndUpdate()                     // returns Query
1179 *
1180 * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes      immediately if `callback` is passed else a Query object is returned.
1181 *
1182 * ####Options:
1183 *
1184 * - `new`: bool - true to return the modified document rather than the original. defaults to true
1185 * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
1186 * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
1187 *
1188 * ####Note:
1189 *
1190 * All top level update keys which are not `atomic` operation names are treated as set operations:
1191 *
1192 * ####Example:
1193 *
1194 *     Model.findByIdAndUpdate(id, { name: 'jason borne' }, options, callback)
1195 *
1196 *     // is sent as
1197 *     Model.findByIdAndUpdate(id, { $set: { name: 'jason borne' }}, options, callback)
1198 *
1199 * This helps prevent accidentally overwriting your document with `{ name: 'jason borne' }`.
1200 *
1201 * ####Note:
1202 *
1203 * Although values are cast to their appropriate types when using the findAndModify helpers, the following are *not* applied:
1204 *
1205 * - defaults
1206 * - setters
1207 * - validators
1208 * - middleware
1209 *
1210 * If you need those features, use the traditional approach of first retrieving the document.
1211 *
1212 *     Model.findById(id, function (err, doc) {
1213 *       if (err) ..
1214 *       doc.name = 'jason borne';
1215 *       doc.save(callback);
1216 *     })
1217 *
1218 * @param {ObjectId|HexId} id an ObjectId or string that can be cast to one.
1219 * @param {Object} [update]
1220 * @param {Object} [options]
1221 * @param {Function} [callback]
1222 * @return {Query}
1223 * @see Model.findOneAndUpdate #model_Model.findOneAndUpdate
1224 * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
1225 * @api public
1226 */
1227
1228Model.findByIdAndUpdate = function (id, update, options, callback) {
1229  var args;
1230
1231  if (1 === arguments.length) {
1232    if ('function' == typeof id) {
1233      var msg = 'Model.findByIdAndUpdate(): First argument must not be a function.\n\n'
1234                + '  ' + this.modelName + '.findByIdAndUpdate(id, callback)\n'
1235                + '  ' + this.modelName + '.findByIdAndUpdate(id)\n'
1236                + '  ' + this.modelName + '.findByIdAndUpdate()\n';
1237      throw new TypeError(msg)
1238    }
1239    return this.findOneAndUpdate({_id: id }, undefined);
1240  }
1241
1242  args = utils.args(arguments, 1);
1243  args.unshift({ _id: id });
1244  return this.findOneAndUpdate.apply(this, args);
1245}
1246
1247/**
1248 * Issue a mongodb findAndModify remove command.
1249 *
1250 * Finds a matching document, removes it, passing the found document (if any) to the callback.
1251 *
1252 * Executes immediately if `callback` is passed else a Query object is returned.
1253 *
1254 * ####Options:
1255 *
1256 * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
1257 * - `select`: sets the document fields to return
1258 *
1259 * ####Examples:
1260 *
1261 *     A.findOneAndRemove(conditions, options, callback) // executes
1262 *     A.findOneAndRemove(conditions, options)  // return Query
1263 *     A.findOneAndRemove(conditions, callback) // executes
1264 *     A.findOneAndRemove(conditions) // returns Query
1265 *     A.findOneAndRemove()           // returns Query
1266 *
1267 * Although values are cast to their appropriate types when using the findAndModify helpers, the following are *not* applied:
1268 *
1269 * - defaults
1270 * - setters
1271 * - validators
1272 * - middleware
1273 *
1274 * If you need those features, use the traditional approach of first retrieving the document.
1275 *
1276 *     Model.findById(id, function (err, doc) {
1277 *       if (err) ..
1278 *       doc.remove(callback);
1279 *     })
1280 *
1281 * @param {Object} conditions
1282 * @param {Object} [options]
1283 * @param {Function} [callback]
1284 * @return {Query}
1285 * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
1286 * @api public
1287 */
1288
1289Model.findOneAndRemove = function (conditions, options, callback) {
1290  if (1 === arguments.length && 'function' == typeof conditions) {
1291    var msg = 'Model.findOneAndRemove(): First argument must not be a function.\n\n'
1292              + '  ' + this.modelName + '.findOneAndRemove(conditions, callback)\n'
1293              + '  ' + this.modelName + '.findOneAndRemove(conditions)\n'
1294              + '  ' + this.modelName + '.findOneAndRemove()\n';
1295    throw new TypeError(msg)
1296  }
1297
1298  if ('function' == typeof options) {
1299    callback = options;
1300    options = undefined;
1301  }
1302
1303  var fields;
1304  if (options) {
1305    fields = options.select;
1306    options.select = undefined;
1307  }
1308
1309  var query = new Query(conditions);
1310  query.setOptions(options);
1311  query.select(fields);
1312  query.bind(this, 'findOneAndRemove');
1313
1314  if ('undefined' == typeof callback)
1315    return query;
1316
1317  this._applyNamedScope(query);
1318  return query.findOneAndRemove(callback);
1319}
1320
1321/**
1322 * Issue a mongodb findAndModify remove command by a documents id.
1323 *
1324 * Finds a matching document, removes it, passing the found document (if any) to the callback.
1325 *
1326 * Executes immediately if `callback` is passed, else a `Query` object is returned.
1327 *
1328 * ####Options:
1329 *
1330 * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
1331 * - `select`: sets the document fields to return
1332 *
1333 * ####Examples:
1334 *
1335 *     A.findByIdAndRemove(id, options, callback) // executes
1336 *     A.findByIdAndRemove(id, options)  // return Query
1337 *     A.findByIdAndRemove(id, callback) // executes
1338 *     A.findByIdAndRemove(id) // returns Query
1339 *     A.findByIdAndRemove()           // returns Query
1340 *
1341 * @param {ObjectId|HexString} id ObjectId or string that can be cast to one
1342 * @param {Object} [options]
1343 * @param {Function} [callback]
1344 * @return {Query}
1345 * @see Model.findOneAndRemove #model_Model.findOneAndRemove
1346 * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
1347 */
1348
1349Model.findByIdAndRemove = function (id, options, callback) {
1350  if (1 === arguments.length && 'function' == typeof id) {
1351    var msg = 'Model.findByIdAndRemove(): First argument must not be a function.\n\n'
1352              + '  ' + this.modelName + '.findByIdAndRemove(id, callback)\n'
1353              + '  ' + this.modelName + '.findByIdAndRemove(id)\n'
1354              + '  ' + this.modelName + '.findByIdAndRemove()\n';
1355    throw new TypeError(msg)
1356  }
1357
1358  return this.findOneAndRemove({ _id: id }, options, callback);
1359}
1360
1361/**
1362 * Shortcut for creating a new Document that is automatically saved to the db if valid.
1363 *
1364 * ####Example:
1365 *
1366 *     Candy.create({ type: 'jelly bean' }, { type: 'snickers' }, function (err, jellybean, snickers) {
1367 *       if (err) // ...
1368 *     });
1369 *
1370 *     var array = [{ type: 'jelly bean' }, { type: 'snickers' }];
1371 *     Candy.create(array, function (err, jellybean, snickers) {
1372 *       if (err) // ...
1373 *     });
1374 *
1375 * @param {Array|Object...} doc
1376 * @param {Function} fn callback
1377 * @api public
1378 */
1379
1380Model.create = function create (doc, fn) {
1381  if (1 === arguments.length) {
1382    return 'function' === typeof doc && doc(null);
1383  }
1384
1385  var self = this
1386    , docs = [null]
1387    , promise
1388    , count
1389    , args
1390
1391  if (Array.isArray(doc)) {
1392    args = doc;
1393  } else {
1394    args = utils.args(arguments, 0, arguments.length - 1);
1395    fn = arguments[arguments.length - 1];
1396  }
1397
1398  if (0 === args.length) return fn(null);
1399
1400  promise = new Promise(fn);
1401  count = args.length;
1402
1403  args.forEach(function (arg, i) {
1404    var doc = new self(arg);
1405    docs[i+1] = doc;
1406    doc.save(function (err) {
1407      if (err) return promise.error(err);
1408      --count || fn.apply(null, docs);
1409    });
1410  });
1411};
1412
1413/**
1414 * Updates documents in the database without returning them.
1415 *
1416 * ####Examples:
1417 *
1418 *     MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn);
1419 *     MyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, function (err, numberAffected, raw) {
1420 *       if (err) return handleError(err);
1421 *       console.log('The number of updated documents was %d', numberAffected);
1422 *       console.log('The raw response from Mongo was ', raw);
1423 *     });
1424 *
1425 * ####Valid options:
1426 *
1427 *  - `safe` (boolean) safe mode (defaults to value set in schema (true))
1428 *  - `upsert` (boolean) whether to create the doc if it doesn't match (false)
1429 *  - `multi` (boolean) whether multiple documents should be updated (false)
1430 *  - `strict` (boolean) overrides the `strict` option for this update
1431 *
1432 * All `update` values are cast to their appropriate SchemaTypes before being sent.
1433 *
1434 * The `callback` function receives `(err, numberAffected, rawResponse)`.
1435 *
1436 * - `err` is the error if any occurred
1437 * - `numberAffected` is the count of updated documents Mongo reported
1438 * - `rawResponse` is the full response from Mongo
1439 *
1440 * ####Note:
1441 *
1442 * All top level keys which are not `atomic` operation names are treated as set operations:
1443 *
1444 * ####Example:
1445 *
1446 *     var query = { name: 'borne' };
1447 *     Model.update(query, { name: 'jason borne' }, options, callback)
1448 *
1449 *     // is sent as
1450 *     Model.update(query, { $set: { name: 'jason borne' }}, options, callback)
1451 *
1452 * This helps prevent accidentally overwriting all documents in your collection with `{ name: 'jason borne' }`.
1453 *
1454 * ####Note:
1455 *
1456 * Be careful to not use an existing model instance for the update clause (this won't work and can cause weird behavior like infinite loops). Also, ensure that the update clause does not have an _id property, which causes Mongo to return a "Mod on _id not allowed" error.
1457 *
1458 * ####Note:
1459 *
1460 * To update documents without waiting for a response from MongoDB, do not pass a `callback`, then call `exec` on the returned [Query](#query-js):
1461 *
1462 *     Comment.update({ _id: id }, { $set: { text: 'changed' }}).exec();
1463 *
1464 * ####Note:
1465 *
1466 * Although values are casted to their appropriate types when using update, the following are *not* applied:
1467 *
1468 * - defaults
1469 * - setters
1470 * - validators
1471 * - middleware
1472 *
1473 * If you need those features, use the traditional approach of first retrieving the document.
1474 *
1475 *     Model.findOne({ name: 'borne' }, function (err, doc) {
1476 *       if (err) ..
1477 *       doc.name = 'jason borne';
1478 *       doc.save(callback);
1479 *     })
1480 *
1481 * @see strict schemas http://mongoosejs.com/docs/guide.html#strict
1482 * @param {Object} conditions
1483 * @param {Object} update
1484 * @param {Object} [options]
1485 * @param {Function} [callback]
1486 * @return {Query}
1487 * @api public
1488 */
1489
1490Model.update = function update (conditions, doc, options, callback) {
1491  if (arguments.length < 4) {
1492    if ('function' === typeof options) {
1493      // Scenario: update(conditions, doc, callback)
1494      callback = options;
1495      options = null;
1496    } else if ('function' === typeof doc) {
1497      // Scenario: update(doc, callback);
1498      callback = doc;
1499      doc = conditions;
1500      conditions = {};
1501      options = null;
1502    }
1503  }
1504
1505  var query = new Query(conditions, options).bind(this, 'update', doc);
1506
1507  if ('undefined' == typeof callback)
1508    return query;
1509
1510  this._applyNamedScope(query);
1511  return query.update(doc, callback);
1512};
1513
1514/**
1515 * Executes a mapReduce command.
1516 *
1517 * `o` is an object specifying all mapReduce options as well as the map and reduce functions. All options are delegated to the driver implementation.
1518 *
1519 * ####Example:
1520 *
1521 *     var o = {};
1522 *     o.map = function () { emit(this.name, 1) }
1523 *     o.reduce = function (k, vals) { return vals.length }
1524 *     User.mapReduce(o, function (err, results) {
1525 *       console.log(results)
1526 *     })
1527 *
1528 * ####Other options:
1529 *
1530 * - `query` {Object} query filter object.
1531 * - `limit` {Number} max number of documents
1532 * - `keeptemp` {Boolean, default:false} keep temporary data
1533 * - `finalize` {Function} finalize function
1534 * - `scope` {Object} scope variables exposed to map/reduce/finalize during execution
1535 * - `jsMode` {Boolean, default:false} it is possible to make the execution stay in JS. Provided in MongoDB > 2.0.X
1536 * - `verbose` {Boolean, default:false} provide statistics on job execution time.
1537 * - `out*` {Object, default: {inline:1}} sets the output target for the map reduce job.
1538 *
1539 * ####* out options:
1540 *
1541 * - `{inline:1}` the results are returned in an array
1542 * - `{replace: 'collectionName'}` add the results to collectionName: the results replace the collection
1543 * - `{reduce: 'collectionName'}` add the results to collectionName: if dups are detected, uses the reducer / finalize functions
1544 * - `{merge: 'collectionName'}` add the results to collectionName: if dups exist the new docs overwrite the old
1545 *
1546 * If `options.out` is set to `replace`, `merge`, or `reduce`, a Model instance is returned that can be used for further querying. Queries run against this model are all executed with the `lean` option; meaning only the js object is returned and no Mongoose magic is applied (getters, setters, etc).
1547 *
1548 * ####Example:
1549 *
1550 *     var o = {};
1551 *     o.map = function () { emit(this.name, 1) }
1552 *     o.reduce = function (k, vals) { return vals.length }
1553 *     o.out = { replace: 'createdCollectionNameForResults' }
1554 *     o.verbose = true;
1555 *     User.mapReduce(o, function (err, model, stats) {
1556 *       console.log('map reduce took %d ms', stats.processtime)
1557 *       model.find().where('value').gt(10).exec(function (err, docs) {
1558 *         console.log(docs);
1559 *       });
1560 *     })
1561 *
1562 * @param {Object} o an object specifying map-reduce options
1563 * @param {Function} callback
1564 * @see http://www.mongodb.org/display/DOCS/MapReduce
1565 * @api public
1566 */
1567
1568Model.mapReduce = function mapReduce (o, callback) {
1569  if ('function' != typeof callback) throw new Error('missing callback');
1570
1571  var self = this;
1572
1573  if (!Model.mapReduce.schema) {
1574    var opts = { noId: true, noVirtualId: true, strict: false }
1575    Model.mapReduce.schema = new Schema({}, opts);
1576  }
1577
1578  if (!o.out) o.out = { inline: 1 };
1579
1580  o.map = String(o.map);
1581  o.reduce = String(o.reduce);
1582
1583  if (o.query) {
1584    var q = new Query(o.query);
1585    q.cast(this);
1586    o.query = q._conditions;
1587    q = undefined;
1588  }
1589
1590  this.collection.mapReduce(null, null, o, function (err, ret, stats) {
1591    if (err) return callback(err);
1592
1593    if (ret.findOne && ret.mapReduce) {
1594      // returned a collection, convert to Model
1595      var model = Model.compile(
1596          '_mapreduce_' + ret.collectionName
1597        , Model.mapReduce.schema
1598        , ret.collectionName
1599        , self.db
1600        , self.base);
1601
1602      model._mapreduce = true;
1603
1604      return callback(err, model, stats);
1605    }
1606
1607    callback(err, ret, stats);
1608  });
1609}
1610
1611/**
1612 * Executes an aggregate command on this models collection.
1613 *
1614 * ####Example:
1615 *
1616 *     // find the max age of all users
1617 *     Users.aggregate(
1618 *         { $group: { _id: null, maxAge: { $max: '$age' }}}
1619 *       , { $project: { _id: 0, maxAge: 1 }}
1620 *       , function (err, res) {
1621 *       if (err) return handleError(err);
1622 *       console.log(res); // [ { maxAge: 98 } ]
1623 *     });
1624 *
1625 * ####NOTE:
1626 *
1627 * - At this time, arguments are not cast to the schema because $project operators allow redefining the "shape" of the documents at any stage of the pipeline.
1628 * - The documents returned are plain javascript objects, not mongoose documents cast to this models schema definition (since any shape of document can be returned).
1629 * - Requires MongoDB >= 2.1
1630 *
1631 * @param {Array} array an array of pipeline commands
1632 * @param {Object} [options]
1633 * @param {Function} callback
1634 * @see aggregation http://docs.mongodb.org/manual/applications/aggregation/
1635 * @see driver http://mongodb.github.com/node-mongodb-native/api-generated/collection.html#aggregate
1636 * @api public
1637 */
1638
1639Model.aggregate = function aggregate () {
1640  return this.collection.aggregate.apply(this.collection, arguments);
1641}
1642
1643/**
1644 * Populates document references.
1645 *
1646 * ####Available options:
1647 *
1648 * - path: space delimited path(s) to populate
1649 * - select: optional fields to select
1650 * - match: optional query conditions to match
1651 * - model: optional name of the model to use for population
1652 * - options: optional query options like sort, limit, etc
1653 *
1654 * ####Examples:
1655 *
1656 *     // populates a single object
1657 *     User.findById(id, function (err, user) {
1658 *       var opts = [
1659 *           { path: 'company', match: { x: 1 }, select: 'name' }
1660 *         , { path: 'notes', options: { limit: 10 }, model: 'override' }
1661 *       ]
1662 *
1663 *       User.populate(user, opts, function (err, user) {
1664 *         console.log(user);
1665 *       })
1666 *     })
1667 *
1668 *     // populates an array of objects
1669 *     User.find(match, function (err, users) {
1670 *       var opts = [{ path: 'company', match: { x: 1 }, select: 'name' }]
1671 *
1672 *       User.populate(users, opts, function (err, user) {
1673 *         console.log(user);
1674 *       })
1675 *     })
1676 *
1677 *     // imagine a Weapon model exists with two saved documents:
1678 *     //   { _id: 389, name: 'whip' }
1679 *     //   { _id: 8921, name: 'boomerang' }
1680 *
1681 *     var user = { name: 'Indiana Jones', weapon: 389 }
1682 *     Weapon.populate(user, { path: 'weapon', model: 'Weapon' }, function (err, user) {
1683 *       console.log(user.weapon.name) // whip
1684 *     })
1685 *
1686 *     // populate many plain objects
1687 *     var users = [{ name: 'Indiana Jones', weapon: 389 }]
1688 *     users.push({ name: 'Batman', weapon: 8921 })
1689 *     Weapon.populate(users, { path: 'weapon' }, function (err, users) {
1690 *       users.forEach(function (user) {
1691 *         console.log('%s uses a %s', users.name, user.weapon.name)
1692 *         // Indiana Jones uses a whip
1693 *         // Batman uses a boomerang
1694 *       })
1695 *     })
1696 *     // Note that we didn't need to specify the Weapon model because
1697 *     // we were already using it's populate() method.
1698 *
1699 * @param {Document|Array} docs Either a single document or array of documents to populate.
1700 * @param {Object} options A hash of key/val (path, options) used for population.
1701 * @param {Function} cb(err,doc) A callback, executed upon completion. Receives `err` and the `doc(s)`.
1702 * @api public
1703 */
1704
1705Model.populate = function (docs, paths, cb) {
1706  assert.equal('function', typeof cb);
1707
1708  // always callback on nextTick for consistent async behavior
1709  function callback () {
1710    var args = utils.args(arguments);
1711    process.nextTick(function () {
1712      cb.apply(null, args);
1713    });
1714  }
1715
1716  // normalized paths
1717  var paths = utils.populate(paths);
1718  var pending = paths.length;
1719
1720  if (0 === pending) {
1721    return callback(null, docs);
1722  }
1723
1724  // each path has its own query options and must be executed separately
1725  var i = pending;
1726  var path;
1727  while (i--) {
1728    path = paths[i];
1729    populate(this, docs, path, next);
1730  }
1731
1732  function next (err) {
1733    if (next.err) return;
1734    if (err) return callback(next.err = err);
1735    if (--pending) return;
1736    callback(null, docs);
1737  }
1738}
1739
1740/*!
1741 * Prepare population options
1742 */
1743
1744function populate (model, docs, options, cb) {
1745  var path = options.path
1746  var schema = model._getSchema(path);
1747
1748  // handle document arrays
1749  if (schema && schema.caster) {
1750    schema = schema.caster;
1751  }
1752
1753  // model name for the populate query
1754  var modelName = options.model && options.model.modelName
1755               || options.model                // query options
1756               || schema && schema.options.ref // declared in schema
1757               || model.modelName              // an ad-hoc structure
1758
1759  var Model = model.db.model(modelName);
1760
1761  // expose the model used
1762  options.model = Model;
1763
1764  // normalize single / multiple docs passed
1765  if (!Array.isArray(docs)) {
1766    docs = [docs];
1767  }
1768
1769  if (0 === docs.length || docs.every(utils.isNullOrUndefined)) {
1770    return cb();
1771  }
1772
1773  populateDocs(docs, options, cb);
1774}
1775
1776/*!
1777 * Populates `docs`
1778 */
1779
1780function populateDocs (docs, options, cb) {
1781  var select = options.select;
1782  var match = options.match;
1783  var path = options.path;
1784  var Model = options.model;
1785
1786  var rawIds = [];
1787  var i, doc, id;
1788  var len = docs.length;
1789  var found = 0;
1790  var isDocument;
1791  var subpath;
1792  var ret;
1793
1794  for (i = 0; i < len; i++) {
1795    ret = undefined;
1796    doc = docs[i];
1797    id = String(utils.getValue("_id", doc));
1798    isDocument = !! doc.$__;
1799
1800    if (isDocument && !doc.isModified(path)) {
1801      // it is possible a previously populated path is being
1802      // populated again. Because users can specify matcher
1803      // clauses in their populate arguments we use the cached
1804      // _ids from the original populate call to ensure all _ids
1805      // are looked up, but only if the path wasn't modified which
1806      // signifies the users intent of the state of the path.
1807      ret = doc.populated(path);
1808    }
1809
1810    if (!ret || Array.isArray(ret) && 0 === ret.length) {
1811      ret = utils.getValue(path, doc);
1812    }
1813
1814    if (ret) {
1815      ret = convertTo_id(ret);
1816
1817      // previously we always assigned this even if the document had no _id
1818      options._docs[id] = Array.isArray(ret)
1819        ? ret.slice()
1820        : ret;
1821    }
1822
1823    // always retain original values, even empty values. these are
1824    // used to map the query results back to the correct position.
1825    rawIds.push(ret);
1826
1827    if (isDocument) {
1828      // cache original populated _ids and model used
1829      doc.populated(path, options._docs[id], options);
1830    }
1831  }
1832
1833  var ids = utils.array.flatten(rawIds, function (item) {
1834    // no need to include undefined values in our query
1835    return undefined !== item;
1836  });
1837
1838  if (0 === ids.length || ids.every(utils.isNullOrUndefined)) {
1839    return cb();
1840  }
1841
1842  // preserve original match conditions by copying
1843  if (match) {
1844    match = utils.object.shallowCopy(match);
1845  } else {
1846    match = {};
1847  }
1848
1849  match._id || (match._id = { $in: ids });
1850
1851  var assignmentOpts = {};
1852  assignmentOpts.sort = options.options && options.options.sort || undefined;
1853  assignmentOpts.excludeId = /\s?-_id\s?/.test(select) || (select && 0 === select._id);
1854
1855  if (assignmentOpts.excludeId) {
1856    // override the exclusion from the query so we can use the _id
1857    // for document matching during assignment. we'll delete the
1858    // _id back off before returning the result.
1859    if ('string' == typeof select) {
1860      select = select.replace(/\s?-_id\s?/g, ' ');
1861    } else {
1862      // preserve original select conditions by copying
1863      select = utils.object.shallowCopy(select);
1864      delete select._id;
1865    }
1866  }
1867
1868  // if a limit option is passed, we should have the limit apply to *each*
1869  // document, not apply in the aggregate
1870  if (options.options && options.options.limit) {
1871    options.options.limit = options.options.limit * len;
1872  }
1873
1874  Model.find(match, select, options.options, function (err, vals) {
1875    if (err) return cb(err);
1876
1877    var lean = options.options && options.options.lean;
1878    var len = vals.length;
1879    var rawOrder = {};
1880    var rawDocs = {}
1881    var key;
1882    var val;
1883
1884    // optimization:
1885    // record the document positions as returned by
1886    // the query result.
1887    for (var i = 0; i < len; i++) {
1888      val = vals[i];
1889      key = String(utils.getValue('_id', val));
1890      rawDocs[key] = val;
1891      rawOrder[key] = i;
1892
1893      // flag each as result of population
1894      if (!lean) val.$__.wasPopulated = true;
1895    }
1896
1897    assignVals({
1898      rawIds: rawIds,
1899      rawDocs: rawDocs,
1900      rawOrder: rawOrder,
1901      docs: docs,
1902      path: path,
1903      options: assignmentOpts
1904    });
1905
1906    cb();
1907  });
1908}
1909
1910/*!
1911 * Retrieve the _id of `val` if a Document or Array of Documents.
1912 *
1913 * @param {Array|Document|Any} val
1914 * @return {Array|Document|Any}
1915 */
1916
1917function convertTo_id (val) {
1918  if (val instanceof Model) return val._id;
1919
1920  if (Array.isArray(val)) {
1921    for (var i = 0; i < val.length; ++i) {
1922      if (val[i] instanceof Model) {
1923        val[i] = val[i]._id;
1924      }
1925    }
1926    return val;
1927  }
1928
1929  return val;
1930}
1931
1932/*!
1933 * Assigns documents returned from a population query back
1934 * to the original document path.
1935 */
1936
1937function assignVals (o) {
1938  // replace the original ids in our intermediate _ids structure
1939  // with the documents found by query
1940
1941  assignRawDocsToIdStructure(o.rawIds, o.rawDocs, o.rawOrder, o.options);
1942
1943  // now update the original documents being populated using the
1944  // result structure that contains real documents.
1945
1946  var docs = o.docs;
1947  var path = o.path;
1948  var rawIds = o.rawIds;
1949  var options = o.options;
1950
1951  for (var i = 0; i < docs.length; ++i) {
1952    utils.setValue(path, rawIds[i], docs[i], function (val) {
1953      return valueFilter(val, options);
1954    });
1955  }
1956}
1957
1958/*!
1959 * 1) Apply backwards compatible find/findOne behavior to sub documents
1960 *
1961 *    find logic:
1962 *      a) filter out non-documents
1963 *      b) remove _id from sub docs when user specified
1964 *
1965 *    findOne
1966 *      a) if no doc found, set to null
1967 *      b) remove _id from sub docs when user specified
1968 *
1969 * 2) Remove _ids when specified by users query.
1970 *
1971 * background:
1972 * _ids are left in the query even when user excludes them so
1973 * that population mapping can occur.
1974 */
1975
1976function valueFilter (val, assignmentOpts) {
1977  if (Array.isArray(val)) {
1978    // find logic
1979    var ret = [];
1980    for (var i = 0; i < val.length; ++i) {
1981      var subdoc = val[i];
1982      if (!isDoc(subdoc)) continue;
1983      maybeRemoveId(subdoc, assignmentOpts);
1984      ret.push(subdoc);
1985    }
1986    return ret;
1987  }
1988
1989  // findOne
1990  if (isDoc(val)) {
1991    maybeRemoveId(val, assignmentOpts);
1992    return val;
1993  }
1994
1995  return null;
1996}
1997
1998/*!
1999 * Remove _id from `subdoc` if user specified "lean" query option
2000 */
2001
2002function maybeRemoveId (subdoc, assignmentOpts) {
2003  if (assignmentOpts.excludeId) {
2004    if ('function' == typeof subdoc.setValue) {
2005      subdoc.setValue('_id', undefined);
2006    } else {
2007      delete subdoc._id;
2008    }
2009  }
2010}
2011
2012/*!
2013 * Determine if `doc` is a document returned
2014 * by a populate query.
2015 */
2016
2017function isDoc (doc) {
2018  if (null == doc)
2019    return false;
2020
2021  var type = typeof doc;
2022  if ('string' == type)
2023    return false;
2024
2025  if ('number' == type)
2026    return false;
2027
2028  if (Buffer.isBuffer(doc))
2029    return false;
2030
2031  if ('ObjectID' == doc.constructor.name)
2032    return false;
2033
2034  // only docs
2035  return true;
2036}
2037
2038/*!
2039 * Assign `vals` returned by mongo query to the `rawIds`
2040 * structure returned from utils.getVals() honoring
2041 * query sort order if specified by user.
2042 *
2043 * This can be optimized.
2044 *
2045 * Rules:
2046 *
2047 *   if the value of the path is not an array, use findOne rules, else find.
2048 *   for findOne the results are assigned directly to doc path (including null results).
2049 *   for find, if user specified sort order, results are assigned directly
2050 *   else documents are put back in original order of array if found in results
2051 *
2052 * @param {Array} rawIds
2053 * @param {Array} vals
2054 * @param {Boolean} sort
2055 * @api private
2056 */
2057
2058function assignRawDocsToIdStructure (rawIds, resultDocs, resultOrder, options, recursed) {
2059  // honor user specified sort order
2060  var newOrder = [];
2061  var sorting = options.sort && rawIds.length > 1;
2062  var found;
2063  var doc;
2064  var sid;
2065  var id;
2066
2067  for (var i = 0; i < rawIds.length; ++i) {
2068    id = rawIds[i];
2069
2070    if (Array.isArray(id)) {
2071      // handle [ [id0, id2], [id3] ]
2072      assignRawDocsToIdStructure(id, resultDocs, resultOrder, options, true);
2073      newOrder.push(id);
2074      continue;
2075    }
2076
2077    if (null === id && !sorting) {
2078      // keep nulls for findOne unless sorting, which always
2079      // removes them (backward compat)
2080      newOrder.push(id);
2081      continue;
2082    }
2083
2084    sid = String(id);
2085    found = false;
2086
2087    if (recursed) {
2088      // apply find behavior
2089
2090      // assign matching documents in original order unless sorting
2091      doc = resultDocs[sid];
2092      if (doc) {
2093        if (sorting) {
2094          newOrder[resultOrder[sid]] = doc;
2095        } else {
2096          newOrder.push(doc);
2097        }
2098      } else {
2099        newOrder.push(id);
2100      }
2101    } else {
2102      // apply findOne behavior - if document in results, assign, else assign null
2103      newOrder[i] = doc = resultDocs[sid] || null;
2104    }
2105  }
2106
2107  rawIds.length = 0;
2108  if (newOrder.length) {
2109    // reassign the documents based on corrected order
2110
2111    // forEach skips over sparse entries in arrays so we
2112    // can safely use this to our advantage dealing with sorted
2113    // result sets too.
2114    newOrder.forEach(function (doc, i) {
2115      rawIds[i] = doc;
2116    });
2117  }
2118}
2119
2120/**
2121 * Finds the schema for `path`. This is different than
2122 * calling `schema.path` as it also resolves paths with
2123 * positional selectors (something.$.another.$.path).
2124 *
2125 * @param {String} path
2126 * @return {Schema}
2127 * @api private
2128 */
2129
2130Model._getSchema = function _getSchema (path) {
2131  var schema = this.schema
2132    , pathschema = schema.path(path);
2133
2134  if (pathschema)
2135    return pathschema;
2136
2137  // look for arrays
2138  return (function search (parts, schema) {
2139    var p = parts.length + 1
2140      , foundschema
2141      , trypath
2142
2143    while (p--) {
2144      trypath = parts.slice(0, p).join('.');
2145      foundschema = schema.path(trypath);
2146      if (foundschema) {
2147        if (foundschema.caster) {
2148
2149          // array of Mixed?
2150          if (foundschema.caster instanceof Types.Mixed) {
2151            return foundschema.caster;
2152          }
2153
2154          // Now that we found the array, we need to check if there
2155          // are remaining document paths to look up for casting.
2156          // Also we need to handle array.$.path since schema.path
2157          // doesn't work for that.
2158          // If there is no foundschema.schema we are dealing with
2159          // a path like array.$
2160          if (p !== parts.length && foundschema.schema) {
2161            if ('$' === parts[p]) {
2162              // comments.$.comments.$.title
2163              return search(parts.slice(p+1), foundschema.schema);
2164            } else {
2165              // this is the last path of the selector
2166              return search(parts.slice(p), foundschema.schema);
2167            }
2168          }
2169        }
2170        return foundschema;
2171      }
2172    }
2173  })(path.split('.'), schema)
2174}
2175
2176/*!
2177 * Compiler utility.
2178 *
2179 * @param {String} name model name
2180 * @param {Schema} schema
2181 * @param {String} collectionName
2182 * @param {Connection} connection
2183 * @param {Mongoose} base mongoose instance
2184 */
2185
2186Model.compile = function compile (name, schema, collectionName, connection, base) {
2187  var versioningEnabled = false !== schema.options.versionKey;
2188
2189  if (versioningEnabled && !schema.paths[schema.options.versionKey]) {
2190    // add versioning to top level documents only
2191    var o = {};
2192    o[schema.options.versionKey] = Number;
2193    schema.add(o);
2194  }
2195
2196  // generate new class
2197  function model (doc, fields, skipId) {
2198    if (!(this instanceof model))
2199      return new model(doc, fields, skipId);
2200    Model.call(this, doc, fields, skipId);
2201  };
2202
2203  model.base = base;
2204  model.modelName = name;
2205  model.__proto__ = Model;
2206  model.prototype.__proto__ = Model.prototype;
2207  model.model = Model.prototype.model;
2208  model.db = model.prototype.db = connection;
2209
2210  model.prototype.$__setSchema(schema);
2211
2212  var collectionOptions = {
2213      bufferCommands: schema.options.bufferCommands
2214    , capped: schema.options.capped
2215  };
2216
2217  model.prototype.collection = connection.collection(
2218      collectionName
2219    , collectionOptions
2220  );
2221
2222  // apply methods
2223  for (var i in schema.methods)
2224    model.prototype[i] = schema.methods[i];
2225
2226  // apply statics
2227  for (var i in schema.statics)
2228    model[i] = schema.statics[i];
2229
2230  // apply named scopes
2231  if (schema.namedScopes) schema.namedScopes.compile(model);
2232
2233  model.schema = model.prototype.schema;
2234  model.options = model.prototype.options;
2235  model.collection = model.prototype.collection;
2236
2237  return model;
2238};
2239
2240/*!
2241 * Subclass this model with `conn`, `schema`, and `collection` settings.
2242 *
2243 * @param {Connection} conn
2244 * @param {Schema} [schema]
2245 * @param {String} [collection]
2246 * @return {Model}
2247 */
2248
2249Model.__subclass = function subclass (conn, schema, collection) {
2250  // subclass model using this connection and collection name
2251  var model = this;
2252
2253  var Model = function Model (doc, fields, skipId) {
2254    if (!(this instanceof Model)) {
2255      return new Model(doc, fields, skipId);
2256    }
2257    model.call(this, doc, fields, skipId);
2258  }
2259
2260  Model.__proto__ = model;
2261  Model.prototype.__proto__ = model.prototype;
2262  Model.db = Model.prototype.db = conn;
2263
2264  var s = schema && 'string' != typeof schema
2265    ? schema
2266    : model.prototype.schema;
2267
2268  if (!collection) {
2269    collection = model.prototype.schema.get('collection')
2270              || utils.toCollectionName(model.modelName);
2271  }
2272
2273  var collectionOptions = {
2274      bufferCommands: s ? s.options.bufferCommands : true
2275    , capped: s && s.options.capped
2276  };
2277
2278  Model.prototype.collection = conn.collection(collection, collectionOptions);
2279  Model.collection = Model.prototype.collection;
2280  Model.init();
2281  return Model;
2282}
2283
2284/*!
2285 * Module exports.
2286 */
2287
2288module.exports = exports = Model;