mongodb 3.3.2 → 3.3.3

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.
package/lib/collection.js CHANGED
@@ -260,7 +260,7 @@ const DEPRECATED_FIND_OPTIONS = ['maxScan', 'fields', 'snapshot'];
260
260
  * @param {boolean} [options.snapshot=false] DEPRECATED: Snapshot query.
261
261
  * @param {boolean} [options.timeout=false] Specify if the cursor can timeout.
262
262
  * @param {boolean} [options.tailable=false] Specify if the cursor is tailable.
263
- * @param {number} [options.batchSize=0] Set the batchSize for the getMoreCommand when iterating over the query results.
263
+ * @param {number} [options.batchSize=1000] Set the batchSize for the getMoreCommand when iterating over the query results.
264
264
  * @param {boolean} [options.returnKey=false] Only return the index key.
265
265
  * @param {number} [options.maxScan] DEPRECATED: Limit the number of items to scan.
266
266
  * @param {number} [options.min] Set index bounds.
@@ -667,7 +667,7 @@ Collection.prototype.insert = deprecate(function(docs, options, callback) {
667
667
  * @property {Object} upsertedId The upserted id.
668
668
  * @property {ObjectId} upsertedId._id The upserted _id returned from the server.
669
669
  * @property {Object} message
670
- * @property {Array} ops
670
+ * @property {object[]} [ops] In a response to {@link Collection#replaceOne replaceOne}, contains the new value of the document on the server. This is the same document that was originally passed in, and is only here for legacy purposes.
671
671
  */
672
672
 
673
673
  /**
@@ -729,7 +729,7 @@ Collection.prototype.updateOne = function(filter, update, options, callback) {
729
729
  * @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
730
730
  * @param {ClientSession} [options.session] optional session to use for this operation
731
731
  * @param {Collection~updateWriteOpCallback} [callback] The command result callback
732
- * @return {Promise<Collection~updatewriteOpResultObject>} returns Promise if no callback passed
732
+ * @return {Promise<Collection~updateWriteOpResult>} returns Promise if no callback passed
733
733
  */
734
734
  Collection.prototype.replaceOne = function(filter, doc, options, callback) {
735
735
  if (typeof options === 'function') (callback = options), (options = {});
@@ -759,7 +759,7 @@ Collection.prototype.replaceOne = function(filter, doc, options, callback) {
759
759
  * @param {Array} [options.arrayFilters] optional list of array filters referenced in filtered positional operators
760
760
  * @param {ClientSession} [options.session] optional session to use for this operation
761
761
  * @param {Collection~updateWriteOpCallback} [callback] The command result callback
762
- * @return {Promise<Collection~updateWriteOpResultObject>} returns Promise if no callback passed
762
+ * @return {Promise<Collection~updateWriteOpResult>} returns Promise if no callback passed
763
763
  */
764
764
  Collection.prototype.updateMany = function(filter, update, options, callback) {
765
765
  if (typeof options === 'function') (callback = options), (options = {});
@@ -985,7 +985,7 @@ Collection.prototype.save = deprecate(function(doc, options, callback) {
985
985
  * @param {boolean} [options.snapshot=false] DEPRECATED: Snapshot query.
986
986
  * @param {boolean} [options.timeout=false] Specify if the cursor can timeout.
987
987
  * @param {boolean} [options.tailable=false] Specify if the cursor is tailable.
988
- * @param {number} [options.batchSize=0] Set the batchSize for the getMoreCommand when iterating over the query results.
988
+ * @param {number} [options.batchSize=1] Set the batchSize for the getMoreCommand when iterating over the query results.
989
989
  * @param {boolean} [options.returnKey=false] Only return the index key.
990
990
  * @param {number} [options.maxScan] DEPRECATED: Limit the number of items to scan.
991
991
  * @param {number} [options.min] Set index bounds.
@@ -1119,7 +1119,7 @@ Collection.prototype.isCapped = function(options, callback) {
1119
1119
  /**
1120
1120
  * Creates an index on the db and collection collection.
1121
1121
  * @method
1122
- * @param {(string|object)} fieldOrSpec Defines the index.
1122
+ * @param {(string|array|object)} fieldOrSpec Defines the index.
1123
1123
  * @param {object} [options] Optional settings.
1124
1124
  * @param {(number|string)} [options.w] The write concern.
1125
1125
  * @param {number} [options.wtimeout] The write concern timeout.
@@ -1138,6 +1138,25 @@ Collection.prototype.isCapped = function(options, callback) {
1138
1138
  * @param {ClientSession} [options.session] optional session to use for this operation
1139
1139
  * @param {Collection~resultCallback} [callback] The command result callback
1140
1140
  * @return {Promise} returns Promise if no callback passed
1141
+ * @example
1142
+ * const collection = client.db('foo').collection('bar');
1143
+ *
1144
+ * await collection.createIndex({ a: 1, b: -1 });
1145
+ *
1146
+ * // Alternate syntax for { c: 1, d: -1 } that ensures order of indexes
1147
+ * await collection.createIndex([ [c, 1], [d, -1] ]);
1148
+ *
1149
+ * // Equivalent to { e: 1 }
1150
+ * await collection.createIndex('e');
1151
+ *
1152
+ * // Equivalent to { f: 1, g: 1 }
1153
+ * await collection.createIndex(['f', 'g'])
1154
+ *
1155
+ * // Equivalent to { h: 1, i: -1 }
1156
+ * await collection.createIndex([ { h: 1 }, { i: -1 } ]);
1157
+ *
1158
+ * // Equivalent to { j: 1, k: -1, l: 2d }
1159
+ * await collection.createIndex(['j', ['k', -1], { l: '2d' }])
1141
1160
  */
1142
1161
  Collection.prototype.createIndex = function(fieldOrSpec, options, callback) {
1143
1162
  if (typeof options === 'function') (callback = options), (options = {});
@@ -1153,16 +1172,43 @@ Collection.prototype.createIndex = function(fieldOrSpec, options, callback) {
1153
1172
  return executeOperation(this.s.topology, createIndexOperation, callback);
1154
1173
  };
1155
1174
 
1175
+ /**
1176
+ * @typedef {object} Collection~IndexDefinition
1177
+ * @description A definition for an index. Used by the createIndex command.
1178
+ * @see https://docs.mongodb.com/manual/reference/command/createIndexes/
1179
+ */
1180
+
1156
1181
  /**
1157
1182
  * Creates multiple indexes in the collection, this method is only supported for
1158
1183
  * MongoDB 2.6 or higher. Earlier version of MongoDB will throw a command not supported
1159
- * error. Index specifications are defined at http://docs.mongodb.org/manual/reference/command/createIndexes/.
1184
+ * error.
1185
+ *
1186
+ * **Note**: Unlike {@link Collection#createIndex createIndex}, this function takes in raw index specifications.
1187
+ * Index specifications are defined {@link http://docs.mongodb.org/manual/reference/command/createIndexes/ here}.
1188
+ *
1160
1189
  * @method
1161
- * @param {array} indexSpecs An array of index specifications to be created
1190
+ * @param {Collection~IndexDefinition[]} indexSpecs An array of index specifications to be created
1162
1191
  * @param {Object} [options] Optional settings
1163
1192
  * @param {ClientSession} [options.session] optional session to use for this operation
1164
1193
  * @param {Collection~resultCallback} [callback] The command result callback
1165
1194
  * @return {Promise} returns Promise if no callback passed
1195
+ * @example
1196
+ * const collection = client.db('foo').collection('bar');
1197
+ * await collection.createIndexes([
1198
+ * // Simple index on field fizz
1199
+ * {
1200
+ * key: { fizz: 1 },
1201
+ * }
1202
+ * // wildcard index
1203
+ * {
1204
+ * key: { '$**': 1 }
1205
+ * },
1206
+ * // named index on darmok and jalad
1207
+ * {
1208
+ * key: { darmok: 1, jalad: -1 }
1209
+ * name: 'tanagra'
1210
+ * }
1211
+ * ]);
1166
1212
  */
1167
1213
  Collection.prototype.createIndexes = function(indexSpecs, options, callback) {
1168
1214
  if (typeof options === 'function') (callback = options), (options = {});
@@ -1256,7 +1302,7 @@ Collection.prototype.reIndex = function(options, callback) {
1256
1302
  *
1257
1303
  * @method
1258
1304
  * @param {object} [options] Optional settings.
1259
- * @param {number} [options.batchSize] The batchSize for the returned command cursor or if pre 2.8 the systems batch collection
1305
+ * @param {number} [options.batchSize=1000] The batchSize for the returned command cursor or if pre 2.8 the systems batch collection
1260
1306
  * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
1261
1307
  * @param {ClientSession} [options.session] optional session to use for this operation
1262
1308
  * @return {CommandCursor}
@@ -1355,7 +1401,12 @@ Collection.prototype.indexInformation = function(options, callback) {
1355
1401
  */
1356
1402
 
1357
1403
  /**
1358
- * Count number of matching documents in the db to a query.
1404
+ * An estimated count of matching documents in the db to a query.
1405
+ *
1406
+ * **NOTE:** This method has been deprecated, since it does not provide an accurate count of the documents
1407
+ * in a collection. To obtain an accurate count of documents in the collection, use {@link Collection#countDocuments countDocuments}.
1408
+ * To obtain an estimated count of all documents in the collection, use {@link Collection#estimatedDocumentCount estimatedDocumentCount}.
1409
+ *
1359
1410
  * @method
1360
1411
  * @param {object} [query={}] The query for the count.
1361
1412
  * @param {object} [options] Optional settings.
@@ -1510,7 +1561,7 @@ Collection.prototype.stats = function(options, callback) {
1510
1561
  /**
1511
1562
  * @typedef {Object} Collection~findAndModifyWriteOpResult
1512
1563
  * @property {object} value Document returned from the `findAndModify` command. If no documents were found, `value` will be `null` by default (`returnOriginal: true`), even if a document was upserted; if `returnOriginal` was false, the upserted document will be returned in that case.
1513
- * @property {object} lastErrorObject The raw lastErrorObject returned from the command.
1564
+ * @property {object} lastErrorObject The raw lastErrorObject returned from the command. See {@link https://docs.mongodb.com/manual/reference/command/findAndModify/index.html#lasterrorobject|findAndModify command documentation}.
1514
1565
  * @property {Number} ok Is 1 if the command executed correctly.
1515
1566
  */
1516
1567
 
@@ -1716,7 +1767,7 @@ Collection.prototype.findAndRemove = deprecate(function(query, sort, options, ca
1716
1767
  * @param {object} [options] Optional settings.
1717
1768
  * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
1718
1769
  * @param {object} [options.cursor] Return the query as cursor, on 2.6 > it returns as a real cursor on pre 2.6 it returns as an emulated cursor.
1719
- * @param {number} [options.cursor.batchSize] The batchSize for the cursor
1770
+ * @param {number} [options.cursor.batchSize=1000] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
1720
1771
  * @param {boolean} [options.explain=false] Explain returns the aggregation execution plan (requires mongodb 2.6 >).
1721
1772
  * @param {boolean} [options.allowDiskUse=false] allowDiskUse lets the server know if it can use disk to store temporary results for the aggregation (requires mongodb 2.6 >).
1722
1773
  * @param {number} [options.maxTimeMS] maxTimeMS specifies a cumulative time limit in milliseconds for processing operations on the cursor. MongoDB interrupts the operation at the earliest following interrupt point.
@@ -1792,7 +1843,7 @@ Collection.prototype.aggregate = function(pipeline, options, callback) {
1792
1843
  * @param {string} [options.fullDocument='default'] Allowed values: ‘default’, ‘updateLookup’. When set to ‘updateLookup’, the change stream will include both a delta describing the changes to the document, as well as a copy of the entire document that was changed from some time after the change occurred.
1793
1844
  * @param {object} [options.resumeAfter] Specifies the logical starting point for the new change stream. This should be the _id field from a previously returned change stream document.
1794
1845
  * @param {number} [options.maxAwaitTimeMS] The maximum amount of time for the server to wait on new documents to satisfy a change stream query
1795
- * @param {number} [options.batchSize] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
1846
+ * @param {number} [options.batchSize=1000] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
1796
1847
  * @param {object} [options.collation] Specify collation settings for operation. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
1797
1848
  * @param {ReadPreference} [options.readPreference] The read preference. Defaults to the read preference of the database or collection. See {@link https://docs.mongodb.com/manual/reference/read-preference|read preference documentation}.
1798
1849
  * @param {Timestamp} [options.startAtOperationTime] receive change events that occur after the specified timestamp
@@ -1825,7 +1876,7 @@ Collection.prototype.watch = function(pipeline, options) {
1825
1876
  * @method
1826
1877
  * @param {object} [options] Optional settings.
1827
1878
  * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
1828
- * @param {number} [options.batchSize] Set the batchSize for the getMoreCommand when iterating over the query results.
1879
+ * @param {number} [options.batchSize=1000] Set the batchSize for the getMoreCommand when iterating over the query results.
1829
1880
  * @param {number} [options.numCursors=1] The maximum number of parallel command cursors to return (the number of returned cursors will be in the range 1:numCursors)
1830
1881
  * @param {boolean} [options.raw=false] Return all BSON documents as Raw Buffer documents.
1831
1882
  * @param {Collection~parallelCollectionScanCallback} [callback] The command result callback
@@ -90,7 +90,7 @@ class CommandCursor extends Cursor {
90
90
  /**
91
91
  * Set the batch size for the cursor.
92
92
  * @method
93
- * @param {number} value The batchSize for the cursor.
93
+ * @param {number} value The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/find/|find command documentation}.
94
94
  * @throws {MongoError}
95
95
  * @return {CommandCursor}
96
96
  */
@@ -56,10 +56,13 @@ class Connection extends EventEmitter {
56
56
  /**
57
57
  * Creates a new Connection instance
58
58
  *
59
+ * **NOTE**: Internal class, do not instantiate directly
60
+ *
59
61
  * @param {Socket} socket The socket this connection wraps
60
- * @param {Object} [options] Optional settings
61
- * @param {string} [options.host] The host the socket is connected to
62
- * @param {number} [options.port] The port used for the socket connection
62
+ * @param {Object} options Various settings
63
+ * @param {object} options.bson An implementation of bson serialize and deserialize
64
+ * @param {string} [options.host='localhost'] The host the socket is connected to
65
+ * @param {number} [options.port=27017] The port used for the socket connection
63
66
  * @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
64
67
  * @param {number} [options.keepAliveInitialDelay=300000] Initial delay before TCP keep alive enabled
65
68
  * @param {number} [options.connectionTimeout=30000] TCP Connection timeout setting
@@ -67,6 +70,7 @@ class Connection extends EventEmitter {
67
70
  * @param {boolean} [options.promoteLongs] Convert Long values from the db into Numbers if they fit into 53 bits
68
71
  * @param {boolean} [options.promoteValues] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
69
72
  * @param {boolean} [options.promoteBuffers] Promotes Binary BSON values to native Node Buffers.
73
+ * @param {number} [options.maxBsonMessageSize=0x4000000] Largest possible size of a BSON message (for legacy purposes)
70
74
  */
71
75
  constructor(socket, options) {
72
76
  super();
@@ -27,6 +27,7 @@
27
27
  // [uint32 checksum;]
28
28
  // };
29
29
 
30
+ const Buffer = require('safe-buffer').Buffer;
30
31
  const opcodes = require('../wireprotocol/shared').opcodes;
31
32
  const databaseNamespace = require('../wireprotocol/shared').databaseNamespace;
32
33
  const ReadPreference = require('../topologies/read_preference');
@@ -90,7 +91,7 @@ class Msg {
90
91
  flags |= OPTS_EXHAUST_ALLOWED;
91
92
  }
92
93
 
93
- const header = new Buffer(
94
+ const header = Buffer.alloc(
94
95
  4 * 4 + // Header
95
96
  4 // Flags
96
97
  );
@@ -110,7 +111,7 @@ class Msg {
110
111
  }
111
112
 
112
113
  makeDocumentSegment(buffers, document) {
113
- const payloadTypeBuffer = new Buffer(1);
114
+ const payloadTypeBuffer = Buffer.alloc(1);
114
115
  payloadTypeBuffer[0] = 0;
115
116
 
116
117
  const documentBuffer = this.serializeBson(document);
@@ -3,7 +3,7 @@
3
3
  const inherits = require('util').inherits;
4
4
  const EventEmitter = require('events').EventEmitter;
5
5
  const MongoError = require('../error').MongoError;
6
- const MongoNetworkError = require('../error').MongoNetworkError;
6
+ const MongoTimeoutError = require('../error').MongoTimeoutError;
7
7
  const MongoWriteConcernError = require('../error').MongoWriteConcernError;
8
8
  const Logger = require('./logger');
9
9
  const f = require('util').format;
@@ -60,7 +60,7 @@ var _id = 0;
60
60
  * @param {Buffer} [options.crl] SSL Certificate revocation store binary buffer
61
61
  * @param {Buffer} [options.cert] SSL Certificate binary buffer
62
62
  * @param {Buffer} [options.key] SSL Key file binary buffer
63
- * @param {string} [options.passPhrase] SSL Certificate pass phrase
63
+ * @param {string} [options.passphrase] SSL Certificate pass phrase
64
64
  * @param {boolean} [options.rejectUnauthorized=false] Reject unauthorized server certificates
65
65
  * @param {boolean} [options.promoteLongs=true] Convert Long values from the db into Numbers if they fit into 53 bits
66
66
  * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
@@ -103,7 +103,7 @@ var Pool = function(topology, options) {
103
103
  crl: null,
104
104
  cert: null,
105
105
  key: null,
106
- passPhrase: null,
106
+ passphrase: null,
107
107
  rejectUnauthorized: false,
108
108
  promoteLongs: true,
109
109
  promoteValues: true,
@@ -113,7 +113,9 @@ var Pool = function(topology, options) {
113
113
  reconnectInterval: 1000,
114
114
  reconnectTries: 30,
115
115
  // Enable domains
116
- domainsEnabled: false
116
+ domainsEnabled: false,
117
+ // feature flag for determining if we are running with the unified topology or not
118
+ legacyCompatMode: true
117
119
  },
118
120
  options
119
121
  );
@@ -123,6 +125,7 @@ var Pool = function(topology, options) {
123
125
  // Current reconnect retries
124
126
  this.retriesLeft = this.options.reconnectTries;
125
127
  this.reconnectId = null;
128
+ this.reconnectError = null;
126
129
  // No bson parser passed in
127
130
  if (
128
131
  !options.bson ||
@@ -146,9 +149,6 @@ var Pool = function(topology, options) {
146
149
  // Operation work queue
147
150
  this.queue = [];
148
151
 
149
- // Contains the reconnect connection
150
- this.reconnectConnection = null;
151
-
152
152
  // Number of consecutive timeouts caught
153
153
  this.numberOfConsecutiveTimeouts = 0;
154
154
  // Current pool Index
@@ -214,7 +214,6 @@ function resetPoolState(pool) {
214
214
  pool.availableConnections = [];
215
215
  pool.connectingConnections = 0;
216
216
  pool.executing = false;
217
- pool.reconnectConnection = null;
218
217
  pool.numberOfConsecutiveTimeouts = 0;
219
218
  pool.connectionIndex = 0;
220
219
  pool.retriesLeft = pool.options.reconnectTries;
@@ -293,73 +292,62 @@ function connectionFailureHandler(pool, event, err, conn) {
293
292
 
294
293
  // Start reconnection attempts
295
294
  if (!pool.reconnectId && pool.options.reconnect) {
295
+ pool.reconnectError = err;
296
296
  pool.reconnectId = setTimeout(attemptReconnect(pool), pool.options.reconnectInterval);
297
297
  }
298
298
 
299
299
  // Do we need to do anything to maintain the minimum pool size
300
300
  const totalConnections = totalConnectionCount(pool);
301
301
  if (totalConnections < pool.minSize) {
302
- _createConnection(pool);
302
+ createConnection(pool);
303
303
  }
304
304
  }
305
305
 
306
- function attemptReconnect(self) {
306
+ function attemptReconnect(pool, callback) {
307
307
  return function() {
308
- self.emit('attemptReconnect', self);
309
- if (self.state === DESTROYED || self.state === DESTROYING) return;
308
+ pool.emit('attemptReconnect', pool);
309
+
310
+ if (pool.state === DESTROYED || pool.state === DESTROYING) {
311
+ if (typeof callback === 'function') {
312
+ callback(new MongoError('Cannot create connection when pool is destroyed'));
313
+ }
310
314
 
311
- // We are connected do not try again
312
- if (self.isConnected()) {
313
- self.reconnectId = null;
314
315
  return;
315
316
  }
316
317
 
317
- self.connectingConnections++;
318
- connect(self.options, (err, connection) => {
319
- self.connectingConnections--;
318
+ pool.retriesLeft = pool.retriesLeft - 1;
319
+ if (pool.retriesLeft <= 0) {
320
+ pool.destroy();
320
321
 
321
- if (err) {
322
- if (self.logger.isDebug()) {
323
- self.logger.debug(`connection attempt failed with error [${JSON.stringify(err)}]`);
324
- }
325
-
326
- self.retriesLeft = self.retriesLeft - 1;
327
- if (self.retriesLeft <= 0) {
328
- self.destroy();
329
- self.emit(
330
- 'reconnectFailed',
331
- new MongoNetworkError(
332
- f(
333
- 'failed to reconnect after %s attempts with interval %s ms',
334
- self.options.reconnectTries,
335
- self.options.reconnectInterval
336
- )
337
- )
338
- );
339
- } else {
340
- self.reconnectId = setTimeout(attemptReconnect(self), self.options.reconnectInterval);
341
- }
322
+ const error = new MongoTimeoutError(
323
+ `failed to reconnect after ${pool.options.reconnectTries} attempts with interval ${
324
+ pool.options.reconnectInterval
325
+ } ms`,
326
+ pool.reconnectError
327
+ );
342
328
 
343
- return;
329
+ pool.emit('reconnectFailed', error);
330
+ if (typeof callback === 'function') {
331
+ callback(error);
344
332
  }
345
333
 
346
- if (self.state === DESTROYED || self.state === DESTROYING) {
347
- return connection.destroy();
334
+ return;
335
+ }
336
+
337
+ // clear the reconnect id on retry
338
+ pool.reconnectId = null;
339
+
340
+ // now retry creating a connection
341
+ createConnection(pool, (err, conn) => {
342
+ if (err == null) {
343
+ pool.reconnectId = null;
344
+ pool.retriesLeft = pool.options.reconnectTries;
345
+ pool.emit('reconnect', pool);
348
346
  }
349
347
 
350
- self.reconnectId = null;
351
- handlers.forEach(event => connection.removeAllListeners(event));
352
- connection.on('error', self._connectionErrorHandler);
353
- connection.on('close', self._connectionCloseHandler);
354
- connection.on('timeout', self._connectionTimeoutHandler);
355
- connection.on('parseError', self._connectionParseErrorHandler);
356
- connection.on('message', self._messageHandler);
357
-
358
- self.retriesLeft = self.options.reconnectTries;
359
- self.availableConnections.push(connection);
360
- self.reconnectConnection = null;
361
- self.emit('reconnect', self);
362
- _execute(self)();
348
+ if (typeof callback === 'function') {
349
+ callback(err, conn);
350
+ }
363
351
  });
364
352
  };
365
353
  }
@@ -564,64 +552,26 @@ Pool.prototype.connect = function() {
564
552
  throw new MongoError('connection in unlawful state ' + this.state);
565
553
  }
566
554
 
567
- const self = this;
568
555
  stateTransition(this, CONNECTING);
569
-
570
- self.connectingConnections++;
571
- connect(self.options, (err, connection) => {
572
- self.connectingConnections--;
573
-
556
+ createConnection(this, (err, conn) => {
574
557
  if (err) {
575
- if (self.logger.isDebug()) {
576
- self.logger.debug(`connection attempt failed with error [${JSON.stringify(err)}]`);
577
- }
578
-
579
- if (self.state === CONNECTING) {
580
- self.emit('error', err);
558
+ if (this.state === CONNECTING) {
559
+ this.emit('error', err);
581
560
  }
582
561
 
562
+ this.destroy();
583
563
  return;
584
564
  }
585
565
 
586
- if (self.state === DESTROYED || self.state === DESTROYING) {
587
- return self.destroy();
588
- }
589
-
590
- // attach event handlers
591
- connection.on('error', self._connectionErrorHandler);
592
- connection.on('close', self._connectionCloseHandler);
593
- connection.on('timeout', self._connectionTimeoutHandler);
594
- connection.on('parseError', self._connectionParseErrorHandler);
595
- connection.on('message', self._messageHandler);
596
-
597
- // If we are in a topology, delegate the auth to it
598
- // This is to avoid issues where we would auth against an
599
- // arbiter
600
- if (self.options.inTopology) {
601
- stateTransition(self, CONNECTED);
602
- self.availableConnections.push(connection);
603
- return self.emit('connect', self, connection);
604
- }
605
-
606
- if (self.state === DESTROYED || self.state === DESTROYING) {
607
- return self.destroy();
608
- }
609
-
610
- if (err) {
611
- self.destroy();
612
- return self.emit('error', err);
613
- }
566
+ stateTransition(this, CONNECTED);
567
+ this.emit('connect', this, conn);
614
568
 
615
- stateTransition(self, CONNECTED);
616
- self.availableConnections.push(connection);
617
-
618
- if (self.minSize) {
619
- for (let i = 0; i < self.minSize; i++) {
620
- _createConnection(self);
569
+ // create min connections
570
+ if (this.minSize) {
571
+ for (let i = 0; i < this.minSize; i++) {
572
+ createConnection(this);
621
573
  }
622
574
  }
623
-
624
- self.emit('connect', self, connection);
625
575
  });
626
576
  };
627
577
 
@@ -718,12 +668,6 @@ Pool.prototype.destroy = function(force, callback) {
718
668
  clearTimeout(this.reconnectId);
719
669
  }
720
670
 
721
- // If we have a reconnect connection running, close
722
- // immediately
723
- if (this.reconnectConnection) {
724
- this.reconnectConnection.destroy();
725
- }
726
-
727
671
  // Wait for the operations to drain before we close the pool
728
672
  function checkStatus() {
729
673
  flushMonitoringOperations(self.queue);
@@ -782,7 +726,7 @@ Pool.prototype.reset = function(callback) {
782
726
  resetPoolState(this);
783
727
 
784
728
  // create an initial connection, and kick off execution again
785
- _createConnection(this);
729
+ createConnection(this);
786
730
 
787
731
  if (typeof callback === 'function') {
788
732
  callback(null, null);
@@ -996,55 +940,82 @@ function removeConnection(self, connection) {
996
940
  if (remove(connection, self.inUseConnections)) return;
997
941
  }
998
942
 
999
- const handlers = ['close', 'message', 'error', 'timeout', 'parseError', 'connect'];
1000
- function _createConnection(self) {
1001
- if (self.state === DESTROYED || self.state === DESTROYING) {
943
+ function createConnection(pool, callback) {
944
+ if (pool.state === DESTROYED || pool.state === DESTROYING) {
945
+ if (typeof callback === 'function') {
946
+ callback(new MongoError('Cannot create connection when pool is destroyed'));
947
+ }
948
+
1002
949
  return;
1003
950
  }
1004
951
 
1005
- self.connectingConnections++;
1006
- connect(self.options, (err, connection) => {
1007
- self.connectingConnections--;
952
+ pool.connectingConnections++;
953
+ connect(pool.options, (err, connection) => {
954
+ pool.connectingConnections--;
1008
955
 
1009
956
  if (err) {
1010
- if (self.logger.isDebug()) {
1011
- self.logger.debug(`connection attempt failed with error [${JSON.stringify(err)}]`);
957
+ if (pool.logger.isDebug()) {
958
+ pool.logger.debug(`connection attempt failed with error [${JSON.stringify(err)}]`);
1012
959
  }
1013
960
 
1014
- if (!self.reconnectId && self.options.reconnect) {
1015
- self.reconnectId = setTimeout(attemptReconnect(self), self.options.reconnectInterval);
961
+ if (pool.options.legacyCompatMode === false) {
962
+ // The unified topology uses the reported `error` from a pool to track what error
963
+ // reason is returned to the user during selection timeout. We only want to emit
964
+ // this if the pool is active because the listeners are removed on destruction.
965
+ if (pool.state !== DESTROYED && pool.state !== DESTROYING) {
966
+ pool.emit('error', err);
967
+ }
1016
968
  }
1017
969
 
1018
- return;
1019
- }
970
+ // check if reconnect is enabled, and attempt retry if so
971
+ if (!pool.reconnectId && pool.options.reconnect) {
972
+ if (pool.state === CONNECTING && pool.options.legacyCompatMode) {
973
+ callback(err);
974
+ return;
975
+ }
1020
976
 
1021
- if (self.state === DESTROYED || self.state === DESTROYING) {
1022
- removeConnection(self, connection);
1023
- return connection.destroy();
977
+ pool.reconnectError = err;
978
+ pool.reconnectId = setTimeout(
979
+ attemptReconnect(pool, callback),
980
+ pool.options.reconnectInterval
981
+ );
982
+
983
+ return;
984
+ }
985
+
986
+ if (typeof callback === 'function') {
987
+ callback(err);
988
+ }
989
+
990
+ return;
1024
991
  }
1025
992
 
1026
- connection.on('error', self._connectionErrorHandler);
1027
- connection.on('close', self._connectionCloseHandler);
1028
- connection.on('timeout', self._connectionTimeoutHandler);
1029
- connection.on('parseError', self._connectionParseErrorHandler);
1030
- connection.on('message', self._messageHandler);
993
+ // the pool might have been closed since we started creating the connection
994
+ if (pool.state === DESTROYED || pool.state === DESTROYING) {
995
+ if (typeof callback === 'function') {
996
+ callback(new MongoError('Pool was destroyed after connection creation'));
997
+ }
1031
998
 
1032
- if (self.state === DESTROYED || self.state === DESTROYING) {
1033
- return connection.destroy();
999
+ connection.destroy();
1000
+ return;
1034
1001
  }
1035
1002
 
1036
- // Remove the connection from the connectingConnections list
1037
- removeConnection(self, connection);
1003
+ // otherwise, connect relevant event handlers and add it to our available connections
1004
+ connection.on('error', pool._connectionErrorHandler);
1005
+ connection.on('close', pool._connectionCloseHandler);
1006
+ connection.on('timeout', pool._connectionTimeoutHandler);
1007
+ connection.on('parseError', pool._connectionParseErrorHandler);
1008
+ connection.on('message', pool._messageHandler);
1038
1009
 
1039
- // Handle error
1040
- if (err) {
1041
- return connection.destroy();
1010
+ pool.availableConnections.push(connection);
1011
+
1012
+ // if a callback was provided, return the connection
1013
+ if (typeof callback === 'function') {
1014
+ callback(null, connection);
1042
1015
  }
1043
1016
 
1044
- // Push to available
1045
- self.availableConnections.push(connection);
1046
- // Execute any work waiting
1047
- _execute(self)();
1017
+ // immediately execute any waiting work
1018
+ _execute(pool)();
1048
1019
  });
1049
1020
  }
1050
1021
 
@@ -1146,7 +1117,7 @@ function _execute(self) {
1146
1117
  // Attempt to grow the pool if it's not yet maxsize
1147
1118
  if (totalConnections < self.options.size && self.queue.length > 0) {
1148
1119
  // Create a new connection
1149
- _createConnection(self);
1120
+ createConnection(self);
1150
1121
  }
1151
1122
 
1152
1123
  // Re-execute the operation
@@ -1166,7 +1137,7 @@ function _execute(self) {
1166
1137
  // Lets put the workItem back on the list
1167
1138
  self.queue.unshift(workItem);
1168
1139
  // Create a new connection
1169
- _createConnection(self);
1140
+ createConnection(self);
1170
1141
  // Break from the loop
1171
1142
  break;
1172
1143
  }
@@ -68,7 +68,7 @@ class CoreCursor extends Readable {
68
68
  * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
69
69
  * @param {{object}|Long} cmd The selector (can be a command or a cursorId)
70
70
  * @param {object} [options=null] Optional settings.
71
- * @param {object} [options.batchSize=1000] Batchsize for the operation
71
+ * @param {object} [options.batchSize=1000] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/find/| find command documentation} and {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
72
72
  * @param {array} [options.documents=[]] Initial documents list for cursor
73
73
  * @param {object} [options.transforms=null] Transform methods for the cursor results
74
74
  * @param {function} [options.transforms.query] Transform the value returned from the initial query