/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 are truncated click here to view the full file

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