PageRenderTime 162ms CodeModel.GetById 77ms app.highlight 73ms RepoModel.GetById 2ms app.codeStats 0ms

/node_modules/mongoose/node_modules/mongodb/lib/db.js

https://bitbucket.org/coleman333/smartsite
JavaScript | 1809 lines | 1008 code | 217 blank | 584 comment | 296 complexity | 335236f3088dbb5debebe08971a07a1a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1'use strict';
   2
   3var EventEmitter = require('events').EventEmitter,
   4  inherits = require('util').inherits,
   5  getSingleProperty = require('./utils').getSingleProperty,
   6  shallowClone = require('./utils').shallowClone,
   7  parseIndexOptions = require('./utils').parseIndexOptions,
   8  debugOptions = require('./utils').debugOptions,
   9  CommandCursor = require('./command_cursor'),
  10  handleCallback = require('./utils').handleCallback,
  11  filterOptions = require('./utils').filterOptions,
  12  toError = require('./utils').toError,
  13  ReadPreference = require('mongodb-core').ReadPreference,
  14  f = require('util').format,
  15  Admin = require('./admin'),
  16  Code = require('mongodb-core').BSON.Code,
  17  MongoError = require('mongodb-core').MongoError,
  18  ObjectID = require('mongodb-core').ObjectID,
  19  Define = require('./metadata'),
  20  Logger = require('mongodb-core').Logger,
  21  Collection = require('./collection'),
  22  crypto = require('crypto'),
  23  mergeOptionsAndWriteConcern = require('./utils').mergeOptionsAndWriteConcern,
  24  executeOperation = require('./utils').executeOperation;
  25
  26var debugFields = [
  27  'authSource',
  28  'w',
  29  'wtimeout',
  30  'j',
  31  'native_parser',
  32  'forceServerObjectId',
  33  'serializeFunctions',
  34  'raw',
  35  'promoteLongs',
  36  'promoteValues',
  37  'promoteBuffers',
  38  'bufferMaxEntries',
  39  'numberOfRetries',
  40  'retryMiliSeconds',
  41  'readPreference',
  42  'pkFactory',
  43  'parentDb',
  44  'promiseLibrary',
  45  'noListener'
  46];
  47
  48// Filter out any write concern options
  49var illegalCommandFields = [
  50  'w',
  51  'wtimeout',
  52  'j',
  53  'fsync',
  54  'autoIndexId',
  55  'strict',
  56  'serializeFunctions',
  57  'pkFactory',
  58  'raw',
  59  'readPreference',
  60  'session'
  61];
  62
  63/**
  64 * @fileOverview The **Db** class is a class that represents a MongoDB Database.
  65 *
  66 * @example
  67 * const MongoClient = require('mongodb').MongoClient;
  68 * const test = require('assert');
  69 * // Connection url
  70 * const url = 'mongodb://localhost:27017';
  71 * // Database Name
  72 * const dbName = 'test';
  73 * // Connect using MongoClient
  74 * MongoClient.connect(url, function(err, client) {
  75 *   // Get an additional db
  76 *   const testDb = client.db('test');
  77 *   client.close();
  78 * });
  79 */
  80
  81// Allowed parameters
  82var legalOptionNames = [
  83  'w',
  84  'wtimeout',
  85  'fsync',
  86  'j',
  87  'readPreference',
  88  'readPreferenceTags',
  89  'native_parser',
  90  'forceServerObjectId',
  91  'pkFactory',
  92  'serializeFunctions',
  93  'raw',
  94  'bufferMaxEntries',
  95  'authSource',
  96  'ignoreUndefined',
  97  'promoteLongs',
  98  'promiseLibrary',
  99  'readConcern',
 100  'retryMiliSeconds',
 101  'numberOfRetries',
 102  'parentDb',
 103  'noListener',
 104  'loggerLevel',
 105  'logger',
 106  'promoteBuffers',
 107  'promoteLongs',
 108  'promoteValues',
 109  'compression',
 110  'retryWrites'
 111];
 112
 113/**
 114 * Creates a new Db instance
 115 * @class
 116 * @param {string} databaseName The name of the database this instance represents.
 117 * @param {(Server|ReplSet|Mongos)} topology The server topology for the database.
 118 * @param {object} [options=null] Optional settings.
 119 * @param {string} [options.authSource=null] If the database authentication is dependent on another databaseName.
 120 * @param {(number|string)} [options.w=null] The write concern.
 121 * @param {number} [options.wtimeout=null] The write concern timeout.
 122 * @param {boolean} [options.j=false] Specify a journal write concern.
 123 * @param {boolean} [options.forceServerObjectId=false] Force server to assign _id values instead of driver.
 124 * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
 125 * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
 126 * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
 127 * @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
 128 * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
 129 * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
 130 * @param {number} [options.bufferMaxEntries=-1] Sets a cap on how many operations the driver will buffer up before giving up on getting a working connection, default is -1 which is unlimited.
 131 * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
 132 * @param {object} [options.pkFactory=null] A primary key factory object for generation of custom _id keys.
 133 * @param {object} [options.promiseLibrary=null] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
 134 * @param {object} [options.readConcern=null] Specify a read concern for the collection. (only MongoDB 3.2 or higher supported)
 135 * @param {object} [options.readConcern.level='local'] Specify a read concern level for the collection operations, one of [local|majority]. (only MongoDB 3.2 or higher supported)
 136 * @property {(Server|ReplSet|Mongos)} serverConfig Get the current db topology.
 137 * @property {number} bufferMaxEntries Current bufferMaxEntries value for the database
 138 * @property {string} databaseName The name of the database this instance represents.
 139 * @property {object} options The options associated with the db instance.
 140 * @property {boolean} native_parser The current value of the parameter native_parser.
 141 * @property {boolean} slaveOk The current slaveOk value for the db instance.
 142 * @property {object} writeConcern The current write concern values.
 143 * @property {object} topology Access the topology object (single server, replicaset or mongos).
 144 * @fires Db#close
 145 * @fires Db#reconnect
 146 * @fires Db#error
 147 * @fires Db#timeout
 148 * @fires Db#parseError
 149 * @fires Db#fullsetup
 150 * @return {Db} a Db instance.
 151 */
 152var Db = function(databaseName, topology, options) {
 153  options = options || {};
 154  if (!(this instanceof Db)) return new Db(databaseName, topology, options);
 155  EventEmitter.call(this);
 156  var self = this;
 157
 158  // Get the promiseLibrary
 159  var promiseLibrary = options.promiseLibrary || Promise;
 160
 161  // Filter the options
 162  options = filterOptions(options, legalOptionNames);
 163
 164  // Ensure we put the promiseLib in the options
 165  options.promiseLibrary = promiseLibrary;
 166
 167  // var self = this;  // Internal state of the db object
 168  this.s = {
 169    // Database name
 170    databaseName: databaseName,
 171    // DbCache
 172    dbCache: {},
 173    // Children db's
 174    children: [],
 175    // Topology
 176    topology: topology,
 177    // Options
 178    options: options,
 179    // Logger instance
 180    logger: Logger('Db', options),
 181    // Get the bson parser
 182    bson: topology ? topology.bson : null,
 183    // Unpack read preference
 184    readPreference: options.readPreference,
 185    // Set buffermaxEntries
 186    bufferMaxEntries: typeof options.bufferMaxEntries === 'number' ? options.bufferMaxEntries : -1,
 187    // Parent db (if chained)
 188    parentDb: options.parentDb || null,
 189    // Set up the primary key factory or fallback to ObjectID
 190    pkFactory: options.pkFactory || ObjectID,
 191    // Get native parser
 192    nativeParser: options.nativeParser || options.native_parser,
 193    // Promise library
 194    promiseLibrary: promiseLibrary,
 195    // No listener
 196    noListener: typeof options.noListener === 'boolean' ? options.noListener : false,
 197    // ReadConcern
 198    readConcern: options.readConcern
 199  };
 200
 201  // Ensure we have a valid db name
 202  validateDatabaseName(self.s.databaseName);
 203
 204  // Add a read Only property
 205  getSingleProperty(this, 'serverConfig', self.s.topology);
 206  getSingleProperty(this, 'bufferMaxEntries', self.s.bufferMaxEntries);
 207  getSingleProperty(this, 'databaseName', self.s.databaseName);
 208
 209  // This is a child db, do not register any listeners
 210  if (options.parentDb) return;
 211  if (this.s.noListener) return;
 212
 213  // Add listeners
 214  topology.on('error', createListener(self, 'error', self));
 215  topology.on('timeout', createListener(self, 'timeout', self));
 216  topology.on('close', createListener(self, 'close', self));
 217  topology.on('parseError', createListener(self, 'parseError', self));
 218  topology.once('open', createListener(self, 'open', self));
 219  topology.once('fullsetup', createListener(self, 'fullsetup', self));
 220  topology.once('all', createListener(self, 'all', self));
 221  topology.on('reconnect', createListener(self, 'reconnect', self));
 222};
 223
 224inherits(Db, EventEmitter);
 225
 226var define = (Db.define = new Define('Db', Db, false));
 227
 228// Topology
 229Object.defineProperty(Db.prototype, 'topology', {
 230  enumerable: true,
 231  get: function() {
 232    return this.s.topology;
 233  }
 234});
 235
 236// Options
 237Object.defineProperty(Db.prototype, 'options', {
 238  enumerable: true,
 239  get: function() {
 240    return this.s.options;
 241  }
 242});
 243
 244// slaveOk specified
 245Object.defineProperty(Db.prototype, 'slaveOk', {
 246  enumerable: true,
 247  get: function() {
 248    if (
 249      this.s.options.readPreference != null &&
 250      (this.s.options.readPreference !== 'primary' ||
 251        this.s.options.readPreference.mode !== 'primary')
 252    ) {
 253      return true;
 254    }
 255    return false;
 256  }
 257});
 258
 259// get the write Concern
 260Object.defineProperty(Db.prototype, 'writeConcern', {
 261  enumerable: true,
 262  get: function() {
 263    var ops = {};
 264    if (this.s.options.w != null) ops.w = this.s.options.w;
 265    if (this.s.options.j != null) ops.j = this.s.options.j;
 266    if (this.s.options.fsync != null) ops.fsync = this.s.options.fsync;
 267    if (this.s.options.wtimeout != null) ops.wtimeout = this.s.options.wtimeout;
 268    return ops;
 269  }
 270});
 271
 272/**
 273 * Ensures provided read preference is properly converted into an object
 274 * @param {(ReadPreference|string|object)} readPreference the user provided read preference
 275 * @return {ReadPreference}
 276 */
 277const convertReadPreference = function(readPreference) {
 278  if (readPreference) {
 279    if (typeof readPreference === 'string') {
 280      return new ReadPreference(readPreference);
 281    } else if (
 282      readPreference &&
 283      !(readPreference instanceof ReadPreference) &&
 284      typeof readPreference === 'object'
 285    ) {
 286      const mode = readPreference.mode || readPreference.preference;
 287      if (mode && typeof mode === 'string') {
 288        return new ReadPreference(mode, readPreference.tags, {
 289          maxStalenessSeconds: readPreference.maxStalenessSeconds
 290        });
 291      }
 292    } else if (!(readPreference instanceof ReadPreference)) {
 293      throw new TypeError('Invalid read preference: ' + readPreference);
 294    }
 295  }
 296
 297  return readPreference;
 298};
 299
 300/**
 301 * The callback format for results
 302 * @callback Db~resultCallback
 303 * @param {MongoError} error An error instance representing the error during the execution.
 304 * @param {object} result The result object if the command was executed successfully.
 305 */
 306var executeCommand = function(self, command, options, callback) {
 307  // Did the user destroy the topology
 308  if (self.serverConfig && self.serverConfig.isDestroyed())
 309    return callback(new MongoError('topology was destroyed'));
 310  // Get the db name we are executing against
 311  var dbName = options.dbName || options.authdb || self.s.databaseName;
 312
 313  // If we have a readPreference set
 314  if (options.readPreference == null && self.s.readPreference) {
 315    options.readPreference = self.s.readPreference;
 316  }
 317
 318  // Convert the readPreference if its not a write
 319  if (options.readPreference) {
 320    options.readPreference = convertReadPreference(options.readPreference);
 321  } else {
 322    options.readPreference = ReadPreference.primary;
 323  }
 324
 325  // Debug information
 326  if (self.s.logger.isDebug())
 327    self.s.logger.debug(
 328      f(
 329        'executing command %s against %s with options [%s]',
 330        JSON.stringify(command),
 331        f('%s.$cmd', dbName),
 332        JSON.stringify(debugOptions(debugFields, options))
 333      )
 334    );
 335
 336  // Execute command
 337  self.s.topology.command(f('%s.$cmd', dbName), command, options, function(err, result) {
 338    if (err) return handleCallback(callback, err);
 339    if (options.full) return handleCallback(callback, null, result);
 340    handleCallback(callback, null, result.result);
 341  });
 342};
 343
 344/**
 345 * Execute a command
 346 * @method
 347 * @param {object} command The command hash
 348 * @param {object} [options=null] Optional settings.
 349 * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
 350 * @param {ClientSession} [options.session] optional session to use for this operation
 351 * @param {Db~resultCallback} [callback] The command result callback
 352 * @return {Promise} returns Promise if no callback passed
 353 */
 354Db.prototype.command = function(command, options, callback) {
 355  // Change the callback
 356  if (typeof options === 'function') (callback = options), (options = {});
 357  // Clone the options
 358  options = shallowClone(options);
 359
 360  return executeOperation(this.s.topology, executeCommand, [this, command, options, callback]);
 361};
 362
 363define.classMethod('command', { callback: true, promise: true });
 364
 365/**
 366 * Return the Admin db instance
 367 * @method
 368 * @return {Admin} return the new Admin db instance
 369 */
 370Db.prototype.admin = function() {
 371  return new Admin(this, this.s.topology, this.s.promiseLibrary);
 372};
 373
 374define.classMethod('admin', { callback: false, promise: false, returns: [Admin] });
 375
 376/**
 377 * The callback format for the collection method, must be used if strict is specified
 378 * @callback Db~collectionResultCallback
 379 * @param {MongoError} error An error instance representing the error during the execution.
 380 * @param {Collection} collection The collection instance.
 381 */
 382
 383var collectionKeys = [
 384  'pkFactory',
 385  'readPreference',
 386  'serializeFunctions',
 387  'strict',
 388  'readConcern',
 389  'ignoreUndefined',
 390  'promoteValues',
 391  'promoteBuffers',
 392  'promoteLongs'
 393];
 394
 395/**
 396 * Fetch a specific collection (containing the actual collection information). If the application does not use strict mode you
 397 * can use it without a callback in the following way: `var collection = db.collection('mycollection');`
 398 *
 399 * @method
 400 * @param {string} name the collection name we wish to access.
 401 * @param {object} [options=null] Optional settings.
 402 * @param {(number|string)} [options.w=null] The write concern.
 403 * @param {number} [options.wtimeout=null] The write concern timeout.
 404 * @param {boolean} [options.j=false] Specify a journal write concern.
 405 * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
 406 * @param {object} [options.pkFactory=null] A primary key factory object for generation of custom _id keys.
 407 * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
 408 * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
 409 * @param {boolean} [options.strict=false] Returns an error if the collection does not exist
 410 * @param {object} [options.readConcern=null] Specify a read concern for the collection. (only MongoDB 3.2 or higher supported)
 411 * @param {object} [options.readConcern.level='local'] Specify a read concern level for the collection operations, one of [local|majority]. (only MongoDB 3.2 or higher supported)
 412 * @param {Db~collectionResultCallback} [callback] The collection result callback
 413 * @return {Collection} return the new Collection instance if not in strict mode
 414 */
 415Db.prototype.collection = function(name, options, callback) {
 416  var self = this;
 417  if (typeof options === 'function') (callback = options), (options = {});
 418  options = options || {};
 419  options = shallowClone(options);
 420  // Set the promise library
 421  options.promiseLibrary = this.s.promiseLibrary;
 422
 423  // If we have not set a collection level readConcern set the db level one
 424  options.readConcern = options.readConcern || this.s.readConcern;
 425
 426  // Do we have ignoreUndefined set
 427  if (this.s.options.ignoreUndefined) {
 428    options.ignoreUndefined = this.s.options.ignoreUndefined;
 429  }
 430
 431  // Merge in all needed options and ensure correct writeConcern merging from db level
 432  options = mergeOptionsAndWriteConcern(options, this.s.options, collectionKeys, true);
 433
 434  // Execute
 435  if (options == null || !options.strict) {
 436    try {
 437      var collection = new Collection(
 438        this,
 439        this.s.topology,
 440        this.s.databaseName,
 441        name,
 442        this.s.pkFactory,
 443        options
 444      );
 445      if (callback) callback(null, collection);
 446      return collection;
 447    } catch (err) {
 448      if (err instanceof MongoError && callback) return callback(err);
 449      throw err;
 450    }
 451  }
 452
 453  // Strict mode
 454  if (typeof callback !== 'function') {
 455    throw toError(f('A callback is required in strict mode. While getting collection %s.', name));
 456  }
 457
 458  // Did the user destroy the topology
 459  if (self.serverConfig && self.serverConfig.isDestroyed()) {
 460    return callback(new MongoError('topology was destroyed'));
 461  }
 462
 463  // Strict mode
 464  this.listCollections({ name: name }, options).toArray(function(err, collections) {
 465    if (err != null) return handleCallback(callback, err, null);
 466    if (collections.length === 0)
 467      return handleCallback(
 468        callback,
 469        toError(f('Collection %s does not exist. Currently in strict mode.', name)),
 470        null
 471      );
 472
 473    try {
 474      return handleCallback(
 475        callback,
 476        null,
 477        new Collection(self, self.s.topology, self.s.databaseName, name, self.s.pkFactory, options)
 478      );
 479    } catch (err) {
 480      return handleCallback(callback, err, null);
 481    }
 482  });
 483};
 484
 485define.classMethod('collection', { callback: true, promise: false, returns: [Collection] });
 486
 487function decorateWithWriteConcern(command, self, options) {
 488  // Do we support write concerns 3.4 and higher
 489  if (self.s.topology.capabilities().commandsTakeWriteConcern) {
 490    // Get the write concern settings
 491    var finalOptions = writeConcern(shallowClone(options), self, options);
 492    // Add the write concern to the command
 493    if (finalOptions.writeConcern) {
 494      command.writeConcern = finalOptions.writeConcern;
 495    }
 496  }
 497}
 498
 499var createCollection = function(self, name, options, callback) {
 500  // Get the write concern options
 501  var finalOptions = writeConcern(shallowClone(options), self, options);
 502  // Did the user destroy the topology
 503  if (self.serverConfig && self.serverConfig.isDestroyed()) {
 504    return callback(new MongoError('topology was destroyed'));
 505  }
 506
 507  // Check if we have the name
 508  self
 509    .listCollections({ name: name }, finalOptions)
 510    .setReadPreference(ReadPreference.PRIMARY)
 511    .toArray(function(err, collections) {
 512      if (err != null) return handleCallback(callback, err, null);
 513      if (collections.length > 0 && finalOptions.strict) {
 514        return handleCallback(
 515          callback,
 516          MongoError.create({
 517            message: f('Collection %s already exists. Currently in strict mode.', name),
 518            driver: true
 519          }),
 520          null
 521        );
 522      } else if (collections.length > 0) {
 523        try {
 524          return handleCallback(
 525            callback,
 526            null,
 527            new Collection(
 528              self,
 529              self.s.topology,
 530              self.s.databaseName,
 531              name,
 532              self.s.pkFactory,
 533              options
 534            )
 535          );
 536        } catch (err) {
 537          return handleCallback(callback, err);
 538        }
 539      }
 540
 541      // Create collection command
 542      var cmd = { create: name };
 543
 544      // Decorate command with writeConcern if supported
 545      decorateWithWriteConcern(cmd, self, options);
 546      // Add all optional parameters
 547      for (var n in options) {
 548        if (
 549          options[n] != null &&
 550          typeof options[n] !== 'function' &&
 551          illegalCommandFields.indexOf(n) === -1
 552        ) {
 553          cmd[n] = options[n];
 554        }
 555      }
 556
 557      // Force a primary read Preference
 558      finalOptions.readPreference = ReadPreference.PRIMARY;
 559
 560      // Execute command
 561      self.command(cmd, finalOptions, function(err) {
 562        if (err) return handleCallback(callback, err);
 563        handleCallback(
 564          callback,
 565          null,
 566          new Collection(
 567            self,
 568            self.s.topology,
 569            self.s.databaseName,
 570            name,
 571            self.s.pkFactory,
 572            options
 573          )
 574        );
 575      });
 576    });
 577};
 578
 579/**
 580 * Create a new collection on a server with the specified options. Use this to create capped collections.
 581 * More information about command options available at https://docs.mongodb.com/manual/reference/command/create/
 582 *
 583 * @method
 584 * @param {string} name the collection name we wish to access.
 585 * @param {object} [options=null] Optional settings.
 586 * @param {(number|string)} [options.w=null] The write concern.
 587 * @param {number} [options.wtimeout=null] The write concern timeout.
 588 * @param {boolean} [options.j=false] Specify a journal write concern.
 589 * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
 590 * @param {object} [options.pkFactory=null] A primary key factory object for generation of custom _id keys.
 591 * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
 592 * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
 593 * @param {boolean} [options.strict=false] Returns an error if the collection does not exist
 594 * @param {boolean} [options.capped=false] Create a capped collection.
 595 * @param {boolean} [options.autoIndexId=true] Create an index on the _id field of the document, True by default on MongoDB 2.2 or higher off for version < 2.2.
 596 * @param {number} [options.size=null] The size of the capped collection in bytes.
 597 * @param {number} [options.max=null] The maximum number of documents in the capped collection.
 598 * @param {number} [options.flags=null] Optional. Available for the MMAPv1 storage engine only to set the usePowerOf2Sizes and the noPadding flag.
 599 * @param {object} [options.storageEngine=null] Allows users to specify configuration to the storage engine on a per-collection basis when creating a collection on MongoDB 3.0 or higher.
 600 * @param {object} [options.validator=null] Allows users to specify validation rules or expressions for the collection. For more information, see Document Validation on MongoDB 3.2 or higher.
 601 * @param {string} [options.validationLevel=null] Determines how strictly MongoDB applies the validation rules to existing documents during an update on MongoDB 3.2 or higher.
 602 * @param {string} [options.validationAction=null] Determines whether to error on invalid documents or just warn about the violations but allow invalid documents to be inserted on MongoDB 3.2 or higher.
 603 * @param {object} [options.indexOptionDefaults=null] Allows users to specify a default configuration for indexes when creating a collection on MongoDB 3.2 or higher.
 604 * @param {string} [options.viewOn=null] The name of the source collection or view from which to create the view. The name is not the full namespace of the collection or view; i.e. does not include the database name and implies the same database as the view to create on MongoDB 3.4 or higher.
 605 * @param {array} [options.pipeline=null] An array that consists of the aggregation pipeline stage. create creates the view by applying the specified pipeline to the viewOn collection or view on MongoDB 3.4 or higher.
 606 * @param {object} [options.collation=null] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
 607 * @param {ClientSession} [options.session] optional session to use for this operation
 608 * @param {Db~collectionResultCallback} [callback] The results callback
 609 * @return {Promise} returns Promise if no callback passed
 610 */
 611Db.prototype.createCollection = function(name, options, callback) {
 612  if (typeof options === 'function') (callback = options), (options = {});
 613  options = options || {};
 614  options.promiseLibrary = options.promiseLibrary || this.s.promiseLibrary;
 615
 616  return executeOperation(this.s.topology, createCollection, [this, name, options, callback]);
 617};
 618
 619define.classMethod('createCollection', { callback: true, promise: true });
 620
 621/**
 622 * Get all the db statistics.
 623 *
 624 * @method
 625 * @param {object} [options=null] Optional settings.
 626 * @param {number} [options.scale=null] Divide the returned sizes by scale value.
 627 * @param {ClientSession} [options.session] optional session to use for this operation
 628 * @param {Db~resultCallback} [callback] The collection result callback
 629 * @return {Promise} returns Promise if no callback passed
 630 */
 631Db.prototype.stats = function(options, callback) {
 632  if (typeof options === 'function') (callback = options), (options = {});
 633  options = options || {};
 634  // Build command object
 635  var commandObject = { dbStats: true };
 636  // Check if we have the scale value
 637  if (options['scale'] != null) commandObject['scale'] = options['scale'];
 638
 639  // If we have a readPreference set
 640  if (options.readPreference == null && this.s.readPreference) {
 641    options.readPreference = this.s.readPreference;
 642  }
 643
 644  // Execute the command
 645  return this.command(commandObject, options, callback);
 646};
 647
 648define.classMethod('stats', { callback: true, promise: true });
 649
 650// Transformation methods for cursor results
 651var listCollectionsTranforms = function(databaseName) {
 652  var matching = f('%s.', databaseName);
 653
 654  return {
 655    doc: function(doc) {
 656      var index = doc.name.indexOf(matching);
 657      // Remove database name if available
 658      if (doc.name && index === 0) {
 659        doc.name = doc.name.substr(index + matching.length);
 660      }
 661
 662      return doc;
 663    }
 664  };
 665};
 666
 667/**
 668 * Get the list of all collection information for the specified db.
 669 *
 670 * @method
 671 * @param {object} [filter={}] Query to filter collections by
 672 * @param {object} [options=null] Optional settings.
 673 * @param {number} [options.batchSize=null] The batchSize for the returned command cursor or if pre 2.8 the systems batch collection
 674 * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
 675 * @param {ClientSession} [options.session] optional session to use for this operation
 676 * @return {CommandCursor}
 677 */
 678Db.prototype.listCollections = function(filter, options) {
 679  filter = filter || {};
 680  options = options || {};
 681
 682  // Shallow clone the object
 683  options = shallowClone(options);
 684  // Set the promise library
 685  options.promiseLibrary = this.s.promiseLibrary;
 686
 687  // Ensure valid readPreference
 688  if (options.readPreference) {
 689    options.readPreference = convertReadPreference(options.readPreference);
 690  } else {
 691    options.readPreference = this.s.readPreference || ReadPreference.primary;
 692  }
 693
 694  // We have a list collections command
 695  if (this.serverConfig.capabilities().hasListCollectionsCommand) {
 696    // Cursor options
 697    var cursor = options.batchSize ? { batchSize: options.batchSize } : {};
 698    // Build the command
 699    var command = { listCollections: true, filter: filter, cursor: cursor };
 700    // Set the AggregationCursor constructor
 701    options.cursorFactory = CommandCursor;
 702    // Create the cursor
 703    cursor = this.s.topology.cursor(f('%s.$cmd', this.s.databaseName), command, options);
 704    // Do we have a readPreference, apply it
 705    if (options.readPreference) {
 706      cursor.setReadPreference(options.readPreference);
 707    }
 708    // Return the cursor
 709    return cursor;
 710  }
 711
 712  // We cannot use the listCollectionsCommand
 713  if (!this.serverConfig.capabilities().hasListCollectionsCommand) {
 714    // If we have legacy mode and have not provided a full db name filter it
 715    if (
 716      typeof filter.name === 'string' &&
 717      !new RegExp('^' + this.databaseName + '\\.').test(filter.name)
 718    ) {
 719      filter = shallowClone(filter);
 720      filter.name = f('%s.%s', this.s.databaseName, filter.name);
 721    }
 722  }
 723
 724  // No filter, filter by current database
 725  if (filter == null) {
 726    filter.name = f('/%s/', this.s.databaseName);
 727  }
 728
 729  // Rewrite the filter to use $and to filter out indexes
 730  if (filter.name) {
 731    filter = { $and: [{ name: filter.name }, { name: /^((?!\$).)*$/ }] };
 732  } else {
 733    filter = { name: /^((?!\$).)*$/ };
 734  }
 735
 736  // Return options
 737  var _options = { transforms: listCollectionsTranforms(this.s.databaseName) };
 738  // Get the cursor
 739  cursor = this.collection(Db.SYSTEM_NAMESPACE_COLLECTION).find(filter, _options);
 740  // Do we have a readPreference, apply it
 741  if (options.readPreference) cursor.setReadPreference(options.readPreference);
 742  // Set the passed in batch size if one was provided
 743  if (options.batchSize) cursor = cursor.batchSize(options.batchSize);
 744  // We have a fallback mode using legacy systems collections
 745  return cursor;
 746};
 747
 748define.classMethod('listCollections', {
 749  callback: false,
 750  promise: false,
 751  returns: [CommandCursor]
 752});
 753
 754var evaluate = function(self, code, parameters, options, callback) {
 755  var finalCode = code;
 756  var finalParameters = [];
 757
 758  // Did the user destroy the topology
 759  if (self.serverConfig && self.serverConfig.isDestroyed())
 760    return callback(new MongoError('topology was destroyed'));
 761
 762  // If not a code object translate to one
 763  if (!(finalCode && finalCode._bsontype === 'Code')) finalCode = new Code(finalCode);
 764  // Ensure the parameters are correct
 765  if (parameters != null && !Array.isArray(parameters) && typeof parameters !== 'function') {
 766    finalParameters = [parameters];
 767  } else if (parameters != null && Array.isArray(parameters) && typeof parameters !== 'function') {
 768    finalParameters = parameters;
 769  }
 770
 771  // Create execution selector
 772  var cmd = { $eval: finalCode, args: finalParameters };
 773  // Check if the nolock parameter is passed in
 774  if (options['nolock']) {
 775    cmd['nolock'] = options['nolock'];
 776  }
 777
 778  // Set primary read preference
 779  options.readPreference = new ReadPreference(ReadPreference.PRIMARY);
 780
 781  // Execute the command
 782  self.command(cmd, options, function(err, result) {
 783    if (err) return handleCallback(callback, err, null);
 784    if (result && result.ok === 1) return handleCallback(callback, null, result.retval);
 785    if (result)
 786      return handleCallback(
 787        callback,
 788        MongoError.create({ message: f('eval failed: %s', result.errmsg), driver: true }),
 789        null
 790      );
 791    handleCallback(callback, err, result);
 792  });
 793};
 794
 795/**
 796 * Evaluate JavaScript on the server
 797 *
 798 * @method
 799 * @param {Code} code JavaScript to execute on server.
 800 * @param {(object|array)} parameters The parameters for the call.
 801 * @param {object} [options=null] Optional settings.
 802 * @param {boolean} [options.nolock=false] Tell MongoDB not to block on the evaulation of the javascript.
 803 * @param {ClientSession} [options.session] optional session to use for this operation
 804 * @param {Db~resultCallback} [callback] The results callback
 805 * @deprecated Eval is deprecated on MongoDB 3.2 and forward
 806 * @return {Promise} returns Promise if no callback passed
 807 */
 808Db.prototype.eval = function(code, parameters, options, callback) {
 809  var args = Array.prototype.slice.call(arguments, 1);
 810  callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
 811  parameters = args.length ? args.shift() : parameters;
 812  options = args.length ? args.shift() || {} : {};
 813
 814  return executeOperation(this.s.topology, evaluate, [this, code, parameters, options, callback]);
 815};
 816
 817define.classMethod('eval', { callback: true, promise: true });
 818
 819/**
 820 * Rename a collection.
 821 *
 822 * @method
 823 * @param {string} fromCollection Name of current collection to rename.
 824 * @param {string} toCollection New name of of the collection.
 825 * @param {object} [options=null] Optional settings.
 826 * @param {boolean} [options.dropTarget=false] Drop the target name collection if it previously exists.
 827 * @param {ClientSession} [options.session] optional session to use for this operation
 828 * @param {Db~collectionResultCallback} [callback] The results callback
 829 * @return {Promise} returns Promise if no callback passed
 830 */
 831Db.prototype.renameCollection = function(fromCollection, toCollection, options, callback) {
 832  if (typeof options === 'function') (callback = options), (options = {});
 833  options = options || {};
 834  // Add return new collection
 835  options.new_collection = true;
 836
 837  const collection = this.collection(fromCollection);
 838  return executeOperation(this.s.topology, collection.rename.bind(collection), [
 839    toCollection,
 840    options,
 841    callback
 842  ]);
 843};
 844
 845define.classMethod('renameCollection', { callback: true, promise: true });
 846
 847/**
 848 * Drop a collection from the database, removing it permanently. New accesses will create a new collection.
 849 *
 850 * @method
 851 * @param {string} name Name of collection to drop
 852 * @param {Object} [options] Optional settings
 853 * @param {ClientSession} [options.session] optional session to use for this operation
 854 * @param {Db~resultCallback} [callback] The results callback
 855 * @return {Promise} returns Promise if no callback passed
 856 */
 857Db.prototype.dropCollection = function(name, options, callback) {
 858  if (typeof options === 'function') (callback = options), (options = {});
 859  options = options || {};
 860
 861  // Command to execute
 862  var cmd = { drop: name };
 863
 864  // Decorate with write concern
 865  decorateWithWriteConcern(cmd, this, options);
 866
 867  // options
 868  const opts = Object.assign({}, this.s.options, { readPreference: ReadPreference.PRIMARY });
 869  if (options.session) opts.session = options.session;
 870
 871  return executeOperation(this.s.topology, dropCollection, [this, cmd, opts, callback]);
 872};
 873
 874const dropCollection = (self, cmd, options, callback) => {
 875  return self.command(cmd, options, function(err, result) {
 876    // Did the user destroy the topology
 877    if (self.serverConfig && self.serverConfig.isDestroyed()) {
 878      return callback(new MongoError('topology was destroyed'));
 879    }
 880
 881    if (err) return handleCallback(callback, err);
 882    if (result.ok) return handleCallback(callback, null, true);
 883    handleCallback(callback, null, false);
 884  });
 885};
 886
 887define.classMethod('dropCollection', { callback: true, promise: true });
 888
 889/**
 890 * Drop a database, removing it permanently from the server.
 891 *
 892 * @method
 893 * @param {Object} [options] Optional settings
 894 * @param {ClientSession} [options.session] optional session to use for this operation
 895 * @param {Db~resultCallback} [callback] The results callback
 896 * @return {Promise} returns Promise if no callback passed
 897 */
 898Db.prototype.dropDatabase = function(options, callback) {
 899  if (typeof options === 'function') (callback = options), (options = {});
 900  options = options || {};
 901  // Drop database command
 902  var cmd = { dropDatabase: 1 };
 903
 904  // Decorate with write concern
 905  decorateWithWriteConcern(cmd, this, options);
 906
 907  // Ensure primary only
 908  const finalOptions = Object.assign(
 909    {},
 910    { readPreference: ReadPreference.PRIMARY },
 911    this.s.options
 912  );
 913  if (options.session) {
 914    finalOptions.session = options.session;
 915  }
 916
 917  return executeOperation(this.s.topology, dropDatabase, [this, cmd, finalOptions, callback]);
 918};
 919
 920const dropDatabase = (self, cmd, options, callback) => {
 921  self.command(cmd, options, function(err, result) {
 922    // Did the user destroy the topology
 923    if (self.serverConfig && self.serverConfig.isDestroyed()) {
 924      return callback(new MongoError('topology was destroyed'));
 925    }
 926
 927    if (callback == null) return;
 928    if (err) return handleCallback(callback, err, null);
 929    handleCallback(callback, null, result.ok ? true : false);
 930  });
 931};
 932
 933define.classMethod('dropDatabase', { callback: true, promise: true });
 934
 935/**
 936 * Fetch all collections for the current db.
 937 *
 938 * @method
 939 * @param {Object} [options] Optional settings
 940 * @param {ClientSession} [options.session] optional session to use for this operation
 941 * @param {Db~collectionsResultCallback} [callback] The results callback
 942 * @return {Promise} returns Promise if no callback passed
 943 */
 944Db.prototype.collections = function(options, callback) {
 945  if (typeof options === 'function') (callback = options), (options = {});
 946  options = options || {};
 947
 948  return executeOperation(this.s.topology, collections, [this, options, callback]);
 949};
 950
 951var collections = function(self, options, callback) {
 952  // Let's get the collection names
 953  self.listCollections({}, options).toArray(function(err, documents) {
 954    if (err != null) return handleCallback(callback, err, null);
 955    // Filter collections removing any illegal ones
 956    documents = documents.filter(function(doc) {
 957      return doc.name.indexOf('$') === -1;
 958    });
 959
 960    // Return the collection objects
 961    handleCallback(
 962      callback,
 963      null,
 964      documents.map(function(d) {
 965        return new Collection(
 966          self,
 967          self.s.topology,
 968          self.s.databaseName,
 969          d.name,
 970          self.s.pkFactory,
 971          self.s.options
 972        );
 973      })
 974    );
 975  });
 976};
 977
 978define.classMethod('collections', { callback: true, promise: true });
 979
 980/**
 981 * Runs a command on the database as admin.
 982 * @method
 983 * @param {object} command The command hash
 984 * @param {object} [options=null] Optional settings.
 985 * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
 986 * @param {ClientSession} [options.session] optional session to use for this operation
 987 * @param {Db~resultCallback} [callback] The command result callback
 988 * @return {Promise} returns Promise if no callback passed
 989 */
 990Db.prototype.executeDbAdminCommand = function(selector, options, callback) {
 991  if (typeof options === 'function') (callback = options), (options = {});
 992  options = options || {};
 993
 994  // Convert read preference
 995  if (options.readPreference) {
 996    options.readPreference = convertReadPreference(options.readPreference);
 997  }
 998
 999  return executeOperation(this.s.topology, executeDbAdminCommand, [
1000    this,
1001    selector,
1002    options,
1003    callback
1004  ]);
1005};
1006
1007const executeDbAdminCommand = (self, selector, options, callback) => {
1008  self.s.topology.command('admin.$cmd', selector, options, function(err, result) {
1009    // Did the user destroy the topology
1010    if (self.serverConfig && self.serverConfig.isDestroyed()) {
1011      return callback(new MongoError('topology was destroyed'));
1012    }
1013
1014    if (err) return handleCallback(callback, err);
1015    handleCallback(callback, null, result.result);
1016  });
1017};
1018
1019define.classMethod('executeDbAdminCommand', { callback: true, promise: true });
1020
1021/**
1022 * Creates an index on the db and collection collection.
1023 * @method
1024 * @param {string} name Name of the collection to create the index on.
1025 * @param {(string|object)} fieldOrSpec Defines the index.
1026 * @param {object} [options=null] Optional settings.
1027 * @param {(number|string)} [options.w=null] The write concern.
1028 * @param {number} [options.wtimeout=null] The write concern timeout.
1029 * @param {boolean} [options.j=false] Specify a journal write concern.
1030 * @param {boolean} [options.unique=false] Creates an unique index.
1031 * @param {boolean} [options.sparse=false] Creates a sparse index.
1032 * @param {boolean} [options.background=false] Creates the index in the background, yielding whenever possible.
1033 * @param {boolean} [options.dropDups=false] A unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value
1034 * @param {number} [options.min=null] For geospatial indexes set the lower bound for the co-ordinates.
1035 * @param {number} [options.max=null] For geospatial indexes set the high bound for the co-ordinates.
1036 * @param {number} [options.v=null] Specify the format version of the indexes.
1037 * @param {number} [options.expireAfterSeconds=null] Allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher)
1038 * @param {number} [options.name=null] Override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
1039 * @param {object} [options.partialFilterExpression=null] Creates a partial index based on the given filter object (MongoDB 3.2 or higher)
1040 * @param {ClientSession} [options.session] optional session to use for this operation
1041 * @param {Db~resultCallback} [callback] The command result callback
1042 * @return {Promise} returns Promise if no callback passed
1043 */
1044Db.prototype.createIndex = function(name, fieldOrSpec, options, callback) {
1045  if (typeof options === 'function') (callback = options), (options = {});
1046  options = options ? shallowClone(options) : {};
1047
1048  return executeOperation(this.s.topology, createIndex, [
1049    this,
1050    name,
1051    fieldOrSpec,
1052    options,
1053    callback
1054  ]);
1055};
1056
1057var createIndex = function(self, name, fieldOrSpec, options, callback) {
1058  // Get the write concern options
1059  var finalOptions = Object.assign({}, { readPreference: ReadPreference.PRIMARY }, options);
1060  finalOptions = writeConcern(finalOptions, self, options);
1061
1062  // Ensure we have a callback
1063  if (finalOptions.writeConcern && typeof callback !== 'function') {
1064    throw MongoError.create({
1065      message: 'Cannot use a writeConcern without a provided callback',
1066      driver: true
1067    });
1068  }
1069
1070  // Did the user destroy the topology
1071  if (self.serverConfig && self.serverConfig.isDestroyed())
1072    return callback(new MongoError('topology was destroyed'));
1073
1074  // Attempt to run using createIndexes command
1075  createIndexUsingCreateIndexes(self, name, fieldOrSpec, options, function(err, result) {
1076    if (err == null) return handleCallback(callback, err, result);
1077
1078    // 67 = 'CannotCreateIndex' (malformed index options)
1079    // 85 = 'IndexOptionsConflict' (index already exists with different options)
1080    // 11000 = 'DuplicateKey' (couldn't build unique index because of dupes)
1081    // 11600 = 'InterruptedAtShutdown' (interrupted at shutdown)
1082    // These errors mean that the server recognized `createIndex` as a command
1083    // and so we don't need to fallback to an insert.
1084    if (err.code === 67 || err.code === 11000 || err.code === 85 || err.code === 11600) {
1085      return handleCallback(callback, err, result);
1086    }
1087
1088    // Create command
1089    var doc = createCreateIndexCommand(self, name, fieldOrSpec, options);
1090    // Set no key checking
1091    finalOptions.checkKeys = false;
1092    // Insert document
1093    self.s.topology.insert(
1094      f('%s.%s', self.s.databaseName, Db.SYSTEM_INDEX_COLLECTION),
1095      doc,
1096      finalOptions,
1097      function(err, result) {
1098        if (callback == null) return;
1099        if (err) return handleCallback(callback, err);
1100        if (result == null) return handleCallback(callback, null, null);
1101        if (result.result.writeErrors)
1102          return handleCallback(callback, MongoError.create(result.result.writeErrors[0]), null);
1103        handleCallback(callback, null, doc.name);
1104      }
1105    );
1106  });
1107};
1108
1109define.classMethod('createIndex', { callback: true, promise: true });
1110
1111/**
1112 * Ensures that an index exists, if it does not it creates it
1113 * @method
1114 * @deprecated since version 2.0
1115 * @param {string} name The index name
1116 * @param {(string|object)} fieldOrSpec Defines the index.
1117 * @param {object} [options=null] Optional settings.
1118 * @param {(number|string)} [options.w=null] The write concern.
1119 * @param {number} [options.wtimeout=null] The write concern timeout.
1120 * @param {boolean} [options.j=false] Specify a journal write concern.
1121 * @param {boolean} [options.unique=false] Creates an unique index.
1122 * @param {boolean} [options.sparse=false] Creates a sparse index.
1123 * @param {boolean} [options.background=false] Creates the index in the background, yielding whenever possible.
1124 * @param {boolean} [options.dropDups=false] A unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value
1125 * @param {number} [options.min=null] For geospatial indexes set the lower bound for the co-ordinates.
1126 * @param {number} [options.max=null] For geospatial indexes set the high bound for the co-ordinates.
1127 * @param {number} [options.v=null] Specify the format version of the indexes.
1128 * @param {number} [options.expireAfterSeconds=null] Allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher)
1129 * @param {number} [options.name=null] Override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
1130 * @param {ClientSession} [options.session] optional session to use for this operation
1131 * @param {Db~resultCallback} [callback] The command result callback
1132 * @return {Promise} returns Promise if no callback passed
1133 */
1134Db.prototype.ensureIndex = function(name, fieldOrSpec, options, callback) {
1135  if (typeof options === 'function') (callback = options), (options = {});
1136  options = options || {};
1137
1138  return executeOperation(this.s.topology, ensureIndex, [
1139    this,
1140    name,
1141    fieldOrSpec,
1142    options,
1143    callback
1144  ]);
1145};
1146
1147var ensureIndex = function(self, name, fieldOrSpec, options, callback) {
1148  // Get the write concern options
1149  var finalOptions = writeConcern({}, self, options);
1150  // Create command
1151  var selector = createCreateIndexCommand(self, name, fieldOrSpec, options);
1152  var index_name = selector.name;
1153
1154  // Did the user destroy the topology
1155  if (self.serverConfig && self.serverConfig.isDestroyed())
1156    return callback(new MongoError('topology was destroyed'));
1157
1158  // Merge primary readPreference
1159  finalOptions.readPreference = ReadPreference.PRIMARY;
1160
1161  // Check if the index allready exists
1162  self.indexInformation(name, finalOptions, function(err, indexInformation) {
1163    if (err != null && err.code !== 26) return handleCallback(callback, err, null);
1164    // If the index does not exist, create it
1165    if (indexInformation == null || !indexInformation[index_name]) {
1166      self.createIndex(name, fieldOrSpec, options, callback);
1167    } else {
1168      if (typeof callback === 'function') return handleCallback(callback, null, index_name);
1169    }
1170  });
1171};
1172
1173define.classMethod('ensureIndex', { callback: true, promise: true });
1174
1175Db.prototype.addChild = function(db) {
1176  if (this.s.parentDb) return this.s.parentDb.addChild(db);
1177  this.s.children.push(db);
1178};
1179
1180var _executeAuthCreateUserCommand = function(self, username, password, options, callback) {
1181  // Special case where there is no password ($external users)
1182  if (typeof username === 'string' && password != null && typeof password === 'object') {
1183    options = password;
1184    password = null;
1185  }
1186
1187  // Unpack all options
1188  if (typeof options === 'function') {
1189    callback = options;
1190    options = {};
1191  }
1192
1193  // Error out if we digestPassword set
1194  if (options.digestPassword != null) {
1195    throw toError(
1196      "The digestPassword option is not supported via add_user. Please use db.command('createUser', ...) instead for this option."
1197    );
1198  }
1199
1200  // Get additional values
1201  var customData = options.customData != null ? options.customData : {};
1202  var roles = Array.isArray(options.roles) ? options.roles : [];
1203  var maxTimeMS = typeof options.maxTimeMS === 'number' ? options.maxTimeMS : null;
1204
1205  // If not roles defined print deprecated message
1206  if (roles.length === 0) {
1207    console.log('Creating a user without roles is deprecated in MongoDB >= 2.6');
1208  }
1209
1210  // Get the error options
1211  var commandOptions = { writeCommand: true };
1212  if (options['dbName']) commandOptions.dbName = options['dbName'];
1213
1214  // Add maxTimeMS to options if set
1215  if (maxTimeMS != null) commandOptions.maxTimeMS = maxTimeMS;
1216
1217  // Check the db name and add roles if needed
1218  if (
1219    (self.databaseName.toLowerCase() === 'admin' || options.dbName === 'admin') &&
1220    !Array.isArray(options.roles)
1221  ) {
1222    roles = ['root'];
1223  } else if (!Array.isArray(options.roles)) {
1224    roles = ['dbOwner'];
1225  }
1226
1227  // Build the command to execute
1228  var command = {
1229    createUser: username,
1230    customData: customData,
1231    roles: roles,
1232    digestPassword: false
1233  };
1234
1235  // Apply write concern to command
1236  command = writeConcern(command, self, options);
1237
1238  // Use node md5 generator
1239  var md5 = crypto.createHash('md5');
1240  // Generate keys used for authentication
1241  md5.update(username + ':mongo:' + password);
1242  var userPassword = md5.digest('hex');
1243
1244  // No password
1245  if (typeof password === 'string') {
1246    command.pwd = userPassword;
1247  }
1248
1249  // Force write using primary
1250  commandOptions.readPreference = ReadPreference.primary;
1251
1252  // Execute the command
1253  self.command(command, commandOptions, function(err, result) {
1254    if (err && err.ok === 0 && err.code === undefined)
1255      return handleCallback(callback, { code: -5000 }, null);
1256    if (err) return handleCallback(callback, err, null);
1257    handleCallback(
1258      callback,
1259      !result.ok ? toError(result) : null,
1260      result.ok ? [{ user: username, pwd: '' }] : null
1261    );
1262  });
1263};
1264
1265var addUser = function(self, username, password, options, callback) {
1266  // Did the user destroy the topology
1267  if (self.serverConfig && self.serverConfig.isDestroyed())
1268    return callback(new MongoError('topology was destroyed'));
1269  // Attempt to execute auth command
1270  _executeAuthCreateUserCommand(self, username, password, options, function(err, r) {
1271    // We need to perform the backward compatible insert operation
1272    if (err && err.code === -5000) {
1273      var finalOptions = writeConcern(shallowClone(options), self, options);
1274      // Use node md5 generator
1275      var md5 = crypto.createHash('md5');
1276      // Generate keys used for authentication
1277      md5.update(username + ':mongo:' + password);
1278      var userPassword = md5.digest('hex');
1279
1280      // If we have another db set
1281      var db = options.dbName ? new Db(options.dbName, self.s.topology, self.s.option

Large files files are truncated, but you can click here to view the full file