mongodb 3.3.1 → 3.3.4

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.
Files changed (41) hide show
  1. package/HISTORY.md +82 -0
  2. package/lib/aggregation_cursor.js +1 -1
  3. package/lib/bulk/common.js +166 -73
  4. package/lib/bulk/ordered.js +1 -1
  5. package/lib/bulk/unordered.js +1 -0
  6. package/lib/change_stream.js +2 -3
  7. package/lib/collection.js +70 -15
  8. package/lib/command_cursor.js +1 -1
  9. package/lib/core/connection/connect.js +20 -5
  10. package/lib/core/connection/connection.js +7 -3
  11. package/lib/core/connection/msg.js +3 -2
  12. package/lib/core/connection/pool.js +192 -206
  13. package/lib/core/cursor.js +1 -1
  14. package/lib/core/error.js +6 -1
  15. package/lib/core/sdam/monitoring.js +8 -1
  16. package/lib/core/sdam/server.js +90 -34
  17. package/lib/core/sdam/server_description.js +29 -0
  18. package/lib/core/sdam/topology.js +200 -91
  19. package/lib/core/sdam/topology_description.js +5 -3
  20. package/lib/core/topologies/mongos.js +72 -24
  21. package/lib/core/topologies/replset.js +34 -15
  22. package/lib/core/topologies/replset_state.js +5 -5
  23. package/lib/core/topologies/server.js +13 -12
  24. package/lib/core/topologies/shared.js +11 -15
  25. package/lib/core/uri_parser.js +8 -2
  26. package/lib/core/utils.js +40 -2
  27. package/lib/cursor.js +1 -1
  28. package/lib/db.js +3 -3
  29. package/lib/gridfs-stream/download.js +12 -5
  30. package/lib/gridfs-stream/index.js +1 -1
  31. package/lib/mongo_client.js +3 -3
  32. package/lib/operations/close.js +6 -2
  33. package/lib/operations/common_functions.js +4 -0
  34. package/lib/operations/connect.js +16 -13
  35. package/lib/operations/execute_operation.js +28 -12
  36. package/lib/operations/replace_one.js +1 -1
  37. package/lib/topologies/mongos.js +1 -1
  38. package/lib/topologies/replset.js +1 -1
  39. package/lib/topologies/server.js +1 -1
  40. package/lib/topologies/topology_base.js +4 -5
  41. package/package.json +11 -5
@@ -30,14 +30,15 @@ var DISCONNECTED = 'disconnected';
30
30
  var CONNECTING = 'connecting';
31
31
  var CONNECTED = 'connected';
32
32
  var UNREFERENCED = 'unreferenced';
33
+ var DESTROYING = 'destroying';
33
34
  var DESTROYED = 'destroyed';
34
35
 
35
36
  function stateTransition(self, newState) {
36
37
  var legalTransitions = {
37
- disconnected: [CONNECTING, DESTROYED, DISCONNECTED],
38
- connecting: [CONNECTING, DESTROYED, CONNECTED, DISCONNECTED],
39
- connected: [CONNECTED, DISCONNECTED, DESTROYED, UNREFERENCED],
40
- unreferenced: [UNREFERENCED, DESTROYED],
38
+ disconnected: [CONNECTING, DESTROYING, DESTROYED, DISCONNECTED],
39
+ connecting: [CONNECTING, DESTROYING, DESTROYED, CONNECTED, DISCONNECTED],
40
+ connected: [CONNECTED, DISCONNECTED, DESTROYING, DESTROYED, UNREFERENCED],
41
+ unreferenced: [UNREFERENCED, DESTROYING, DESTROYED],
41
42
  destroyed: [DESTROYED]
42
43
  };
43
44
 
@@ -46,9 +47,9 @@ function stateTransition(self, newState) {
46
47
  if (legalStates && legalStates.indexOf(newState) !== -1) {
47
48
  self.state = newState;
48
49
  } else {
49
- self.logger.error(
50
+ self.s.logger.error(
50
51
  f(
51
- 'Pool with id [%s] failed attempted illegal state transition from [%s] to [%s] only following state allowed [%s]',
52
+ 'Mongos with id [%s] failed attempted illegal state transition from [%s] to [%s] only following state allowed [%s]',
52
53
  self.id,
53
54
  self.state,
54
55
  newState,
@@ -292,7 +293,10 @@ Mongos.prototype.auth = function(credentials, callback) {
292
293
 
293
294
  function handleEvent(self) {
294
295
  return function() {
295
- if (self.state === DESTROYED) return;
296
+ if (self.state === DESTROYED || self.state === DESTROYING) {
297
+ return;
298
+ }
299
+
296
300
  // Move to list of disconnectedProxies
297
301
  moveServerFrom(self.connectedProxies, self.disconnectedProxies, this);
298
302
  // Emit the initial topology
@@ -366,7 +370,8 @@ function handleInitialConnectEvent(self, event) {
366
370
  self.s.logger.warn(f(message, _this.name));
367
371
  }
368
372
 
369
- // This is not a mongos proxy, remove it completely
373
+ // This is not a mongos proxy, destroy and remove it completely
374
+ _this.destroy(true);
370
375
  removeProxyFrom(self.connectingProxies, _this);
371
376
  // Emit the left event
372
377
  self.emit('left', 'server', _this);
@@ -445,10 +450,9 @@ function connectProxies(self, servers) {
445
450
  server.connect(self.s.connectOptions);
446
451
  }, timeoutInterval);
447
452
  }
453
+
448
454
  // Start all the servers
449
- while (servers.length > 0) {
450
- connect(servers.shift(), timeoutInterval++);
451
- }
455
+ servers.forEach(server => connect(server, timeoutInterval++));
452
456
  }
453
457
 
454
458
  function pickProxy(self, session) {
@@ -541,14 +545,14 @@ function reconnectProxies(self, proxies, callback) {
541
545
  count = count - 1;
542
546
 
543
547
  // Destroyed
544
- if (self.state === DESTROYED || self.state === UNREFERENCED) {
548
+ if (self.state === DESTROYED || self.state === DESTROYING || self.state === UNREFERENCED) {
545
549
  moveServerFrom(self.connectingProxies, self.disconnectedProxies, _self);
546
550
  return this.destroy();
547
551
  }
548
552
 
549
553
  if (event === 'connect') {
550
554
  // Destroyed
551
- if (self.state === DESTROYED || self.state === UNREFERENCED) {
555
+ if (self.state === DESTROYED || self.state === DESTROYING || self.state === UNREFERENCED) {
552
556
  moveServerFrom(self.connectingProxies, self.disconnectedProxies, _self);
553
557
  return _self.destroy();
554
558
  }
@@ -592,7 +596,7 @@ function reconnectProxies(self, proxies, callback) {
592
596
  function execute(_server, i) {
593
597
  setTimeout(function() {
594
598
  // Destroyed
595
- if (self.state === DESTROYED || self.state === UNREFERENCED) {
599
+ if (self.state === DESTROYED || self.state === DESTROYING || self.state === UNREFERENCED) {
596
600
  return;
597
601
  }
598
602
 
@@ -608,7 +612,7 @@ function reconnectProxies(self, proxies, callback) {
608
612
  })
609
613
  );
610
614
 
611
- destroyServer(_server);
615
+ destroyServer(_server, { force: true });
612
616
  removeProxyFrom(self.disconnectedProxies, _server);
613
617
 
614
618
  // Relay the server description change
@@ -645,9 +649,17 @@ function reconnectProxies(self, proxies, callback) {
645
649
  function topologyMonitor(self, options) {
646
650
  options = options || {};
647
651
 
652
+ // no need to set up the monitor if we're already closed
653
+ if (self.state === DESTROYED || self.state === DESTROYING || self.state === UNREFERENCED) {
654
+ return;
655
+ }
656
+
648
657
  // Set momitoring timeout
649
658
  self.haTimeoutId = setTimeout(function() {
650
- if (self.state === DESTROYED || self.state === UNREFERENCED) return;
659
+ if (self.state === DESTROYED || self.state === DESTROYING || self.state === UNREFERENCED) {
660
+ return;
661
+ }
662
+
651
663
  // If we have a primary and a disconnect handler, execute
652
664
  // buffered operations
653
665
  if (self.isConnected() && self.s.disconnectHandler) {
@@ -678,7 +690,11 @@ function topologyMonitor(self, options) {
678
690
  socketTimeout: self.s.options.connectionTimeout || 2000
679
691
  },
680
692
  function(err, r) {
681
- if (self.state === DESTROYED || self.state === UNREFERENCED) {
693
+ if (
694
+ self.state === DESTROYED ||
695
+ self.state === DESTROYING ||
696
+ self.state === UNREFERENCED
697
+ ) {
682
698
  // Move from connectingProxies
683
699
  moveServerFrom(self.connectedProxies, self.disconnectedProxies, _server);
684
700
  _server.destroy();
@@ -727,7 +743,9 @@ function topologyMonitor(self, options) {
727
743
 
728
744
  // Attempt to connect to any unknown servers
729
745
  return reconnectProxies(self, self.disconnectedProxies, function() {
730
- if (self.state === DESTROYED || self.state === UNREFERENCED) return;
746
+ if (self.state === DESTROYED || self.state === DESTROYING || self.state === UNREFERENCED) {
747
+ return;
748
+ }
731
749
 
732
750
  // Are we connected ? emit connect event
733
751
  if (self.state === CONNECTING && options.firstConnect) {
@@ -751,11 +769,24 @@ function topologyMonitor(self, options) {
751
769
  count = count - 1;
752
770
 
753
771
  if (count === 0) {
754
- if (self.state === DESTROYED || self.state === UNREFERENCED) return;
772
+ if (
773
+ self.state === DESTROYED ||
774
+ self.state === DESTROYING ||
775
+ self.state === UNREFERENCED
776
+ ) {
777
+ return;
778
+ }
755
779
 
756
780
  // Attempt to connect to any unknown servers
757
781
  reconnectProxies(self, self.disconnectedProxies, function() {
758
- if (self.state === DESTROYED || self.state === UNREFERENCED) return;
782
+ if (
783
+ self.state === DESTROYED ||
784
+ self.state === DESTROYING ||
785
+ self.state === UNREFERENCED
786
+ ) {
787
+ return;
788
+ }
789
+
759
790
  // Perform topology monitor
760
791
  topologyMonitor(self);
761
792
  });
@@ -796,6 +827,14 @@ Mongos.prototype.unref = function() {
796
827
  * @method
797
828
  */
798
829
  Mongos.prototype.destroy = function(options, callback) {
830
+ if (typeof options === 'function') {
831
+ callback = options;
832
+ options = {};
833
+ }
834
+
835
+ options = options || {};
836
+
837
+ stateTransition(this, DESTROYING);
799
838
  if (this.haTimeoutId) {
800
839
  clearTimeout(this.haTimeoutId);
801
840
  }
@@ -929,7 +968,9 @@ Mongos.prototype.insert = function(ns, ops, options, callback) {
929
968
  (callback = options), (options = {}), (options = options || {});
930
969
  }
931
970
 
932
- if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
971
+ if (this.state === DESTROYED) {
972
+ return callback(new MongoError(f('topology was destroyed')));
973
+ }
933
974
 
934
975
  // Not connected but we have a disconnecthandler
935
976
  if (!this.isConnected() && this.s.disconnectHandler != null) {
@@ -963,7 +1004,9 @@ Mongos.prototype.update = function(ns, ops, options, callback) {
963
1004
  (callback = options), (options = {}), (options = options || {});
964
1005
  }
965
1006
 
966
- if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
1007
+ if (this.state === DESTROYED) {
1008
+ return callback(new MongoError(f('topology was destroyed')));
1009
+ }
967
1010
 
968
1011
  // Not connected but we have a disconnecthandler
969
1012
  if (!this.isConnected() && this.s.disconnectHandler != null) {
@@ -997,7 +1040,9 @@ Mongos.prototype.remove = function(ns, ops, options, callback) {
997
1040
  (callback = options), (options = {}), (options = options || {});
998
1041
  }
999
1042
 
1000
- if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
1043
+ if (this.state === DESTROYED) {
1044
+ return callback(new MongoError(f('topology was destroyed')));
1045
+ }
1001
1046
 
1002
1047
  // Not connected but we have a disconnecthandler
1003
1048
  if (!this.isConnected() && this.s.disconnectHandler != null) {
@@ -1036,7 +1081,10 @@ Mongos.prototype.command = function(ns, cmd, options, callback) {
1036
1081
  (callback = options), (options = {}), (options = options || {});
1037
1082
  }
1038
1083
 
1039
- if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
1084
+ if (this.state === DESTROYED) {
1085
+ return callback(new MongoError(f('topology was destroyed')));
1086
+ }
1087
+
1040
1088
  var self = this;
1041
1089
 
1042
1090
  // Pick a proxy
@@ -277,27 +277,34 @@ function rexecuteOperations(self) {
277
277
  }
278
278
 
279
279
  function connectNewServers(self, servers, callback) {
280
+ // No new servers
281
+ if (servers.length === 0) {
282
+ return callback();
283
+ }
284
+
280
285
  // Count lefts
281
286
  var count = servers.length;
282
287
  var error = null;
283
288
 
289
+ function done() {
290
+ count = count - 1;
291
+ if (count === 0) {
292
+ callback(error);
293
+ }
294
+ }
295
+
284
296
  // Handle events
285
297
  var _handleEvent = function(self, event) {
286
298
  return function(err) {
287
299
  var _self = this;
288
- count = count - 1;
289
300
 
290
301
  // Destroyed
291
302
  if (self.state === DESTROYED || self.state === UNREFERENCED) {
292
- return this.destroy({ force: true });
303
+ this.destroy({ force: true });
304
+ return done();
293
305
  }
294
306
 
295
307
  if (event === 'connect') {
296
- // Destroyed
297
- if (self.state === DESTROYED || self.state === UNREFERENCED) {
298
- return _self.destroy({ force: true });
299
- }
300
-
301
308
  // Update the state
302
309
  var result = self.s.replicaSetState.update(_self);
303
310
  // Update the state with the new server
@@ -332,17 +339,10 @@ function connectNewServers(self, servers, callback) {
332
339
 
333
340
  // Rexecute any stalled operation
334
341
  rexecuteOperations(self);
335
-
336
- // Are we done finish up callback
337
- if (count === 0) {
338
- callback(error);
339
- }
342
+ done();
340
343
  };
341
344
  };
342
345
 
343
- // No new servers
344
- if (count === 0) return callback();
345
-
346
346
  // Execute method
347
347
  function execute(_server, i) {
348
348
  setTimeout(function() {
@@ -351,6 +351,17 @@ function connectNewServers(self, servers, callback) {
351
351
  return;
352
352
  }
353
353
 
354
+ // remove existing connecting server if it's failed to connect, otherwise
355
+ // wait for that server to connect
356
+ const existingServerIdx = self.s.connectingServers.findIndex(s => s.name === _server);
357
+ if (existingServerIdx >= 0) {
358
+ const connectingServer = self.s.connectingServers[existingServerIdx];
359
+ connectingServer.destroy({ force: true });
360
+
361
+ self.s.connectingServers.splice(existingServerIdx, 1);
362
+ return done();
363
+ }
364
+
354
365
  // Create a new server instance
355
366
  var server = new Server(
356
367
  Object.assign({}, self.s.options, {
@@ -378,6 +389,7 @@ function connectNewServers(self, servers, callback) {
378
389
  // Command Monitoring events
379
390
  relayEvents(server, self, ['commandStarted', 'commandSucceeded', 'commandFailed']);
380
391
 
392
+ self.s.connectingServers.push(server);
381
393
  server.connect(self.s.connectOptions);
382
394
  }, i);
383
395
  }
@@ -951,6 +963,11 @@ ReplSet.prototype.auth = function(credentials, callback) {
951
963
  * @method
952
964
  */
953
965
  ReplSet.prototype.destroy = function(options, callback) {
966
+ if (typeof options === 'function') {
967
+ callback = options;
968
+ options = {};
969
+ }
970
+
954
971
  options = options || {};
955
972
 
956
973
  let destroyCount = this.s.connectingServers.length + 1; // +1 for the callback from `replicaSetState.destroy`
@@ -1202,6 +1219,7 @@ function executeWriteOperation(args, options, callback) {
1202
1219
 
1203
1220
  // Per SDAM, remove primary from replicaset
1204
1221
  if (self.s.replicaSetState.primary) {
1222
+ self.s.replicaSetState.primary.destroy();
1205
1223
  self.s.replicaSetState.remove(self.s.replicaSetState.primary, { force: true });
1206
1224
  }
1207
1225
 
@@ -1363,6 +1381,7 @@ ReplSet.prototype.command = function(ns, cmd, options, callback) {
1363
1381
 
1364
1382
  // Per SDAM, remove primary from replicaset
1365
1383
  if (this.s.replicaSetState.primary) {
1384
+ this.s.replicaSetState.primary.destroy();
1366
1385
  this.s.replicaSetState.remove(this.s.replicaSetState.primary, { force: true });
1367
1386
  }
1368
1387
 
@@ -391,7 +391,7 @@ ReplSetState.prototype.update = function(server) {
391
391
  removeFrom(server, self.unknownServers);
392
392
 
393
393
  // Destroy the instance
394
- server.destroy();
394
+ server.destroy({ force: true });
395
395
 
396
396
  // Set the type of topology we have
397
397
  if (this.primary && !this.primary.equals(server)) {
@@ -555,7 +555,7 @@ ReplSetState.prototype.update = function(server) {
555
555
  // Signal primary left
556
556
  self.emit('left', 'primary', this.primary);
557
557
  // Destroy the instance
558
- self.primary.destroy();
558
+ self.primary.destroy({ force: true });
559
559
  // Set the new instance
560
560
  self.primary = server;
561
561
  // Set the set information
@@ -607,7 +607,7 @@ ReplSetState.prototype.update = function(server) {
607
607
 
608
608
  // Remove primary
609
609
  if (this.primary && this.primary.name.toLowerCase() === serverName) {
610
- server.destroy();
610
+ server.destroy({ force: true });
611
611
  this.primary = null;
612
612
  self.emit('left', 'primary', server);
613
613
  }
@@ -659,7 +659,7 @@ ReplSetState.prototype.update = function(server) {
659
659
 
660
660
  // Remove primary
661
661
  if (this.primary && this.primary.name.toLowerCase() === serverName) {
662
- server.destroy();
662
+ server.destroy({ force: true });
663
663
  this.primary = null;
664
664
  self.emit('left', 'primary', server);
665
665
  }
@@ -674,7 +674,7 @@ ReplSetState.prototype.update = function(server) {
674
674
  //
675
675
  if (this.set[serverName] && this.set[serverName].type === ServerType.RSPrimary) {
676
676
  self.emit('left', 'primary', this.primary);
677
- this.primary.destroy();
677
+ this.primary.destroy({ force: true });
678
678
  this.primary = null;
679
679
  this.topologyType = TopologyType.ReplicaSetNoPrimary;
680
680
  return false;
@@ -55,6 +55,10 @@ var serverAccounting = false;
55
55
  var servers = {};
56
56
  var BSON = retrieveBSON();
57
57
 
58
+ function topologyId(server) {
59
+ return server.s.parent == null ? server.id : server.s.parent.id;
60
+ }
61
+
58
62
  /**
59
63
  * Creates a new Server instance
60
64
  * @class
@@ -151,8 +155,6 @@ var Server = function(options) {
151
155
  // Monitoring timeout
152
156
  monitoringInterval:
153
157
  typeof options.monitoringInterval === 'number' ? options.monitoringInterval : 5000,
154
- // Topology id
155
- topologyId: -1,
156
158
  compression: { compressors: createCompressionInfo(options) },
157
159
  // Optional parent topology
158
160
  parent: options.parent
@@ -486,14 +488,11 @@ Server.prototype.connect = function(options) {
486
488
 
487
489
  // Emit toplogy opening event if not in topology
488
490
  if (!self.s.inTopology) {
489
- this.emit('topologyOpening', { topologyId: self.id });
491
+ this.emit('topologyOpening', { topologyId: topologyId(self) });
490
492
  }
491
493
 
492
494
  // Emit opening server event
493
- self.emit('serverOpening', {
494
- topologyId: self.s.topologyId !== -1 ? self.s.topologyId : self.id,
495
- address: self.name
496
- });
495
+ self.emit('serverOpening', { topologyId: topologyId(self), address: self.name });
497
496
 
498
497
  self.s.pool.connect();
499
498
  };
@@ -843,6 +842,11 @@ Server.prototype.destroy = function(options, callback) {
843
842
  return;
844
843
  }
845
844
 
845
+ if (typeof options === 'function') {
846
+ callback = options;
847
+ options = {};
848
+ }
849
+
846
850
  options = options || {};
847
851
  var self = this;
848
852
 
@@ -878,14 +882,11 @@ Server.prototype.destroy = function(options, callback) {
878
882
 
879
883
  // Emit opening server event
880
884
  if (self.listeners('serverClosed').length > 0)
881
- self.emit('serverClosed', {
882
- topologyId: self.s.topologyId !== -1 ? self.s.topologyId : self.id,
883
- address: self.name
884
- });
885
+ self.emit('serverClosed', { topologyId: topologyId(self), address: self.name });
885
886
 
886
887
  // Emit toplogy opening event if not in topology
887
888
  if (self.listeners('topologyClosed').length > 0 && !self.s.inTopology) {
888
- self.emit('topologyClosed', { topologyId: self.id });
889
+ self.emit('topologyClosed', { topologyId: topologyId(self) });
889
890
  }
890
891
 
891
892
  if (self.s.logger.isDebug()) {
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const os = require('os');
4
- const f = require('util').format;
5
4
  const ReadPreference = require('./read_preference');
6
5
  const Buffer = require('safe-buffer').Buffer;
7
6
  const TopologyType = require('../sdam/topology_description').TopologyType;
@@ -20,20 +19,19 @@ function emitSDAMEvent(self, event, description) {
20
19
  }
21
20
 
22
21
  // Get package.json variable
23
- var driverVersion = require('../../../package.json').version;
24
- var nodejsversion = f('Node.js %s, %s', process.version, os.endianness());
25
- var type = os.type();
26
- var name = process.platform;
27
- var architecture = process.arch;
28
- var release = os.release();
22
+ const driverVersion = require('../../../package.json').version;
23
+ const nodejsVersion = `'Node.js ${process.version}, ${os.endianness}`;
24
+ const type = os.type();
25
+ const name = process.platform;
26
+ const architecture = process.arch;
27
+ const release = os.release();
29
28
 
30
29
  function createClientInfo(options) {
31
- // Build default client information
32
- var clientInfo = options.clientInfo
30
+ const clientInfo = options.clientInfo
33
31
  ? clone(options.clientInfo)
34
32
  : {
35
33
  driver: {
36
- name: 'nodejs-core',
34
+ name: 'nodejs',
37
35
  version: driverVersion
38
36
  },
39
37
  os: {
@@ -44,11 +42,8 @@ function createClientInfo(options) {
44
42
  }
45
43
  };
46
44
 
47
- // Is platform specified
48
- if (clientInfo.platform && clientInfo.platform.indexOf('mongodb-core') === -1) {
49
- clientInfo.platform = f('%s, mongodb-core: %s', clientInfo.platform, driverVersion);
50
- } else if (!clientInfo.platform) {
51
- clientInfo.platform = nodejsversion;
45
+ if (options.useUnifiedTopology) {
46
+ clientInfo.platform = `${nodejsVersion} (${options.useUnifiedTopology ? 'unified' : 'legacy'})`;
52
47
  }
53
48
 
54
49
  // Do we have an application specific string
@@ -473,3 +468,4 @@ module.exports.Interval = Interval;
473
468
  module.exports.Timeout = Timeout;
474
469
  module.exports.isRetryableWritesSupported = isRetryableWritesSupported;
475
470
  module.exports.getMMAPError = getMMAPError;
471
+ module.exports.topologyType = topologyType;
@@ -534,6 +534,10 @@ function parseConnectionString(uri, options, callback) {
534
534
  if (parsedOptions.auth.username) auth.username = parsedOptions.auth.username;
535
535
  if (parsedOptions.auth.user) auth.username = parsedOptions.auth.user;
536
536
  if (parsedOptions.auth.password) auth.password = parsedOptions.auth.password;
537
+ } else {
538
+ if (parsedOptions.username) auth.username = parsedOptions.username;
539
+ if (parsedOptions.user) auth.username = parsedOptions.user;
540
+ if (parsedOptions.password) auth.password = parsedOptions.password;
537
541
  }
538
542
 
539
543
  if (cap[4].split('?')[0].indexOf('@') !== -1) {
@@ -551,8 +555,8 @@ function parseConnectionString(uri, options, callback) {
551
555
  return callback(new MongoParseError('Unescaped colon in authority section'));
552
556
  }
553
557
 
554
- auth.username = qs.unescape(authParts[0]);
555
- auth.password = authParts[1] ? qs.unescape(authParts[1]) : null;
558
+ if (!auth.username) auth.username = qs.unescape(authParts[0]);
559
+ if (!auth.password) auth.password = authParts[1] ? qs.unescape(authParts[1]) : null;
556
560
  }
557
561
 
558
562
  let hostParsingError = null;
@@ -617,6 +621,8 @@ function parseConnectionString(uri, options, callback) {
617
621
 
618
622
  if (result.auth && result.auth.db) {
619
623
  result.defaultDatabase = result.auth.db;
624
+ } else {
625
+ result.defaultDatabase = 'test';
620
626
  }
621
627
 
622
628
  try {
package/lib/core/utils.js CHANGED
@@ -150,7 +150,12 @@ function eachAsync(arr, eachFn, callback) {
150
150
  }
151
151
 
152
152
  for (let idx = 0; idx < length; ++idx) {
153
- eachFn(arr[idx], eachCallback);
153
+ try {
154
+ eachFn(arr[idx], eachCallback);
155
+ } catch (err) {
156
+ callback(err);
157
+ return;
158
+ }
154
159
  }
155
160
  }
156
161
 
@@ -158,6 +163,36 @@ function isUnifiedTopology(topology) {
158
163
  return topology.description != null;
159
164
  }
160
165
 
166
+ function arrayStrictEqual(arr, arr2) {
167
+ if (!Array.isArray(arr) || !Array.isArray(arr2)) {
168
+ return false;
169
+ }
170
+
171
+ return arr.length === arr2.length && arr.every((elt, idx) => elt === arr2[idx]);
172
+ }
173
+
174
+ function tagsStrictEqual(tags, tags2) {
175
+ const tagsKeys = Object.keys(tags);
176
+ const tags2Keys = Object.keys(tags2);
177
+ return tagsKeys.length === tags2Keys.length && tagsKeys.every(key => tags2[key] === tags[key]);
178
+ }
179
+
180
+ function makeStateMachine(stateTable) {
181
+ return function stateTransition(target, newState) {
182
+ const legalStates = stateTable[target.s.state];
183
+ if (legalStates && legalStates.indexOf(newState) < 0) {
184
+ throw new TypeError(
185
+ `illegal state transition from [${
186
+ target.s.state
187
+ }] => [${newState}], allowed: [${legalStates}]`
188
+ );
189
+ }
190
+
191
+ target.emit('stateChanged', target.s.state, newState);
192
+ target.s.state = newState;
193
+ };
194
+ }
195
+
161
196
  module.exports = {
162
197
  uuidV4,
163
198
  calculateDurationInMs,
@@ -168,5 +203,8 @@ module.exports = {
168
203
  maxWireVersion,
169
204
  isPromiseLike,
170
205
  eachAsync,
171
- isUnifiedTopology
206
+ isUnifiedTopology,
207
+ arrayStrictEqual,
208
+ tagsStrictEqual,
209
+ makeStateMachine
172
210
  };
package/lib/cursor.js CHANGED
@@ -543,7 +543,7 @@ class Cursor extends CoreCursor {
543
543
  /**
544
544
  * Set the batch size for the cursor.
545
545
  * @method
546
- * @param {number} value The batchSize for the cursor.
546
+ * @param {number} value The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/find/|find command documentation}.
547
547
  * @throws {MongoError}
548
548
  * @return {Cursor}
549
549
  */
package/lib/db.js CHANGED
@@ -302,7 +302,7 @@ Db.prototype.command = function(command, options, callback) {
302
302
  * @param {object} [options] Optional settings.
303
303
  * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
304
304
  * @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.
305
- * @param {number} [options.cursor.batchSize] The batchSize for the cursor
305
+ * @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}.
306
306
  * @param {boolean} [options.explain=false] Explain returns the aggregation execution plan (requires mongodb 2.6 >).
307
307
  * @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 >).
308
308
  * @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.
@@ -563,7 +563,7 @@ Db.prototype.stats = function(options, callback) {
563
563
  * @param {object} [filter={}] Query to filter collections by
564
564
  * @param {object} [options] Optional settings.
565
565
  * @param {boolean} [options.nameOnly=false] Since 4.0: If true, will only return the collection name in the response, and will omit additional info
566
- * @param {number} [options.batchSize] The batchSize for the returned command cursor or if pre 2.8 the systems batch collection
566
+ * @param {number} [options.batchSize=1000] The batchSize for the returned command cursor or if pre 2.8 the systems batch collection
567
567
  * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
568
568
  * @param {ClientSession} [options.session] optional session to use for this operation
569
569
  * @return {CommandCursor}
@@ -930,7 +930,7 @@ Db.prototype.unref = function() {
930
930
  * @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.
931
931
  * @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.
932
932
  * @param {number} [options.maxAwaitTimeMS] The maximum amount of time for the server to wait on new documents to satisfy a change stream query
933
- * @param {number} [options.batchSize] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
933
+ * @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}.
934
934
  * @param {object} [options.collation] Specify collation settings for operation. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
935
935
  * @param {ReadPreference} [options.readPreference] The read preference. Defaults to the read preference of the database. See {@link https://docs.mongodb.com/manual/reference/read-preference|read preference documentation}.
936
936
  * @param {Timestamp} [options.startAtOperationTime] receive change events that occur after the specified timestamp
@@ -185,12 +185,19 @@ function doRead(_this) {
185
185
  }
186
186
  if (!doc) {
187
187
  _this.push(null);
188
- return _this.s.cursor.close(function(error) {
189
- if (error) {
190
- return __handleError(_this, error);
191
- }
192
- _this.emit('close');
188
+
189
+ process.nextTick(() => {
190
+ _this.s.cursor.close(function(error) {
191
+ if (error) {
192
+ __handleError(_this, error);
193
+ return;
194
+ }
195
+
196
+ _this.emit('close');
197
+ });
193
198
  });
199
+
200
+ return;
194
201
  }
195
202
 
196
203
  var bytesRemaining = _this.s.file.length - _this.s.bytesRead;
@@ -198,7 +198,7 @@ function _delete(_this, id, callback) {
198
198
  * @method
199
199
  * @param {Object} filter
200
200
  * @param {Object} [options] Optional settings for cursor
201
- * @param {number} [options.batchSize] Optional batch size for cursor
201
+ * @param {number} [options.batchSize=1000] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/find|find command documentation}.
202
202
  * @param {number} [options.limit] Optional limit for cursor
203
203
  * @param {number} [options.maxTimeMS] Optional maxTimeMS for cursor
204
204
  * @param {boolean} [options.noCursorTimeout] Optionally set cursor's `noCursorTimeout` flag