arnaucube
arnaucube/thoughtsPublic Self-hosted repository
thoughts/node_modules/mongoose/lib/index.js
node_modules/mongoose/lib/index.jsRaw
  1'use strict';
  2
  3/*!
  4 * Module dependencies.
  5 */
  6
  7var Schema = require('./schema'),
  8    SchemaType = require('./schematype'),
  9    VirtualType = require('./virtualtype'),
 10    STATES = require('./connectionstate'),
 11    Types = require('./types'),
 12    Query = require('./query'),
 13    Model = require('./model'),
 14    Document = require('./document'),
 15    utils = require('./utils'),
 16    format = utils.toCollectionName,
 17    pkg = require('../package.json');
 18
 19var querystring = require('querystring');
 20
 21var Aggregate = require('./aggregate');
 22var PromiseProvider = require('./promise_provider');
 23
 24/**
 25 * Mongoose constructor.
 26 *
 27 * The exports object of the `mongoose` module is an instance of this class.
 28 * Most apps will only use this one instance.
 29 *
 30 * @api public
 31 */
 32
 33function Mongoose() {
 34  this.connections = [];
 35  this.plugins = [];
 36  this.models = {};
 37  this.modelSchemas = {};
 38  // default global options
 39  this.options = {
 40    pluralization: true
 41  };
 42  var conn = this.createConnection(); // default connection
 43  conn.models = this.models;
 44}
 45
 46/**
 47 * Expose connection states for user-land
 48 *
 49 */
 50Mongoose.prototype.STATES = STATES;
 51
 52/**
 53 * Sets mongoose options
 54 *
 55 * ####Example:
 56 *
 57 *     mongoose.set('test', value) // sets the 'test' option to `value`
 58 *
 59 *     mongoose.set('debug', true) // enable logging collection methods + arguments to the console
 60 *
 61 *     mongoose.set('debug', function(collectionName, methodName, arg1, arg2...) {}); // use custom function to log collection methods + arguments
 62 *
 63 * @param {String} key
 64 * @param {String|Function} value
 65 * @api public
 66 */
 67
 68Mongoose.prototype.set = function(key, value) {
 69  if (arguments.length === 1) {
 70    return this.options[key];
 71  }
 72
 73  this.options[key] = value;
 74  return this;
 75};
 76Mongoose.prototype.set.$hasSideEffects = true;
 77
 78/**
 79 * Gets mongoose options
 80 *
 81 * ####Example:
 82 *
 83 *     mongoose.get('test') // returns the 'test' value
 84 *
 85 * @param {String} key
 86 * @method get
 87 * @api public
 88 */
 89
 90Mongoose.prototype.get = Mongoose.prototype.set;
 91
 92/*!
 93 * ReplSet connection string check.
 94 */
 95
 96var rgxReplSet = /^.+,.+$/;
 97
 98/**
 99 * Checks if ?replicaSet query parameter is specified in URI
100 *
101 * ####Example:
102 *
103 *     checkReplicaSetInUri('localhost:27000?replicaSet=rs0'); // true
104 *
105 * @param {String} uri
106 * @return {boolean}
107 * @api private
108 */
109
110var checkReplicaSetInUri = function(uri) {
111  if (!uri) {
112    return false;
113  }
114
115  var queryStringStart = uri.indexOf('?');
116  var isReplicaSet = false;
117  if (queryStringStart !== -1) {
118    try {
119      var obj = querystring.parse(uri.substr(queryStringStart + 1));
120      if (obj && obj.replicaSet) {
121        isReplicaSet = true;
122      }
123    } catch (e) {
124      return false;
125    }
126  }
127
128  return isReplicaSet;
129};
130
131/**
132 * Creates a Connection instance.
133 *
134 * Each `connection` instance maps to a single database. This method is helpful when mangaging multiple db connections.
135 *
136 * If arguments are passed, they are proxied to either [Connection#open](#connection_Connection-open) or [Connection#openSet](#connection_Connection-openSet) appropriately. This means we can pass `db`, `server`, and `replset` options to the driver. _Note that the `safe` option specified in your schema will overwrite the `safe` db option specified here unless you set your schemas `safe` option to `undefined`. See [this](/docs/guide.html#safe) for more information._
137 *
138 * _Options passed take precedence over options included in connection strings._
139 *
140 * ####Example:
141 *
142 *     // with mongodb:// URI
143 *     db = mongoose.createConnection('mongodb://user:pass@localhost:port/database');
144 *
145 *     // and options
146 *     var opts = { db: { native_parser: true }}
147 *     db = mongoose.createConnection('mongodb://user:pass@localhost:port/database', opts);
148 *
149 *     // replica sets
150 *     db = mongoose.createConnection('mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/database');
151 *
152 *     // and options
153 *     var opts = { replset: { strategy: 'ping', rs_name: 'testSet' }}
154 *     db = mongoose.createConnection('mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/database', opts);
155 *
156 *     // with [host, database_name[, port] signature
157 *     db = mongoose.createConnection('localhost', 'database', port)
158 *
159 *     // and options
160 *     var opts = { server: { auto_reconnect: false }, user: 'username', pass: 'mypassword' }
161 *     db = mongoose.createConnection('localhost', 'database', port, opts)
162 *
163 *     // initialize now, connect later
164 *     db = mongoose.createConnection();
165 *     db.open('localhost', 'database', port, [opts]);
166 *
167 * @param {String} [uri] a mongodb:// URI
168 * @param {Object} [options] options to pass to the driver
169 * @param {Object} [options.config] mongoose-specific options
170 * @param {Boolean} [options.config.autoIndex] set to false to disable automatic index creation for all models associated with this connection.
171 * @see Connection#open #connection_Connection-open
172 * @see Connection#openSet #connection_Connection-openSet
173 * @return {Connection} the created Connection object
174 * @api public
175 */
176
177Mongoose.prototype.createConnection = function(uri, options) {
178  var conn = new Connection(this);
179  this.connections.push(conn);
180
181  if (arguments.length) {
182    if (rgxReplSet.test(arguments[0]) || checkReplicaSetInUri(arguments[0])) {
183      conn.openSet.apply(conn, arguments);
184    } else if (options && options.replset &&
185        (options.replset.replicaSet || options.replset.rs_name)) {
186      conn.openSet.apply(conn, arguments);
187    } else {
188      conn.open.apply(conn, arguments);
189    }
190  }
191
192  return conn;
193};
194Mongoose.prototype.createConnection.$hasSideEffects = true;
195
196/**
197 * Opens the default mongoose connection.
198 *
199 * If arguments are passed, they are proxied to either
200 * [Connection#open](#connection_Connection-open) or
201 * [Connection#openSet](#connection_Connection-openSet) appropriately.
202 *
203 * _Options passed take precedence over options included in connection strings._
204 *
205 * ####Example:
206 *
207 *     mongoose.connect('mongodb://user:pass@localhost:port/database');
208 *
209 *     // replica sets
210 *     var uri = 'mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/mydatabase';
211 *     mongoose.connect(uri);
212 *
213 *     // with options
214 *     mongoose.connect(uri, options);
215 *
216 *     // connecting to multiple mongos
217 *     var uri = 'mongodb://hostA:27501,hostB:27501';
218 *     var opts = { mongos: true };
219 *     mongoose.connect(uri, opts);
220 *
221 *     // optional callback that gets fired when initial connection completed
222 *     var uri = 'mongodb://nonexistent.domain:27000';
223 *     mongoose.connect(uri, function(error) {
224 *       // if error is truthy, the initial connection failed.
225 *     })
226 *
227 * @param {String} uri(s)
228 * @param {Object} [options]
229 * @param {Function} [callback]
230 * @see Mongoose#createConnection #index_Mongoose-createConnection
231 * @api public
232 * @return {MongooseThenable} pseudo-promise wrapper around this
233 */
234
235Mongoose.prototype.connect = function() {
236  var conn = this.connection;
237  if (rgxReplSet.test(arguments[0]) || checkReplicaSetInUri(arguments[0])) {
238    return new MongooseThenable(this, conn.openSet.apply(conn, arguments));
239  }
240
241  return new MongooseThenable(this, conn.open.apply(conn, arguments));
242};
243Mongoose.prototype.connect.$hasSideEffects = true;
244
245/**
246 * Disconnects all connections.
247 *
248 * @param {Function} [fn] called after all connection close.
249 * @return {MongooseThenable} pseudo-promise wrapper around this
250 * @api public
251 */
252
253Mongoose.prototype.disconnect = function(fn) {
254  var error;
255  this.connections.forEach(function(conn) {
256    conn.close(function(err) {
257      if (error) {
258        return;
259      }
260      if (err) {
261        error = err;
262      }
263    });
264  });
265
266  var Promise = PromiseProvider.get();
267  return new MongooseThenable(this, new Promise.ES6(function(resolve, reject) {
268    fn && fn(error);
269    if (error) {
270      reject(error);
271      return;
272    }
273    resolve();
274  }));
275};
276Mongoose.prototype.disconnect.$hasSideEffects = true;
277
278/**
279 * Defines a model or retrieves it.
280 *
281 * Models defined on the `mongoose` instance are available to all connection created by the same `mongoose` instance.
282 *
283 * ####Example:
284 *
285 *     var mongoose = require('mongoose');
286 *
287 *     // define an Actor model with this mongoose instance
288 *     mongoose.model('Actor', new Schema({ name: String }));
289 *
290 *     // create a new connection
291 *     var conn = mongoose.createConnection(..);
292 *
293 *     // retrieve the Actor model
294 *     var Actor = conn.model('Actor');
295 *
296 * _When no `collection` argument is passed, Mongoose produces a collection name by passing the model `name` to the [utils.toCollectionName](#utils_exports.toCollectionName) method. This method pluralizes the name. If you don't like this behavior, either pass a collection name or set your schemas collection name option._
297 *
298 * ####Example:
299 *
300 *     var schema = new Schema({ name: String }, { collection: 'actor' });
301 *
302 *     // or
303 *
304 *     schema.set('collection', 'actor');
305 *
306 *     // or
307 *
308 *     var collectionName = 'actor'
309 *     var M = mongoose.model('Actor', schema, collectionName)
310 *
311 * @param {String} name model name
312 * @param {Schema} [schema]
313 * @param {String} [collection] name (optional, induced from model name)
314 * @param {Boolean} [skipInit] whether to skip initialization (defaults to false)
315 * @api public
316 */
317
318Mongoose.prototype.model = function(name, schema, collection, skipInit) {
319  if (typeof schema === 'string') {
320    collection = schema;
321    schema = false;
322  }
323
324  if (utils.isObject(schema) && !(schema.instanceOfSchema)) {
325    schema = new Schema(schema);
326  }
327
328  if (typeof collection === 'boolean') {
329    skipInit = collection;
330    collection = null;
331  }
332
333  // handle internal options from connection.model()
334  var options;
335  if (skipInit && utils.isObject(skipInit)) {
336    options = skipInit;
337    skipInit = true;
338  } else {
339    options = {};
340  }
341
342  // look up schema for the collection.
343  if (!this.modelSchemas[name]) {
344    if (schema) {
345      // cache it so we only apply plugins once
346      this.modelSchemas[name] = schema;
347      this._applyPlugins(schema);
348    } else {
349      throw new mongoose.Error.MissingSchemaError(name);
350    }
351  }
352
353  var model;
354  var sub;
355
356  // connection.model() may be passing a different schema for
357  // an existing model name. in this case don't read from cache.
358  if (this.models[name] && options.cache !== false) {
359    if (schema && schema.instanceOfSchema && schema !== this.models[name].schema) {
360      throw new mongoose.Error.OverwriteModelError(name);
361    }
362
363    if (collection) {
364      // subclass current model with alternate collection
365      model = this.models[name];
366      schema = model.prototype.schema;
367      sub = model.__subclass(this.connection, schema, collection);
368      // do not cache the sub model
369      return sub;
370    }
371
372    return this.models[name];
373  }
374
375  // ensure a schema exists
376  if (!schema) {
377    schema = this.modelSchemas[name];
378    if (!schema) {
379      throw new mongoose.Error.MissingSchemaError(name);
380    }
381  }
382
383  // Apply relevant "global" options to the schema
384  if (!('pluralization' in schema.options)) schema.options.pluralization = this.options.pluralization;
385
386
387  if (!collection) {
388    collection = schema.get('collection') || format(name, schema.options);
389  }
390
391  var connection = options.connection || this.connection;
392  model = Model.compile(name, schema, collection, connection, this);
393
394  if (!skipInit) {
395    model.init();
396  }
397
398  if (options.cache === false) {
399    return model;
400  }
401
402  this.models[name] = model;
403  return this.models[name];
404};
405Mongoose.prototype.model.$hasSideEffects = true;
406
407/**
408 * Returns an array of model names created on this instance of Mongoose.
409 *
410 * ####Note:
411 *
412 * _Does not include names of models created using `connection.model()`._
413 *
414 * @api public
415 * @return {Array}
416 */
417
418Mongoose.prototype.modelNames = function() {
419  var names = Object.keys(this.models);
420  return names;
421};
422Mongoose.prototype.modelNames.$hasSideEffects = true;
423
424/**
425 * Applies global plugins to `schema`.
426 *
427 * @param {Schema} schema
428 * @api private
429 */
430
431Mongoose.prototype._applyPlugins = function(schema) {
432  if (schema.$globalPluginsApplied) {
433    return;
434  }
435  var i;
436  var len;
437  for (i = 0, len = this.plugins.length; i < len; ++i) {
438    schema.plugin(this.plugins[i][0], this.plugins[i][1]);
439  }
440  schema.$globalPluginsApplied = true;
441  for (i = 0, len = schema.childSchemas.length; i < len; ++i) {
442    this._applyPlugins(schema.childSchemas[i]);
443  }
444};
445Mongoose.prototype._applyPlugins.$hasSideEffects = true;
446
447/**
448 * Declares a global plugin executed on all Schemas.
449 *
450 * Equivalent to calling `.plugin(fn)` on each Schema you create.
451 *
452 * @param {Function} fn plugin callback
453 * @param {Object} [opts] optional options
454 * @return {Mongoose} this
455 * @see plugins ./plugins.html
456 * @api public
457 */
458
459Mongoose.prototype.plugin = function(fn, opts) {
460  this.plugins.push([fn, opts]);
461  return this;
462};
463Mongoose.prototype.plugin.$hasSideEffects = true;
464
465/**
466 * The default connection of the mongoose module.
467 *
468 * ####Example:
469 *
470 *     var mongoose = require('mongoose');
471 *     mongoose.connect(...);
472 *     mongoose.connection.on('error', cb);
473 *
474 * This is the connection used by default for every model created using [mongoose.model](#index_Mongoose-model).
475 *
476 * @property connection
477 * @return {Connection}
478 * @api public
479 */
480
481Mongoose.prototype.__defineGetter__('connection', function() {
482  return this.connections[0];
483});
484
485Mongoose.prototype.__defineSetter__('connection', function(v) {
486  this.connections[0] = v;
487});
488
489/*!
490 * Driver depentend APIs
491 */
492
493var driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native';
494
495/*!
496 * Connection
497 */
498
499var Connection = require(driver + '/connection');
500
501/*!
502 * Collection
503 */
504
505var Collection = require(driver + '/collection');
506
507/**
508 * The Mongoose Aggregate constructor
509 *
510 * @method Aggregate
511 * @api public
512 */
513
514Mongoose.prototype.Aggregate = Aggregate;
515
516/**
517 * The Mongoose Collection constructor
518 *
519 * @method Collection
520 * @api public
521 */
522
523Mongoose.prototype.Collection = Collection;
524
525/**
526 * The Mongoose [Connection](#connection_Connection) constructor
527 *
528 * @method Connection
529 * @api public
530 */
531
532Mongoose.prototype.Connection = Connection;
533
534/**
535 * The Mongoose version
536 *
537 * @property version
538 * @api public
539 */
540
541Mongoose.prototype.version = pkg.version;
542
543/**
544 * The Mongoose constructor
545 *
546 * The exports of the mongoose module is an instance of this class.
547 *
548 * ####Example:
549 *
550 *     var mongoose = require('mongoose');
551 *     var mongoose2 = new mongoose.Mongoose();
552 *
553 * @method Mongoose
554 * @api public
555 */
556
557Mongoose.prototype.Mongoose = Mongoose;
558
559/**
560 * The Mongoose [Schema](#schema_Schema) constructor
561 *
562 * ####Example:
563 *
564 *     var mongoose = require('mongoose');
565 *     var Schema = mongoose.Schema;
566 *     var CatSchema = new Schema(..);
567 *
568 * @method Schema
569 * @api public
570 */
571
572Mongoose.prototype.Schema = Schema;
573
574/**
575 * The Mongoose [SchemaType](#schematype_SchemaType) constructor
576 *
577 * @method SchemaType
578 * @api public
579 */
580
581Mongoose.prototype.SchemaType = SchemaType;
582
583/**
584 * The various Mongoose SchemaTypes.
585 *
586 * ####Note:
587 *
588 * _Alias of mongoose.Schema.Types for backwards compatibility._
589 *
590 * @property SchemaTypes
591 * @see Schema.SchemaTypes #schema_Schema.Types
592 * @api public
593 */
594
595Mongoose.prototype.SchemaTypes = Schema.Types;
596
597/**
598 * The Mongoose [VirtualType](#virtualtype_VirtualType) constructor
599 *
600 * @method VirtualType
601 * @api public
602 */
603
604Mongoose.prototype.VirtualType = VirtualType;
605
606/**
607 * The various Mongoose Types.
608 *
609 * ####Example:
610 *
611 *     var mongoose = require('mongoose');
612 *     var array = mongoose.Types.Array;
613 *
614 * ####Types:
615 *
616 * - [ObjectId](#types-objectid-js)
617 * - [Buffer](#types-buffer-js)
618 * - [SubDocument](#types-embedded-js)
619 * - [Array](#types-array-js)
620 * - [DocumentArray](#types-documentarray-js)
621 *
622 * Using this exposed access to the `ObjectId` type, we can construct ids on demand.
623 *
624 *     var ObjectId = mongoose.Types.ObjectId;
625 *     var id1 = new ObjectId;
626 *
627 * @property Types
628 * @api public
629 */
630
631Mongoose.prototype.Types = Types;
632
633/**
634 * The Mongoose [Query](#query_Query) constructor.
635 *
636 * @method Query
637 * @api public
638 */
639
640Mongoose.prototype.Query = Query;
641
642/**
643 * The Mongoose [Promise](#promise_Promise) constructor.
644 *
645 * @method Promise
646 * @api public
647 */
648
649Object.defineProperty(Mongoose.prototype, 'Promise', {
650  get: function() {
651    return PromiseProvider.get();
652  },
653  set: function(lib) {
654    PromiseProvider.set(lib);
655  }
656});
657
658/**
659 * Storage layer for mongoose promises
660 *
661 * @method PromiseProvider
662 * @api public
663 */
664
665Mongoose.prototype.PromiseProvider = PromiseProvider;
666
667/**
668 * The Mongoose [Model](#model_Model) constructor.
669 *
670 * @method Model
671 * @api public
672 */
673
674Mongoose.prototype.Model = Model;
675
676/**
677 * The Mongoose [Document](#document-js) constructor.
678 *
679 * @method Document
680 * @api public
681 */
682
683Mongoose.prototype.Document = Document;
684
685/**
686 * The Mongoose DocumentProvider constructor.
687 *
688 * @method DocumentProvider
689 * @api public
690 */
691
692Mongoose.prototype.DocumentProvider = require('./document_provider');
693
694/**
695 * The [MongooseError](#error_MongooseError) constructor.
696 *
697 * @method Error
698 * @api public
699 */
700
701Mongoose.prototype.Error = require('./error');
702
703/**
704 * The Mongoose CastError constructor
705 *
706 * @method CastError
707 * @param {String} type The name of the type
708 * @param {Any} value The value that failed to cast
709 * @param {String} path The path `a.b.c` in the doc where this cast error occurred
710 * @param {Error} [reason] The original error that was thrown
711 * @api public
712 */
713
714Mongoose.prototype.CastError = require('./error/cast');
715
716/**
717 * The [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) driver Mongoose uses.
718 *
719 * @property mongo
720 * @api public
721 */
722
723Mongoose.prototype.mongo = require('mongodb');
724
725/**
726 * The [mquery](https://github.com/aheckmann/mquery) query builder Mongoose uses.
727 *
728 * @property mquery
729 * @api public
730 */
731
732Mongoose.prototype.mquery = require('mquery');
733
734/**
735 * Wraps the given Mongoose instance into a thenable (pseudo-promise). This
736 * is so `connect()` and `disconnect()` can return a thenable while maintaining
737 * backwards compatibility.
738 *
739 * @api private
740 */
741
742function MongooseThenable(mongoose, promise) {
743  var _this = this;
744  for (var key in mongoose) {
745    if (typeof mongoose[key] === 'function' && mongoose[key].$hasSideEffects) {
746      (function(key) {
747        _this[key] = function() {
748          return mongoose[key].apply(mongoose, arguments);
749        };
750      })(key);
751    } else if (['connection', 'connections'].indexOf(key) !== -1) {
752      _this[key] = mongoose[key];
753    }
754  }
755  this.$opPromise = promise;
756}
757
758MongooseThenable.prototype = new Mongoose;
759
760/**
761 * Ability to use mongoose object as a pseudo-promise so `.connect().then()`
762 * and `.disconnect().then()` are viable.
763 *
764 * @param {Function} onFulfilled
765 * @param {Function} onRejected
766 * @return {Promise}
767 * @api private
768 */
769
770MongooseThenable.prototype.then = function(onFulfilled, onRejected) {
771  var Promise = PromiseProvider.get();
772  if (!this.$opPromise) {
773    return new Promise.ES6(function(resolve, reject) {
774      reject(new Error('Can only call `.then()` if connect() or disconnect() ' +
775        'has been called'));
776    }).then(onFulfilled, onRejected);
777  }
778  this.$opPromise.$hasHandler = true;
779  return this.$opPromise.then(onFulfilled, onRejected);
780};
781
782/**
783 * Ability to use mongoose object as a pseudo-promise so `.connect().then()`
784 * and `.disconnect().then()` are viable.
785 *
786 * @param {Function} onFulfilled
787 * @param {Function} onRejected
788 * @return {Promise}
789 * @api private
790 */
791
792MongooseThenable.prototype.catch = function(onRejected) {
793  return this.then(null, onRejected);
794};
795
796/*!
797 * The exports object is an instance of Mongoose.
798 *
799 * @api public
800 */
801
802var mongoose = module.exports = exports = new Mongoose;