mongodb 2.2.36 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,64 +1,139 @@
1
- "use strict";
2
-
3
- var parse = require('./url_parser')
4
- , Server = require('./server')
5
- , Mongos = require('./mongos')
6
- , ReplSet = require('./replset')
7
- , EventEmitter = require('events').EventEmitter
8
- , inherits = require('util').inherits
9
- , Define = require('./metadata')
10
- , ReadPreference = require('./read_preference')
11
- , Logger = require('mongodb-core').Logger
12
- , MongoError = require('mongodb-core').MongoError
13
- , Db = require('./db')
14
- , f = require('util').format
15
- , assign = require('./utils').assign
16
- , shallowClone = require('./utils').shallowClone
17
- , authenticate = require('./authenticate');
1
+ 'use strict';
2
+
3
+ var parse = require('./url_parser'),
4
+ Server = require('./topologies/server'),
5
+ Mongos = require('./topologies/mongos'),
6
+ ReplSet = require('./topologies/replset'),
7
+ EventEmitter = require('events').EventEmitter,
8
+ inherits = require('util').inherits,
9
+ Define = require('./metadata'),
10
+ ReadPreference = require('mongodb-core').ReadPreference,
11
+ Logger = require('mongodb-core').Logger,
12
+ MongoError = require('mongodb-core').MongoError,
13
+ handleCallback = require('./utils').handleCallback,
14
+ Db = require('./db'),
15
+ f = require('util').format,
16
+ shallowClone = require('./utils').shallowClone,
17
+ authenticate = require('./authenticate'),
18
+ ServerSessionPool = require('mongodb-core').Sessions.ServerSessionPool,
19
+ executeOperation = require('./utils').executeOperation;
18
20
 
19
21
  /**
20
22
  * @fileOverview The **MongoClient** class is a class that allows for making Connections to MongoDB.
21
23
  *
22
24
  * @example
23
- * var MongoClient = require('mongodb').MongoClient,
24
- * test = require('assert');
25
+ * // Connect using a MongoClient instance
26
+ * const MongoClient = require('mongodb').MongoClient;
27
+ * const test = require('assert');
25
28
  * // Connection url
26
- * var url = 'mongodb://localhost:27017/test';
29
+ * const url = 'mongodb://localhost:27017';
30
+ * // Database Name
31
+ * const dbName = 'test';
27
32
  * // Connect using MongoClient
28
- * MongoClient.connect(url, function(err, db) {
29
- * // Get an additional db
30
- * db.close();
33
+ * const mongoClient = new MongoClient(url);
34
+ * mongoClient.connect(function(err, client) {
35
+ * const db = client.db(dbName);
36
+ * client.close();
37
+ * });
38
+ *
39
+ * @example
40
+ * // Connect using the MongoClient.connect static method
41
+ * const MongoClient = require('mongodb').MongoClient;
42
+ * const test = require('assert');
43
+ * // Connection url
44
+ * const url = 'mongodb://localhost:27017';
45
+ * // Database Name
46
+ * const dbName = 'test';
47
+ * // Connect using MongoClient
48
+ * MongoClient.connect(url, function(err, client) {
49
+ * const db = client.db(dbName);
50
+ * client.close();
31
51
  * });
32
52
  */
33
- var validOptionNames = ['poolSize', 'ssl', 'sslValidate', 'sslCA', 'sslCert', 'ciphers', 'ecdhCurve',
34
- 'sslKey', 'sslPass', 'sslCRL', 'autoReconnect', 'noDelay', 'keepAlive', 'connectTimeoutMS', 'family',
35
- 'socketTimeoutMS', 'reconnectTries', 'reconnectInterval', 'ha', 'haInterval',
36
- 'replicaSet', 'secondaryAcceptableLatencyMS', 'acceptableLatencyMS',
37
- 'connectWithNoPrimary', 'authSource', 'w', 'wtimeout', 'j', 'forceServerObjectId',
38
- 'serializeFunctions', 'ignoreUndefined', 'raw', 'bufferMaxEntries',
39
- 'readPreference', 'pkFactory', 'promiseLibrary', 'readConcern', 'maxStalenessSeconds',
40
- 'loggerLevel', 'logger', 'promoteValues', 'promoteBuffers', 'promoteLongs',
41
- 'domainsEnabled', 'keepAliveInitialDelay', 'checkServerIdentity', 'validateOptions', 'appname', 'auth'];
53
+ var validOptionNames = [
54
+ 'poolSize',
55
+ 'ssl',
56
+ 'sslValidate',
57
+ 'sslCA',
58
+ 'sslCert',
59
+ 'sslKey',
60
+ 'sslPass',
61
+ 'sslCRL',
62
+ 'autoReconnect',
63
+ 'noDelay',
64
+ 'keepAlive',
65
+ 'keepAliveInitialDelay',
66
+ 'connectTimeoutMS',
67
+ 'family',
68
+ 'socketTimeoutMS',
69
+ 'reconnectTries',
70
+ 'reconnectInterval',
71
+ 'ha',
72
+ 'haInterval',
73
+ 'replicaSet',
74
+ 'secondaryAcceptableLatencyMS',
75
+ 'acceptableLatencyMS',
76
+ 'connectWithNoPrimary',
77
+ 'authSource',
78
+ 'w',
79
+ 'wtimeout',
80
+ 'j',
81
+ 'forceServerObjectId',
82
+ 'serializeFunctions',
83
+ 'ignoreUndefined',
84
+ 'raw',
85
+ 'bufferMaxEntries',
86
+ 'readPreference',
87
+ 'pkFactory',
88
+ 'promiseLibrary',
89
+ 'readConcern',
90
+ 'maxStalenessSeconds',
91
+ 'loggerLevel',
92
+ 'logger',
93
+ 'promoteValues',
94
+ 'promoteBuffers',
95
+ 'promoteLongs',
96
+ 'domainsEnabled',
97
+ 'checkServerIdentity',
98
+ 'validateOptions',
99
+ 'appname',
100
+ 'auth',
101
+ 'user',
102
+ 'password',
103
+ 'authMechanism',
104
+ 'compression',
105
+ 'fsync',
106
+ 'readPreferenceTags',
107
+ 'numberOfRetries',
108
+ 'auto_reconnect',
109
+ 'minSize'
110
+ ];
111
+
42
112
  var ignoreOptionNames = ['native_parser'];
43
113
  var legacyOptionNames = ['server', 'replset', 'replSet', 'mongos', 'db'];
44
114
 
45
115
  function validOptions(options) {
46
116
  var _validOptions = validOptionNames.concat(legacyOptionNames);
47
117
 
48
- for(var name in options) {
49
- if(ignoreOptionNames.indexOf(name) != -1) {
118
+ for (var name in options) {
119
+ if (ignoreOptionNames.indexOf(name) !== -1) {
50
120
  continue;
51
121
  }
52
122
 
53
- if(_validOptions.indexOf(name) == -1 && options.validateOptions) {
123
+ if (_validOptions.indexOf(name) === -1 && options.validateOptions) {
54
124
  return new MongoError(f('option %s is not supported', name));
55
- } else if(_validOptions.indexOf(name) == -1) {
125
+ } else if (_validOptions.indexOf(name) === -1) {
56
126
  console.warn(f('the options [%s] is not supported', name));
57
127
  }
58
128
 
59
- if(legacyOptionNames.indexOf(name) != -1) {
60
- console.warn(f('the server/replset/mongos options are deprecated, '
61
- + 'all their options are supported at the top level of the options object [%s]', validOptionNames));
129
+ if (legacyOptionNames.indexOf(name) !== -1) {
130
+ console.warn(
131
+ f(
132
+ 'the server/replset/mongos options are deprecated, ' +
133
+ 'all their options are supported at the top level of the options object [%s]',
134
+ validOptionNames
135
+ )
136
+ );
62
137
  }
63
138
  }
64
139
  }
@@ -66,82 +141,87 @@ function validOptions(options) {
66
141
  /**
67
142
  * Creates a new MongoClient instance
68
143
  * @class
69
- * @return {MongoClient} a MongoClient instance.
144
+ * @param {string} url The connection URI string
145
+ * @param {object} [options] Optional settings
146
+ * @param {number} [options.poolSize=5] The maximum size of the individual server pool
147
+ * @param {boolean} [options.ssl=false] Enable SSL connection.
148
+ * @param {boolean} [options.sslValidate=true] Validate mongod server certificate against Certificate Authority
149
+ * @param {buffer} [options.sslCA=undefined] SSL Certificate store binary buffer
150
+ * @param {buffer} [options.sslCert=undefined] SSL Certificate binary buffer
151
+ * @param {buffer} [options.sslKey=undefined] SSL Key file binary buffer
152
+ * @param {string} [options.sslPass=undefined] SSL Certificate pass phrase
153
+ * @param {buffer} [options.sslCRL=undefined] SSL Certificate revocation list binary buffer
154
+ * @param {boolean} [options.autoReconnect=true] Enable autoReconnect for single server instances
155
+ * @param {boolean} [options.noDelay=true] TCP Connection no delay
156
+ * @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
157
+ * @param {number} [options.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
158
+ * @param {number} [options.connectTimeoutMS=30000] TCP Connection timeout setting
159
+ * @param {number} [options.family=null] Version of IP stack. Can be 4, 6 or null (default).
160
+ * If null, will attempt to connect with IPv6, and will fall back to IPv4 on failure
161
+ * @param {number} [options.socketTimeoutMS=360000] TCP Socket timeout setting
162
+ * @param {number} [options.reconnectTries=30] Server attempt to reconnect #times
163
+ * @param {number} [options.reconnectInterval=1000] Server will wait # milliseconds between retries
164
+ * @param {boolean} [options.ha=true] Control if high availability monitoring runs for Replicaset or Mongos proxies
165
+ * @param {number} [options.haInterval=10000] The High availability period for replicaset inquiry
166
+ * @param {string} [options.replicaSet=undefined] The Replicaset set name
167
+ * @param {number} [options.secondaryAcceptableLatencyMS=15] Cutoff latency point in MS for Replicaset member selection
168
+ * @param {number} [options.acceptableLatencyMS=15] Cutoff latency point in MS for Mongos proxies selection
169
+ * @param {boolean} [options.connectWithNoPrimary=false] Sets if the driver should connect even if no primary is available
170
+ * @param {string} [options.authSource=undefined] Define the database to authenticate against
171
+ * @param {(number|string)} [options.w=null] The write concern
172
+ * @param {number} [options.wtimeout=null] The write concern timeout
173
+ * @param {boolean} [options.j=false] Specify a journal write concern
174
+ * @param {boolean} [options.forceServerObjectId=false] Force server to assign _id values instead of driver
175
+ * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object
176
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields
177
+ * @param {boolean} [options.raw=false] Return document results as raw BSON buffers
178
+ * @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
179
+ * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST)
180
+ * @param {object} [options.pkFactory=null] A primary key factory object for generation of custom _id keys
181
+ * @param {object} [options.promiseLibrary=null] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
182
+ * @param {object} [options.readConcern=null] Specify a read concern for the collection (only MongoDB 3.2 or higher supported)
183
+ * @param {string} [options.readConcern.level='local'] Specify a read concern level for the collection operations, one of [local|majority]. (only MongoDB 3.2 or higher supported)
184
+ * @param {number} [options.maxStalenessSeconds=undefined] The max staleness to secondary reads (values under 10 seconds cannot be guaranteed)
185
+ * @param {string} [options.loggerLevel=undefined] The logging level (error/warn/info/debug)
186
+ * @param {object} [options.logger=undefined] Custom logger object
187
+ * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types
188
+ * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers
189
+ * @param {boolean} [options.promoteLongs=true] Promotes long values to number if they fit inside the 53 bits resolution
190
+ * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit
191
+ * @param {boolean|function} [options.checkServerIdentity=true] Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function
192
+ * @param {object} [options.validateOptions=false] Validate MongoClient passed in options for correctness
193
+ * @param {string} [options.appname=undefined] The name of the application that created this MongoClient instance. MongoDB 3.4 and newer will print this value in the server log upon establishing each connection. It is also recorded in the slow query log and profile collections
194
+ * @param {string} [options.auth.user=undefined] The username for auth
195
+ * @param {string} [options.auth.password=undefined] The password for auth
196
+ * @param {string} [options.authMechanism=undefined] Mechanism for authentication: MDEFAULT, GSSAPI, PLAIN, MONGODB-X509, SCRAM-SHA-1 or MONGODB-CR
197
+ * @param {object} [options.compression=null] Type of compression to use: snappy or zlib
198
+ * @param {boolean} [options.fsync=false] Specify a file sync write concern
199
+ * @param {array} [options.readPreferenceTags=null] Read preference tags
200
+ * @param {number} [options.numberOfRetries=5] The number of retries for a tailable cursor
201
+ * @param {boolean} [options.auto_reconnect=true] Enable auto reconnecting for single server instances
202
+ * @param {MongoClient~connectCallback} [callback] The command result callback
203
+ * @return {MongoClient} a MongoClient instance
70
204
  */
71
- function MongoClient() {
72
- if(!(this instanceof MongoClient)) return new MongoClient();
205
+ function MongoClient(url, options) {
206
+ if (!(this instanceof MongoClient)) return new MongoClient();
73
207
 
74
208
  // Set up event emitter
75
209
  EventEmitter.call(this);
76
210
 
77
- /**
78
- * The callback format for results
79
- * @callback MongoClient~connectCallback
80
- * @param {MongoError} error An error instance representing the error during the execution.
81
- * @param {Db} db The connected database.
82
- */
83
-
84
- /**
85
- * Connect to MongoDB using a url as documented at
86
- *
87
- * docs.mongodb.org/manual/reference/connection-string/
88
- *
89
- * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
90
- *
91
- * @method
92
- * @param {string} url The connection URI string
93
- * @param {object} [options] Optional settings.
94
- * @param {number} [options.poolSize=5] poolSize The maximum size of the individual server pool.
95
- * @param {boolean} [options.ssl=false] Enable SSL connection.
96
- * @param {Buffer} [options.sslCA=undefined] SSL Certificate store binary buffer
97
- * @param {Buffer} [options.sslCRL=undefined] SSL Certificate revocation list binary buffer
98
- * @param {Buffer} [options.sslCert=undefined] SSL Certificate binary buffer
99
- * @param {Buffer} [options.sslKey=undefined] SSL Key file binary buffer
100
- * @param {string} [options.sslPass=undefined] SSL Certificate pass phrase
101
- * @param {boolean|function} [options.checkServerIdentity=true] Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function.
102
- * @param {boolean} [options.autoReconnect=true] Enable autoReconnect for single server instances
103
- * @param {boolean} [options.noDelay=true] TCP Connection no delay
104
- * @param {number} [options.family=4] Version of IP stack. Defaults to 4.
105
- * @param {number} [options.keepAlive=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket.
106
- * @param {number} [options.connectTimeoutMS=30000] TCP Connection timeout setting
107
- * @param {number} [options.socketTimeoutMS=360000] TCP Socket timeout setting
108
- * @param {number} [options.reconnectTries=30] Server attempt to reconnect #times
109
- * @param {number} [options.reconnectInterval=1000] Server will wait # milliseconds between retries
110
- * @param {boolean} [options.ha=true] Control if high availability monitoring runs for Replicaset or Mongos proxies.
111
- * @param {number} [options.haInterval=10000] The High availability period for replicaset inquiry
112
- * @param {string} [options.replicaSet=undefined] The Replicaset set name
113
- * @param {number} [options.secondaryAcceptableLatencyMS=15] Cutoff latency point in MS for Replicaset member selection
114
- * @param {number} [options.acceptableLatencyMS=15] Cutoff latency point in MS for Mongos proxies selection.
115
- * @param {boolean} [options.connectWithNoPrimary=false] Sets if the driver should connect even if no primary is available
116
- * @param {string} [options.authSource=undefined] Define the database to authenticate against
117
- * @param {string} [options.auth.user=undefined] The username for auth
118
- * @param {string} [options.auth.password=undefined] The password for auth
119
- * @param {(number|string)} [options.w=null] The write concern.
120
- * @param {number} [options.wtimeout=null] The write concern timeout.
121
- * @param {boolean} [options.j=false] Specify a journal write concern.
122
- * @param {boolean} [options.forceServerObjectId=false] Force server to assign _id values instead of driver.
123
- * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
124
- * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
125
- * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
126
- * @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
127
- * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
128
- * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
129
- * @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.
130
- * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
131
- * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
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 {string} [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
- * @param {number} [options.maxStalenessSeconds=undefined] The max staleness to secondary reads (values under 10 seconds cannot be guaranteed);
137
- * @param {string} [options.appname=undefined] The name of the application that created this MongoClient instance. MongoDB 3.4 and newer will print this value in the server log upon establishing each connection. It is also recorded in the slow query log and profile collections.
138
- * @param {string} [options.loggerLevel=undefined] The logging level (error/warn/info/debug)
139
- * @param {object} [options.logger=undefined] Custom logger object
140
- * @param {object} [options.validateOptions=false] Validate MongoClient passed in options for correctness.
141
- * @param {MongoClient~connectCallback} [callback] The command result callback
142
- * @return {Promise} returns Promise if no callback passed
143
- */
144
- this.connect = MongoClient.connect;
211
+ // The internal state
212
+ this.s = {
213
+ url: url,
214
+ options: options || {},
215
+ promiseLibrary: null,
216
+ dbCache: {},
217
+ sessions: []
218
+ };
219
+
220
+ // Get the promiseLibrary
221
+ var promiseLibrary = this.s.options.promiseLibrary || Promise;
222
+
223
+ // Add the promise to the internal state
224
+ this.s.promiseLibrary = promiseLibrary;
145
225
  }
146
226
 
147
227
  /**
@@ -149,7 +229,183 @@ function MongoClient() {
149
229
  */
150
230
  inherits(MongoClient, EventEmitter);
151
231
 
152
- var define = MongoClient.define = new Define('MongoClient', MongoClient, false);
232
+ var define = (MongoClient.define = new Define('MongoClient', MongoClient, false));
233
+
234
+ /**
235
+ * The callback format for results
236
+ * @callback MongoClient~connectCallback
237
+ * @param {MongoError} error An error instance representing the error during the execution.
238
+ * @param {MongoClient} client The connected client.
239
+ */
240
+
241
+ /**
242
+ * Connect to MongoDB using a url as documented at
243
+ *
244
+ * docs.mongodb.org/manual/reference/connection-string/
245
+ *
246
+ * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
247
+ *
248
+ * @method
249
+ * @param {MongoClient~connectCallback} [callback] The command result callback
250
+ * @return {Promise<MongoClient>} returns Promise if no callback passed
251
+ */
252
+ MongoClient.prototype.connect = function(callback) {
253
+ // Validate options object
254
+ var err = validOptions(this.s.options);
255
+
256
+ if (typeof callback === 'string') {
257
+ throw new TypeError('`connect` only accepts a callback');
258
+ }
259
+
260
+ return executeOperation(this, connectOp, [this, err, callback], {
261
+ skipSessions: true
262
+ });
263
+ };
264
+
265
+ const connectOp = (self, err, callback) => {
266
+ // Did we have a validation error
267
+ if (err) return callback(err);
268
+ // Fallback to callback based connect
269
+ connect(self, self.s.url, self.s.options, function(err) {
270
+ if (err) return callback(err);
271
+ callback(null, self);
272
+ });
273
+ };
274
+
275
+ define.classMethod('close', { callback: true, promise: true, returns: [MongoClient] });
276
+
277
+ /**
278
+ * Logout user from server, fire off on all connections and remove all auth info
279
+ * @method
280
+ * @param {object} [options=null] Optional settings.
281
+ * @param {string} [options.dbName=null] Logout against different database than current.
282
+ * @param {Db~resultCallback} [callback] The command result callback
283
+ * @return {Promise} returns Promise if no callback passed
284
+ */
285
+ MongoClient.prototype.logout = function(options, callback) {
286
+ if (typeof options === 'function') (callback = options), (options = {});
287
+ options = options || {};
288
+
289
+ // Establish the correct database name
290
+ var dbName = this.s.options.authSource ? this.s.options.authSource : this.s.options.dbName;
291
+
292
+ return executeOperation(this, logout, [this, dbName, callback], {
293
+ skipSessions: true
294
+ });
295
+ };
296
+
297
+ const logout = (self, dbName, callback) => {
298
+ self.topology.logout(dbName, function(err) {
299
+ if (err) return callback(err);
300
+ callback(null, true);
301
+ });
302
+ };
303
+
304
+ define.classMethod('logout', { callback: true, promise: true });
305
+
306
+ /**
307
+ * Close the db and its underlying connections
308
+ * @method
309
+ * @param {boolean} force Force close, emitting no events
310
+ * @param {Db~noResultCallback} [callback] The result callback
311
+ * @return {Promise} returns Promise if no callback passed
312
+ */
313
+ MongoClient.prototype.close = function(force, callback) {
314
+ var self = this;
315
+ if (typeof force === 'function') (callback = force), (force = false);
316
+ // Close the topologu connection
317
+ this.topology.close(force);
318
+
319
+ // Emit close event
320
+ self.emit('close', self);
321
+
322
+ // Fire close event on any cached db instances
323
+ for (var name in this.s.dbCache) {
324
+ this.s.dbCache[name].emit('close');
325
+ }
326
+
327
+ // Remove listeners after emit
328
+ self.removeAllListeners('close');
329
+
330
+ // If we have sessions, we want to send a single `endSessions` command for them,
331
+ // and then individually clean them up. They will be removed from the internal state
332
+ // when they emit their `ended` events.
333
+ if (this.s.sessions.length) {
334
+ this.topology.endSessions(this.s.sessions);
335
+ this.s.sessions.forEach(session => session.endSession({ skipCommand: true }));
336
+ }
337
+
338
+ // Callback after next event loop tick
339
+ if (typeof callback === 'function')
340
+ return process.nextTick(function() {
341
+ handleCallback(callback, null);
342
+ });
343
+
344
+ // Return dummy promise
345
+ return new this.s.promiseLibrary(function(resolve) {
346
+ resolve();
347
+ });
348
+ };
349
+
350
+ define.classMethod('close', { callback: true, promise: true });
351
+
352
+ /**
353
+ * Create a new Db instance sharing the current socket connections. Be aware that the new db instances are
354
+ * related in a parent-child relationship to the original instance so that events are correctly emitted on child
355
+ * db instances. Child db instances are cached so performing db('db1') twice will return the same instance.
356
+ * You can control these behaviors with the options noListener and returnNonCachedInstance.
357
+ *
358
+ * @method
359
+ * @param {string} name The name of the database we want to use.
360
+ * @param {object} [options=null] Optional settings.
361
+ * @param {boolean} [options.noListener=false] Do not make the db an event listener to the original connection.
362
+ * @param {boolean} [options.returnNonCachedInstance=false] Control if you want to return a cached instance or have a new one created
363
+ * @return {Db}
364
+ */
365
+ MongoClient.prototype.db = function(dbName, options) {
366
+ options = options || {};
367
+
368
+ // Copy the options and add out internal override of the not shared flag
369
+ var finalOptions = Object.assign({}, this.s.options, options);
370
+
371
+ // Do we have the db in the cache already
372
+ if (this.s.dbCache[dbName] && finalOptions.returnNonCachedInstance !== true) {
373
+ return this.s.dbCache[dbName];
374
+ }
375
+
376
+ // Add promiseLibrary
377
+ finalOptions.promiseLibrary = this.s.promiseLibrary;
378
+
379
+ // If no topology throw an error message
380
+ if (!this.topology) {
381
+ throw new MongoError('MongoClient must be connected before calling MongoClient.prototype.db');
382
+ }
383
+
384
+ // Return the db object
385
+ var db = new Db(dbName, this.topology, finalOptions);
386
+
387
+ // Add the db to the cache
388
+ this.s.dbCache[dbName] = db;
389
+ // Return the database
390
+ return db;
391
+ };
392
+
393
+ /**
394
+ * Check if MongoClient is connected
395
+ *
396
+ * @method
397
+ * @param {string} name The name of the database we want to use.
398
+ * @param {object} [options=null] Optional settings.
399
+ * @param {boolean} [options.noListener=false] Do not make the db an event listener to the original connection.
400
+ * @param {boolean} [options.returnNonCachedInstance=false] Control if you want to return a cached instance or have a new one created
401
+ * @return {boolean}
402
+ */
403
+ MongoClient.prototype.isConnected = function(options) {
404
+ options = options || {};
405
+
406
+ if (!this.topology) return false;
407
+ return this.topology.isConnected(options);
408
+ };
153
409
 
154
410
  /**
155
411
  * Connect to MongoDB using a url as documented at
@@ -161,100 +417,109 @@ var define = MongoClient.define = new Define('MongoClient', MongoClient, false);
161
417
  * @method
162
418
  * @static
163
419
  * @param {string} url The connection URI string
164
- * @param {object} [options] Optional settings.
165
- * @param {number} [options.poolSize=5] poolSize The maximum size of the individual server pool.
420
+ * @param {object} [options] Optional settings
421
+ * @param {number} [options.poolSize=5] The maximum size of the individual server pool
166
422
  * @param {boolean} [options.ssl=false] Enable SSL connection.
167
- * @param {Buffer} [options.sslCA=undefined] SSL Certificate store binary buffer
168
- * @param {Buffer} [options.sslCRL=undefined] SSL Certificate revocation list binary buffer
169
- * @param {Buffer} [options.sslCert=undefined] SSL Certificate binary buffer
170
- * @param {Buffer} [options.sslKey=undefined] SSL Key file binary buffer
423
+ * @param {boolean} [options.sslValidate=true] Validate mongod server certificate against Certificate Authority
424
+ * @param {buffer} [options.sslCA=undefined] SSL Certificate store binary buffer
425
+ * @param {buffer} [options.sslCert=undefined] SSL Certificate binary buffer
426
+ * @param {buffer} [options.sslKey=undefined] SSL Key file binary buffer
171
427
  * @param {string} [options.sslPass=undefined] SSL Certificate pass phrase
172
- * @param {boolean|function} [options.checkServerIdentity=true] Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function.
428
+ * @param {buffer} [options.sslCRL=undefined] SSL Certificate revocation list binary buffer
173
429
  * @param {boolean} [options.autoReconnect=true] Enable autoReconnect for single server instances
174
430
  * @param {boolean} [options.noDelay=true] TCP Connection no delay
175
- * @param {number} [options.family=4] Version of IP stack. Defaults to 4.
176
- * @param {boolean} [options.keepAlive=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket.
431
+ * @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
432
+ * @param {boolean} [options.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
177
433
  * @param {number} [options.connectTimeoutMS=30000] TCP Connection timeout setting
434
+ * @param {number} [options.family=null] Version of IP stack. Can be 4, 6 or null (default).
435
+ * If null, will attempt to connect with IPv6, and will fall back to IPv4 on failure
178
436
  * @param {number} [options.socketTimeoutMS=360000] TCP Socket timeout setting
179
437
  * @param {number} [options.reconnectTries=30] Server attempt to reconnect #times
180
438
  * @param {number} [options.reconnectInterval=1000] Server will wait # milliseconds between retries
181
- * @param {boolean} [options.ha=true] Control if high availability monitoring runs for Replicaset or Mongos proxies.
439
+ * @param {boolean} [options.ha=true] Control if high availability monitoring runs for Replicaset or Mongos proxies
182
440
  * @param {number} [options.haInterval=10000] The High availability period for replicaset inquiry
183
441
  * @param {string} [options.replicaSet=undefined] The Replicaset set name
184
442
  * @param {number} [options.secondaryAcceptableLatencyMS=15] Cutoff latency point in MS for Replicaset member selection
185
- * @param {number} [options.acceptableLatencyMS=15] Cutoff latency point in MS for Mongos proxies selection.
443
+ * @param {number} [options.acceptableLatencyMS=15] Cutoff latency point in MS for Mongos proxies selection
186
444
  * @param {boolean} [options.connectWithNoPrimary=false] Sets if the driver should connect even if no primary is available
187
445
  * @param {string} [options.authSource=undefined] Define the database to authenticate against
188
- * @param {string} [options.auth.user=undefined] The username for auth
189
- * @param {string} [options.auth.password=undefined] The password for auth
190
- * @param {(number|string)} [options.w=null] The write concern.
191
- * @param {number} [options.wtimeout=null] The write concern timeout.
192
- * @param {boolean} [options.j=false] Specify a journal write concern.
193
- * @param {boolean} [options.forceServerObjectId=false] Force server to assign _id values instead of driver.
194
- * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
195
- * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
196
- * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
197
- * @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
198
- * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
199
- * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
200
- * @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.
201
- * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
202
- * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
203
- * @param {object} [options.pkFactory=null] A primary key factory object for generation of custom _id keys.
446
+ * @param {(number|string)} [options.w=null] The write concern
447
+ * @param {number} [options.wtimeout=null] The write concern timeout
448
+ * @param {boolean} [options.j=false] Specify a journal write concern
449
+ * @param {boolean} [options.forceServerObjectId=false] Force server to assign _id values instead of driver
450
+ * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object
451
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields
452
+ * @param {boolean} [options.raw=false] Return document results as raw BSON buffers
453
+ * @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
454
+ * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST)
455
+ * @param {object} [options.pkFactory=null] A primary key factory object for generation of custom _id keys
204
456
  * @param {object} [options.promiseLibrary=null] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
205
- * @param {object} [options.readConcern=null] Specify a read concern for the collection. (only MongoDB 3.2 or higher supported)
457
+ * @param {object} [options.readConcern=null] Specify a read concern for the collection (only MongoDB 3.2 or higher supported)
206
458
  * @param {string} [options.readConcern.level='local'] Specify a read concern level for the collection operations, one of [local|majority]. (only MongoDB 3.2 or higher supported)
207
- * @param {number} [options.maxStalenessSeconds=undefined] The max staleness to secondary reads (values under 10 seconds cannot be guaranteed);
208
- * @param {string} [options.appname=undefined] The name of the application that created this MongoClient instance. MongoDB 3.4 and newer will print this value in the server log upon establishing each connection. It is also recorded in the slow query log and profile collections.
459
+ * @param {number} [options.maxStalenessSeconds=undefined] The max staleness to secondary reads (values under 10 seconds cannot be guaranteed)
209
460
  * @param {string} [options.loggerLevel=undefined] The logging level (error/warn/info/debug)
210
461
  * @param {object} [options.logger=undefined] Custom logger object
211
- * @param {object} [options.validateOptions=false] Validate MongoClient passed in options for correctness.
462
+ * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types
463
+ * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers
464
+ * @param {boolean} [options.promoteLongs=true] Promotes long values to number if they fit inside the 53 bits resolution
465
+ * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit
466
+ * @param {boolean|function} [options.checkServerIdentity=true] Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function
467
+ * @param {object} [options.validateOptions=false] Validate MongoClient passed in options for correctness
468
+ * @param {string} [options.appname=undefined] The name of the application that created this MongoClient instance. MongoDB 3.4 and newer will print this value in the server log upon establishing each connection. It is also recorded in the slow query log and profile collections
469
+ * @param {string} [options.auth.user=undefined] The username for auth
470
+ * @param {string} [options.auth.password=undefined] The password for auth
471
+ * @param {string} [options.authMechanism=undefined] Mechanism for authentication: MDEFAULT, GSSAPI, PLAIN, MONGODB-X509, SCRAM-SHA-1 or MONGODB-CR
472
+ * @param {object} [options.compression=null] Type of compression to use: snappy or zlib
473
+ * @param {boolean} [options.fsync=false] Specify a file sync write concern
474
+ * @param {array} [options.readPreferenceTags=null] Read preference tags
475
+ * @param {number} [options.numberOfRetries=5] The number of retries for a tailable cursor
476
+ * @param {boolean} [options.auto_reconnect=true] Enable auto reconnecting for single server instances
212
477
  * @param {MongoClient~connectCallback} [callback] The command result callback
213
- * @return {Promise} returns Promise if no callback passed
478
+ * @return {Promise<MongoClient>} returns Promise if no callback passed
214
479
  */
215
480
  MongoClient.connect = function(url, options, callback) {
216
481
  var args = Array.prototype.slice.call(arguments, 1);
217
- callback = typeof args[args.length - 1] == 'function' ? args.pop() : null;
482
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
218
483
  options = args.length ? args.shift() : null;
219
484
  options = options || {};
220
- var self = this;
221
485
 
222
- // Validate options object
223
- var err = validOptions(options);
486
+ // Create client
487
+ var mongoClient = new MongoClient(url, options);
488
+ // Execute the connect method
489
+ return mongoClient.connect(callback);
490
+ };
224
491
 
225
- // Get the promiseLibrary
226
- var promiseLibrary = options.promiseLibrary;
492
+ define.staticMethod('connect', { callback: true, promise: true });
227
493
 
228
- // No promise library selected fall back
229
- if(!promiseLibrary) {
230
- promiseLibrary = typeof global.Promise == 'function' ?
231
- global.Promise : require('es6-promise').Promise;
494
+ /**
495
+ * Starts a new session on the server
496
+ *
497
+ * @param {object} [options] optional settings for a driver session
498
+ * @param {MongoClient~sessionCallback} [callback] The callback called with a newly establish session, or an error if one occurred
499
+ * @return {Promise} if no callback is specified, a promise will be returned for the newly established session
500
+ */
501
+ MongoClient.prototype.startSession = function(options) {
502
+ options = options || {};
503
+ if (!this.topology) {
504
+ throw new MongoError('Must connect to a server before calling this method');
232
505
  }
233
506
 
234
- // Return a promise
235
- if(typeof callback != 'function') {
236
- return new promiseLibrary(function(resolve, reject) {
237
- // Did we have a validation error
238
- if(err) return reject(err);
239
- // Attempt to connect
240
- connect(self, url, options, function(err, db) {
241
- if(err) return reject(err);
242
- resolve(db);
243
- });
244
- });
507
+ if (!this.topology.hasSessionSupport()) {
508
+ throw new MongoError('Current topology does not support sessions');
245
509
  }
246
510
 
247
- // Did we have a validation error
248
- if(err) return callback(err);
249
- // Fallback to callback based connect
250
- connect(self, url, options, callback);
251
- }
511
+ const session = this.topology.startSession(options);
512
+ session.once('ended', () => {
513
+ this.s.sessions = this.s.sessions.filter(s => s.equals(session));
514
+ });
252
515
 
253
- define.staticMethod('connect', {callback: true, promise:true});
516
+ this.s.sessions.push(session);
517
+ return session;
518
+ };
254
519
 
255
520
  var mergeOptions = function(target, source, flatten) {
256
- for(var name in source) {
257
- if(source[name] && typeof source[name] == 'object' && flatten) {
521
+ for (var name in source) {
522
+ if (source[name] && typeof source[name] === 'object' && flatten) {
258
523
  target = mergeOptions(target, source[name], flatten);
259
524
  } else {
260
525
  target[name] = source[name];
@@ -262,20 +527,33 @@ var mergeOptions = function(target, source, flatten) {
262
527
  }
263
528
 
264
529
  return target;
265
- }
530
+ };
266
531
 
267
532
  var createUnifiedOptions = function(finalOptions, options) {
268
- var childOptions = ['mongos', 'server', 'db'
269
- , 'replset', 'db_options', 'server_options', 'rs_options', 'mongos_options'];
270
- var noMerge = ['readconcern'];
271
-
272
- for(var name in options) {
273
- if(noMerge.indexOf(name.toLowerCase()) != -1) {
533
+ var childOptions = [
534
+ 'mongos',
535
+ 'server',
536
+ 'db',
537
+ 'replset',
538
+ 'db_options',
539
+ 'server_options',
540
+ 'rs_options',
541
+ 'mongos_options'
542
+ ];
543
+ var noMerge = ['readconcern', 'compression'];
544
+
545
+ for (var name in options) {
546
+ if (noMerge.indexOf(name.toLowerCase()) !== -1) {
274
547
  finalOptions[name] = options[name];
275
- } else if(childOptions.indexOf(name.toLowerCase()) != -1) {
548
+ } else if (childOptions.indexOf(name.toLowerCase()) !== -1) {
276
549
  finalOptions = mergeOptions(finalOptions, options[name], false);
277
550
  } else {
278
- if(options[name] && typeof options[name] == 'object' && !Buffer.isBuffer(options[name]) && !Array.isArray(options[name])) {
551
+ if (
552
+ options[name] &&
553
+ typeof options[name] === 'object' &&
554
+ !Buffer.isBuffer(options[name]) &&
555
+ !Array.isArray(options[name])
556
+ ) {
279
557
  finalOptions = mergeOptions(finalOptions, options[name], true);
280
558
  } else {
281
559
  finalOptions[name] = options[name];
@@ -284,50 +562,69 @@ var createUnifiedOptions = function(finalOptions, options) {
284
562
  }
285
563
 
286
564
  return finalOptions;
287
- }
565
+ };
288
566
 
289
567
  function translateOptions(options) {
290
568
  // If we have a readPreference passed in by the db options
291
- if(typeof options.readPreference == 'string' || typeof options.read_preference == 'string') {
569
+ if (typeof options.readPreference === 'string' || typeof options.read_preference === 'string') {
292
570
  options.readPreference = new ReadPreference(options.readPreference || options.read_preference);
293
571
  }
294
572
 
295
573
  // Do we have readPreference tags, add them
296
- if(options.readPreference && (options.readPreferenceTags || options.read_preference_tags)) {
574
+ if (options.readPreference && (options.readPreferenceTags || options.read_preference_tags)) {
297
575
  options.readPreference.tags = options.readPreferenceTags || options.read_preference_tags;
298
576
  }
299
577
 
300
578
  // Do we have maxStalenessSeconds
301
- if(options.maxStalenessSeconds) {
579
+ if (options.maxStalenessSeconds) {
302
580
  options.readPreference.maxStalenessSeconds = options.maxStalenessSeconds;
303
581
  }
304
582
 
305
583
  // Set the socket and connection timeouts
306
- if(options.socketTimeoutMS == null) options.socketTimeoutMS = 360000;
307
- if(options.connectTimeoutMS == null) options.connectTimeoutMS = 30000;
584
+ if (options.socketTimeoutMS == null) options.socketTimeoutMS = 360000;
585
+ if (options.connectTimeoutMS == null) options.connectTimeoutMS = 30000;
308
586
 
309
587
  // Create server instances
310
588
  return options.servers.map(function(serverObj) {
311
- return serverObj.domain_socket ?
312
- new Server(serverObj.domain_socket, 27017, options)
313
- : new Server(serverObj.host, serverObj.port, options);
589
+ return serverObj.domain_socket
590
+ ? new Server(serverObj.domain_socket, 27017, options)
591
+ : new Server(serverObj.host, serverObj.port, options);
314
592
  });
315
593
  }
316
594
 
595
+ var events = [
596
+ 'timeout',
597
+ 'close',
598
+ 'serverOpening',
599
+ 'serverDescriptionChanged',
600
+ 'serverHeartbeatStarted',
601
+ 'serverHeartbeatSucceeded',
602
+ 'serverHeartbeatFailed',
603
+ 'serverClosed',
604
+ 'topologyOpening',
605
+ 'topologyClosed',
606
+ 'topologyDescriptionChanged',
607
+ 'joined',
608
+ 'left',
609
+ 'ping',
610
+ 'ha',
611
+ 'all',
612
+ 'fullsetup'
613
+ ];
614
+
317
615
  //
318
616
  // Collect all events in order from SDAM
319
617
  //
320
- function collectEvents(self, db) {
618
+ function collectEvents(self, topology) {
321
619
  var collectedEvents = [];
322
620
 
323
- if(self instanceof MongoClient) {
324
- var events = ["timeout", "close", 'serverOpening', 'serverDescriptionChanged', 'serverHeartbeatStarted',
325
- 'serverHeartbeatSucceeded', 'serverHeartbeatFailed', 'serverClosed', 'topologyOpening',
326
- 'topologyClosed', 'topologyDescriptionChanged', 'joined', 'left', 'ping', 'ha', 'all', 'fullsetup'];
621
+ if (self instanceof MongoClient) {
327
622
  events.forEach(function(event) {
328
- db.serverConfig.on(event, function(object1, object2) {
623
+ topology.on(event, function(object1, object2) {
329
624
  collectedEvents.push({
330
- event: event, object1: object1, object2: object2
625
+ event: event,
626
+ object1: object1,
627
+ object2: object2
331
628
  });
332
629
  });
333
630
  });
@@ -336,135 +633,213 @@ function collectEvents(self, db) {
336
633
  return collectedEvents;
337
634
  }
338
635
 
636
+ //
637
+ // Clear out all event
638
+ //
639
+ function clearAllEvents(topology) {
640
+ events.forEach(function(event) {
641
+ topology.removeAllListeners(event);
642
+ });
643
+ }
644
+
339
645
  //
340
646
  // Replay any events due to single server connection switching to Mongos
341
647
  //
342
648
  function replayEvents(self, events) {
343
- for(var i = 0; i < events.length; i++) {
649
+ for (var i = 0; i < events.length; i++) {
344
650
  self.emit(events[i].event, events[i].object1, events[i].object2);
345
651
  }
346
652
  }
347
653
 
348
- function relayEvents(self, db) {
349
- if(self instanceof MongoClient) {
350
- var events = ["timeout", "close", 'serverOpening', 'serverDescriptionChanged', 'serverHeartbeatStarted',
351
- 'serverHeartbeatSucceeded', 'serverHeartbeatFailed', 'serverClosed', 'topologyOpening',
352
- 'topologyClosed', 'topologyDescriptionChanged', 'joined', 'left', 'ping', 'ha', 'all', 'fullsetup'];
353
- events.forEach(function(event) {
354
- db.serverConfig.on(event, function(object1, object2) {
355
- self.emit(event, object1, object2);
356
- });
654
+ function relayEvents(self, topology) {
655
+ var events = [
656
+ 'serverOpening',
657
+ 'serverDescriptionChanged',
658
+ 'serverHeartbeatStarted',
659
+ 'serverHeartbeatSucceeded',
660
+ 'serverHeartbeatFailed',
661
+ 'serverClosed',
662
+ 'topologyOpening',
663
+ 'topologyClosed',
664
+ 'topologyDescriptionChanged',
665
+ 'joined',
666
+ 'left',
667
+ 'ping',
668
+ 'ha'
669
+ ];
670
+ events.forEach(function(event) {
671
+ topology.on(event, function(object1, object2) {
672
+ self.emit(event, object1, object2);
357
673
  });
358
- }
359
- }
360
-
361
- function createReplicaset(self, options, callback) {
362
- // Set default options
363
- var servers = translateOptions(options);
364
- // Create Db instance
365
- var db = new Db(options.dbName, new ReplSet(servers, options), options);
366
- // Propegate the events to the client
367
- relayEvents(self, db);
368
- // Open the connection
369
- db.open(callback);
674
+ });
370
675
  }
371
676
 
372
- function createMongos(self, options, callback) {
373
- // Set default options
374
- var servers = translateOptions(options);
375
- // Create Db instance
376
- var db = new Db(options.dbName, new Mongos(servers, options), options)
377
- // Propegate the events to the client
378
- relayEvents(self, db);
379
- // Open the connection
380
- db.open(callback);
677
+ function assignTopology(client, topology) {
678
+ client.topology = topology;
679
+ topology.s.sessionPool = new ServerSessionPool(topology.s.coreTopology);
381
680
  }
382
681
 
383
682
  function createServer(self, options, callback) {
683
+ // Pass in the promise library
684
+ options.promiseLibrary = self.s.promiseLibrary;
685
+
384
686
  // Set default options
385
687
  var servers = translateOptions(options);
386
- // Create db instance
387
- var db = new Db(options.dbName, servers[0], options);
388
- // Propegate the events to the client
389
- var collectedEvents = collectEvents(self, db);
390
- // Create Db instance
391
- db.open(function(err, db) {
392
- if(err) return callback(err);
688
+
689
+ // Propagate the events to the client
690
+ var collectedEvents = collectEvents(self, servers[0]);
691
+
692
+ // Connect to topology
693
+ servers[0].connect(function(err, topology) {
694
+ if (err) return callback(err);
695
+ // Clear out all the collected event listeners
696
+ clearAllEvents(servers[0]);
697
+ // Relay all the events
698
+ relayEvents(self, servers[0]);
699
+ // Add listeners
700
+ addListeners(self, servers[0]);
393
701
  // Check if we are really speaking to a mongos
394
- var ismaster = db.serverConfig.lastIsMaster();
702
+ var ismaster = topology.lastIsMaster();
703
+
704
+ // Set the topology
705
+ assignTopology(self, topology);
395
706
 
396
707
  // Do we actually have a mongos
397
- if(ismaster && ismaster.msg == 'isdbgrid') {
708
+ if (ismaster && ismaster.msg === 'isdbgrid') {
398
709
  // Destroy the current connection
399
- db.close();
710
+ topology.close();
400
711
  // Create mongos connection instead
401
712
  return createMongos(self, options, callback);
402
713
  }
403
714
 
404
715
  // Fire all the events
405
716
  replayEvents(self, collectedEvents);
406
- // Propegate the events to the client
407
- relayEvents(self, db);
408
717
  // Otherwise callback
409
- callback(err, db);
718
+ callback(err, topology);
410
719
  });
411
720
  }
412
721
 
413
- function connectHandler(options, callback) {
414
- return function (err, db) {
415
- if(err) {
722
+ function createReplicaset(self, options, callback) {
723
+ // Pass in the promise library
724
+ options.promiseLibrary = self.s.promiseLibrary;
725
+
726
+ // Set default options
727
+ var servers = translateOptions(options);
728
+
729
+ // Create the topology
730
+ var topology = new ReplSet(servers, options);
731
+
732
+ // Add listeners
733
+ addListeners(self, topology);
734
+
735
+ // Propagate the events to the client
736
+ relayEvents(self, topology);
737
+
738
+ // Open the connection
739
+ topology.connect(options, function(err, topology) {
740
+ if (err) return callback(err);
741
+
742
+ assignTopology(self, topology);
743
+ callback(null, topology);
744
+ });
745
+ }
746
+
747
+ function createMongos(self, options, callback) {
748
+ // Pass in the promise library
749
+ options.promiseLibrary = self.s.promiseLibrary;
750
+
751
+ // Set default options
752
+ var servers = translateOptions(options);
753
+
754
+ // Create the topology
755
+ var topology = new Mongos(servers, options);
756
+
757
+ // Add listeners
758
+ addListeners(self, topology);
759
+
760
+ // Propagate the events to the client
761
+ relayEvents(self, topology);
762
+
763
+ // Open the connection
764
+ topology.connect(options, function(err, topology) {
765
+ if (err) return callback(err);
766
+
767
+ assignTopology(self, topology);
768
+ callback(null, topology);
769
+ });
770
+ }
771
+
772
+ function createListener(self, event) {
773
+ return function(v1, v2) {
774
+ if (event === 'open' || event === 'fullsetup' || event === 'all' || event === 'reconnect') {
775
+ return self.emit(event, self);
776
+ }
777
+
778
+ self.emit(event, v1, v2);
779
+ };
780
+ }
781
+
782
+ function addListeners(self, topology) {
783
+ topology.on('authenticated', createListener(self, 'authenticated'));
784
+ topology.on('error', createListener(self, 'error'));
785
+ topology.on('timeout', createListener(self, 'timeout'));
786
+ topology.on('close', createListener(self, 'close'));
787
+ topology.on('parseError', createListener(self, 'parseError'));
788
+ topology.once('open', createListener(self, 'open'));
789
+ topology.once('fullsetup', createListener(self, 'fullsetup'));
790
+ topology.once('all', createListener(self, 'all'));
791
+ topology.on('reconnect', createListener(self, 'reconnect'));
792
+ }
793
+
794
+ function connectHandler(client, options, callback) {
795
+ return function(err, topology) {
796
+ if (err) {
416
797
  return process.nextTick(function() {
417
798
  try {
418
799
  callback(err, null);
419
800
  } catch (err) {
420
- if(db) db.close();
421
- throw err
801
+ if (topology) topology.close();
802
+ throw err;
422
803
  }
423
804
  });
424
805
  }
425
806
 
426
807
  // No authentication just reconnect
427
- if(!options.auth) {
808
+ if (!options.auth) {
428
809
  return process.nextTick(function() {
429
810
  try {
430
- callback(err, db);
811
+ callback(err, topology);
431
812
  } catch (err) {
432
- if(db) db.close();
433
- throw err
813
+ if (topology) topology.close();
814
+ throw err;
434
815
  }
435
- })
436
- }
437
-
438
- // What db to authenticate against
439
- var authentication_db = db;
440
- if(options.authSource) {
441
- authentication_db = db.db(options.authSource);
816
+ });
442
817
  }
443
818
 
444
819
  // Authenticate
445
- authenticate(authentication_db, options.user, options.password, options, function(err, success) {
446
- if(success){
820
+ authenticate(client, options.user, options.password, options, function(err, success) {
821
+ if (success) {
447
822
  process.nextTick(function() {
448
823
  try {
449
- callback(null, db);
824
+ callback(null, topology);
450
825
  } catch (err) {
451
- if(db) db.close();
452
- throw err
826
+ if (topology) topology.close();
827
+ throw err;
453
828
  }
454
829
  });
455
830
  } else {
456
- if(db) db.close();
831
+ if (topology) topology.close();
457
832
  process.nextTick(function() {
458
833
  try {
459
834
  callback(err ? err : new Error('Could not authenticate user ' + options.auth[0]), null);
460
835
  } catch (err) {
461
- if(db) db.close();
462
- throw err
836
+ if (topology) topology.close();
837
+ throw err;
463
838
  }
464
839
  });
465
840
  }
466
841
  });
467
- }
842
+ };
468
843
  }
469
844
 
470
845
  /*
@@ -475,14 +850,39 @@ var connect = function(self, url, options, callback) {
475
850
  options = shallowClone(options);
476
851
 
477
852
  // If callback is null throw an exception
478
- if(callback == null) {
479
- throw new Error("no callback function provided");
853
+ if (callback == null) {
854
+ throw new Error('no callback function provided');
480
855
  }
481
856
 
482
857
  // Get a logger for MongoClient
483
858
  var logger = Logger('MongoClient', options);
484
859
 
860
+ // Did we pass in a Server/ReplSet/Mongos
861
+ if (url instanceof Server || url instanceof ReplSet || url instanceof Mongos) {
862
+ // Set the topology
863
+ assignTopology(self, url);
864
+
865
+ // Add listeners
866
+ addListeners(self, url);
867
+ // Connect
868
+ return url.connect(
869
+ options,
870
+ connectHandler(self, options, function(err, topology) {
871
+ if (err) return connectCallback(err, topology);
872
+ if (options.user || options.password || options.authMechanism) {
873
+ return authenticate(self, options.user, options.password, options, function(err) {
874
+ if (err) return connectCallback(err, topology);
875
+ connectCallback(err, topology);
876
+ });
877
+ }
878
+
879
+ connectCallback(err, topology);
880
+ })
881
+ );
882
+ }
883
+
485
884
  parse(url, options, function(err, object) {
885
+ // Do not attempt to connect if parsing error
486
886
  if (err) return callback(err);
487
887
 
488
888
  // Parse the string
@@ -491,41 +891,64 @@ var connect = function(self, url, options, callback) {
491
891
  _finalOptions = createUnifiedOptions(_finalOptions, options);
492
892
 
493
893
  // Check if we have connection and socket timeout set
494
- if(_finalOptions.socketTimeoutMS == null) _finalOptions.socketTimeoutMS = 360000;
495
- if(_finalOptions.connectTimeoutMS == null) _finalOptions.connectTimeoutMS = 30000;
894
+ if (_finalOptions.socketTimeoutMS == null) _finalOptions.socketTimeoutMS = 360000;
895
+ if (_finalOptions.connectTimeoutMS == null) _finalOptions.connectTimeoutMS = 30000;
496
896
 
497
897
  if (_finalOptions.db_options && _finalOptions.db_options.auth) {
498
898
  delete _finalOptions.db_options.auth;
499
899
  }
500
900
 
901
+ // Store the merged options object
902
+ self.s.options = _finalOptions;
903
+
501
904
  // Failure modes
502
- if(object.servers.length == 0) {
503
- throw new Error("connection string must contain at least one seed host");
905
+ if (object.servers.length === 0) {
906
+ return callback(new Error('connection string must contain at least one seed host'));
504
907
  }
505
908
 
506
909
  // Do we have a replicaset then skip discovery and go straight to connectivity
507
- if(_finalOptions.replicaSet || _finalOptions.rs_name) {
508
- return createReplicaset(self, _finalOptions, connectHandler(_finalOptions, connectCallback));
509
- } else if(object.servers.length > 1) {
510
- return createMongos(self, _finalOptions, connectHandler(_finalOptions, connectCallback));
910
+ if (_finalOptions.replicaSet || _finalOptions.rs_name) {
911
+ return createReplicaset(
912
+ self,
913
+ _finalOptions,
914
+ connectHandler(self, _finalOptions, connectCallback)
915
+ );
916
+ } else if (object.servers.length > 1) {
917
+ return createMongos(
918
+ self,
919
+ _finalOptions,
920
+ connectHandler(self, _finalOptions, connectCallback)
921
+ );
511
922
  } else {
512
- return createServer(self, _finalOptions, connectHandler(_finalOptions, connectCallback));
923
+ return createServer(
924
+ self,
925
+ _finalOptions,
926
+ connectHandler(self, _finalOptions, connectCallback)
927
+ );
513
928
  }
514
929
  });
515
930
 
516
- function connectCallback(err, db) {
517
- if(err && err.message == 'no mongos proxies found in seed list') {
518
- if(logger.isWarn()) {
519
- logger.warn(f('seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name'));
931
+ function connectCallback(err, topology) {
932
+ if (err && err.message === 'no mongos proxies found in seed list') {
933
+ if (logger.isWarn()) {
934
+ logger.warn(
935
+ f(
936
+ 'seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name'
937
+ )
938
+ );
520
939
  }
521
940
 
522
941
  // Return a more specific error message for MongoClient.connect
523
- return callback(new MongoError('seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name'));
942
+ return callback(
943
+ new MongoError(
944
+ 'seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name'
945
+ )
946
+ );
524
947
  }
525
948
 
526
949
  // Return the error and db instance
527
- callback(err, db);
950
+ callback(err, topology);
528
951
  }
529
- }
952
+ };
530
953
 
531
- module.exports = MongoClient
954
+ module.exports = MongoClient;