mongodb 3.2.5 → 3.3.0-beta2

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 (133) hide show
  1. package/HISTORY.md +0 -10
  2. package/index.js +4 -4
  3. package/lib/admin.js +56 -56
  4. package/lib/aggregation_cursor.js +7 -3
  5. package/lib/bulk/common.js +18 -13
  6. package/lib/change_stream.js +196 -89
  7. package/lib/collection.js +217 -169
  8. package/lib/command_cursor.js +17 -7
  9. package/lib/core/auth/auth_provider.js +158 -0
  10. package/lib/core/auth/defaultAuthProviders.js +29 -0
  11. package/lib/core/auth/gssapi.js +241 -0
  12. package/lib/core/auth/mongo_credentials.js +81 -0
  13. package/lib/core/auth/mongocr.js +51 -0
  14. package/lib/core/auth/plain.js +35 -0
  15. package/lib/core/auth/scram.js +293 -0
  16. package/lib/core/auth/sspi.js +131 -0
  17. package/lib/core/auth/x509.js +26 -0
  18. package/lib/core/connection/apm.js +236 -0
  19. package/lib/core/connection/command_result.js +36 -0
  20. package/lib/core/connection/commands.js +507 -0
  21. package/lib/core/connection/connect.js +370 -0
  22. package/lib/core/connection/connection.js +624 -0
  23. package/lib/core/connection/logger.js +246 -0
  24. package/lib/core/connection/msg.js +219 -0
  25. package/lib/core/connection/pool.js +1285 -0
  26. package/lib/core/connection/utils.js +57 -0
  27. package/lib/core/cursor.js +752 -0
  28. package/lib/core/error.js +186 -0
  29. package/lib/core/index.js +50 -0
  30. package/lib/core/sdam/monitoring.js +228 -0
  31. package/lib/core/sdam/server.js +467 -0
  32. package/lib/core/sdam/server_description.js +163 -0
  33. package/lib/core/sdam/server_selectors.js +244 -0
  34. package/lib/core/sdam/srv_polling.js +135 -0
  35. package/lib/core/sdam/topology.js +1151 -0
  36. package/lib/core/sdam/topology_description.js +408 -0
  37. package/lib/core/sessions.js +711 -0
  38. package/lib/core/tools/smoke_plugin.js +61 -0
  39. package/lib/core/topologies/mongos.js +1337 -0
  40. package/lib/core/topologies/read_preference.js +202 -0
  41. package/lib/core/topologies/replset.js +1507 -0
  42. package/lib/core/topologies/replset_state.js +1121 -0
  43. package/lib/core/topologies/server.js +984 -0
  44. package/lib/core/topologies/shared.js +453 -0
  45. package/lib/core/transactions.js +167 -0
  46. package/lib/core/uri_parser.js +631 -0
  47. package/lib/core/utils.js +165 -0
  48. package/lib/core/wireprotocol/command.js +170 -0
  49. package/lib/core/wireprotocol/compression.js +73 -0
  50. package/lib/core/wireprotocol/constants.js +13 -0
  51. package/lib/core/wireprotocol/get_more.js +86 -0
  52. package/lib/core/wireprotocol/index.js +18 -0
  53. package/lib/core/wireprotocol/kill_cursors.js +70 -0
  54. package/lib/core/wireprotocol/query.js +224 -0
  55. package/lib/core/wireprotocol/shared.js +115 -0
  56. package/lib/core/wireprotocol/write_command.js +50 -0
  57. package/lib/cursor.js +40 -46
  58. package/lib/db.js +141 -95
  59. package/lib/dynamic_loaders.js +32 -0
  60. package/lib/error.js +12 -10
  61. package/lib/gridfs/chunk.js +2 -2
  62. package/lib/gridfs/grid_store.js +31 -25
  63. package/lib/gridfs-stream/index.js +4 -4
  64. package/lib/gridfs-stream/upload.js +1 -1
  65. package/lib/mongo_client.js +37 -15
  66. package/lib/operations/add_user.js +96 -0
  67. package/lib/operations/aggregate.js +24 -13
  68. package/lib/operations/aggregate_operation.js +127 -0
  69. package/lib/operations/bulk_write.js +104 -0
  70. package/lib/operations/close.js +47 -0
  71. package/lib/operations/collection_ops.js +28 -287
  72. package/lib/operations/collections.js +55 -0
  73. package/lib/operations/command.js +120 -0
  74. package/lib/operations/command_v2.js +43 -0
  75. package/lib/operations/common_functions.js +372 -0
  76. package/lib/operations/{mongo_client_ops.js → connect.js} +185 -157
  77. package/lib/operations/count.js +72 -0
  78. package/lib/operations/count_documents.js +46 -0
  79. package/lib/operations/create_collection.js +118 -0
  80. package/lib/operations/create_index.js +92 -0
  81. package/lib/operations/create_indexes.js +61 -0
  82. package/lib/operations/cursor_ops.js +3 -4
  83. package/lib/operations/db_ops.js +15 -12
  84. package/lib/operations/delete_many.js +25 -0
  85. package/lib/operations/delete_one.js +25 -0
  86. package/lib/operations/distinct.js +85 -0
  87. package/lib/operations/drop.js +53 -0
  88. package/lib/operations/drop_index.js +42 -0
  89. package/lib/operations/drop_indexes.js +23 -0
  90. package/lib/operations/estimated_document_count.js +33 -0
  91. package/lib/operations/execute_db_admin_command.js +34 -0
  92. package/lib/operations/execute_operation.js +165 -0
  93. package/lib/operations/explain.js +23 -0
  94. package/lib/operations/find_and_modify.js +98 -0
  95. package/lib/operations/find_one.js +33 -0
  96. package/lib/operations/find_one_and_delete.js +16 -0
  97. package/lib/operations/find_one_and_replace.js +18 -0
  98. package/lib/operations/find_one_and_update.js +19 -0
  99. package/lib/operations/geo_haystack_search.js +79 -0
  100. package/lib/operations/has_next.js +40 -0
  101. package/lib/operations/index_exists.js +39 -0
  102. package/lib/operations/index_information.js +23 -0
  103. package/lib/operations/indexes.js +22 -0
  104. package/lib/operations/insert_many.js +63 -0
  105. package/lib/operations/insert_one.js +75 -0
  106. package/lib/operations/is_capped.js +19 -0
  107. package/lib/operations/list_indexes.js +66 -0
  108. package/lib/operations/map_reduce.js +189 -0
  109. package/lib/operations/next.js +32 -0
  110. package/lib/operations/operation.js +63 -0
  111. package/lib/operations/options_operation.js +32 -0
  112. package/lib/operations/profiling_level.js +31 -0
  113. package/lib/operations/re_index.js +28 -0
  114. package/lib/operations/remove_user.js +52 -0
  115. package/lib/operations/rename.js +61 -0
  116. package/lib/operations/replace_one.js +47 -0
  117. package/lib/operations/set_profiling_level.js +48 -0
  118. package/lib/operations/stats.js +45 -0
  119. package/lib/operations/to_array.js +68 -0
  120. package/lib/operations/update_many.js +29 -0
  121. package/lib/operations/update_one.js +44 -0
  122. package/lib/operations/validate_collection.js +40 -0
  123. package/lib/read_concern.js +55 -0
  124. package/lib/topologies/mongos.js +3 -3
  125. package/lib/topologies/native_topology.js +22 -2
  126. package/lib/topologies/replset.js +3 -3
  127. package/lib/topologies/server.js +4 -4
  128. package/lib/topologies/topology_base.js +6 -6
  129. package/lib/url_parser.js +4 -3
  130. package/lib/utils.js +46 -59
  131. package/lib/write_concern.js +66 -0
  132. package/package.json +15 -6
  133. package/lib/.DS_Store +0 -0
@@ -0,0 +1,1337 @@
1
+ 'use strict';
2
+
3
+ const inherits = require('util').inherits;
4
+ const f = require('util').format;
5
+ const EventEmitter = require('events').EventEmitter;
6
+ const BasicCursor = require('../cursor');
7
+ const Logger = require('../connection/logger');
8
+ const retrieveBSON = require('../connection/utils').retrieveBSON;
9
+ const MongoError = require('../error').MongoError;
10
+ const Server = require('./server');
11
+ const clone = require('./shared').clone;
12
+ const diff = require('./shared').diff;
13
+ const cloneOptions = require('./shared').cloneOptions;
14
+ const createClientInfo = require('./shared').createClientInfo;
15
+ const SessionMixins = require('./shared').SessionMixins;
16
+ const isRetryableWritesSupported = require('./shared').isRetryableWritesSupported;
17
+ const relayEvents = require('../utils').relayEvents;
18
+ const isRetryableError = require('../error').isRetryableError;
19
+ const BSON = retrieveBSON();
20
+
21
+ /**
22
+ * @fileOverview The **Mongos** class is a class that represents a Mongos Proxy topology and is
23
+ * used to construct connections.
24
+ */
25
+
26
+ //
27
+ // States
28
+ var DISCONNECTED = 'disconnected';
29
+ var CONNECTING = 'connecting';
30
+ var CONNECTED = 'connected';
31
+ var UNREFERENCED = 'unreferenced';
32
+ var DESTROYED = 'destroyed';
33
+
34
+ function stateTransition(self, newState) {
35
+ var legalTransitions = {
36
+ disconnected: [CONNECTING, DESTROYED, DISCONNECTED],
37
+ connecting: [CONNECTING, DESTROYED, CONNECTED, DISCONNECTED],
38
+ connected: [CONNECTED, DISCONNECTED, DESTROYED, UNREFERENCED],
39
+ unreferenced: [UNREFERENCED, DESTROYED],
40
+ destroyed: [DESTROYED]
41
+ };
42
+
43
+ // Get current state
44
+ var legalStates = legalTransitions[self.state];
45
+ if (legalStates && legalStates.indexOf(newState) !== -1) {
46
+ self.state = newState;
47
+ } else {
48
+ self.logger.error(
49
+ f(
50
+ 'Pool with id [%s] failed attempted illegal state transition from [%s] to [%s] only following state allowed [%s]',
51
+ self.id,
52
+ self.state,
53
+ newState,
54
+ legalStates
55
+ )
56
+ );
57
+ }
58
+ }
59
+
60
+ //
61
+ // ReplSet instance id
62
+ var id = 1;
63
+ var handlers = ['connect', 'close', 'error', 'timeout', 'parseError'];
64
+
65
+ /**
66
+ * Creates a new Mongos instance
67
+ * @class
68
+ * @param {array} seedlist A list of seeds for the replicaset
69
+ * @param {number} [options.haInterval=5000] The High availability period for replicaset inquiry
70
+ * @param {Cursor} [options.cursorFactory=Cursor] The cursor factory class used for all query cursors
71
+ * @param {number} [options.size=5] Server connection pool size
72
+ * @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
73
+ * @param {number} [options.keepAliveInitialDelay=0] Initial delay before TCP keep alive enabled
74
+ * @param {number} [options.localThresholdMS=15] Cutoff latency point in MS for MongoS proxy selection
75
+ * @param {boolean} [options.noDelay=true] TCP Connection no delay
76
+ * @param {number} [options.connectionTimeout=1000] TCP Connection timeout setting
77
+ * @param {number} [options.socketTimeout=0] TCP Socket timeout setting
78
+ * @param {boolean} [options.ssl=false] Use SSL for connection
79
+ * @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.
80
+ * @param {Buffer} [options.ca] SSL Certificate store binary buffer
81
+ * @param {Buffer} [options.crl] SSL Certificate revocation store binary buffer
82
+ * @param {Buffer} [options.cert] SSL Certificate binary buffer
83
+ * @param {Buffer} [options.key] SSL Key file binary buffer
84
+ * @param {string} [options.passphrase] SSL Certificate pass phrase
85
+ * @param {string} [options.servername=null] String containing the server name requested via TLS SNI.
86
+ * @param {boolean} [options.rejectUnauthorized=true] Reject unauthorized server certificates
87
+ * @param {boolean} [options.promoteLongs=true] Convert Long values from the db into Numbers if they fit into 53 bits
88
+ * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
89
+ * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
90
+ * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
91
+ * @param {boolean} [options.monitorCommands=false] Enable command monitoring for this topology
92
+ * @return {Mongos} A cursor instance
93
+ * @fires Mongos#connect
94
+ * @fires Mongos#reconnect
95
+ * @fires Mongos#joined
96
+ * @fires Mongos#left
97
+ * @fires Mongos#failed
98
+ * @fires Mongos#fullsetup
99
+ * @fires Mongos#all
100
+ * @fires Mongos#serverHeartbeatStarted
101
+ * @fires Mongos#serverHeartbeatSucceeded
102
+ * @fires Mongos#serverHeartbeatFailed
103
+ * @fires Mongos#topologyOpening
104
+ * @fires Mongos#topologyClosed
105
+ * @fires Mongos#topologyDescriptionChanged
106
+ * @property {string} type the topology type.
107
+ * @property {string} parserType the parser type used (c++ or js).
108
+ */
109
+ var Mongos = function(seedlist, options) {
110
+ options = options || {};
111
+
112
+ // Get replSet Id
113
+ this.id = id++;
114
+
115
+ // Internal state
116
+ this.s = {
117
+ options: Object.assign({}, options),
118
+ // BSON instance
119
+ bson:
120
+ options.bson ||
121
+ new BSON([
122
+ BSON.Binary,
123
+ BSON.Code,
124
+ BSON.DBRef,
125
+ BSON.Decimal128,
126
+ BSON.Double,
127
+ BSON.Int32,
128
+ BSON.Long,
129
+ BSON.Map,
130
+ BSON.MaxKey,
131
+ BSON.MinKey,
132
+ BSON.ObjectId,
133
+ BSON.BSONRegExp,
134
+ BSON.Symbol,
135
+ BSON.Timestamp
136
+ ]),
137
+ // Factory overrides
138
+ Cursor: options.cursorFactory || BasicCursor,
139
+ // Logger instance
140
+ logger: Logger('Mongos', options),
141
+ // Seedlist
142
+ seedlist: seedlist,
143
+ // Ha interval
144
+ haInterval: options.haInterval ? options.haInterval : 10000,
145
+ // Disconnect handler
146
+ disconnectHandler: options.disconnectHandler,
147
+ // Server selection index
148
+ index: 0,
149
+ // Connect function options passed in
150
+ connectOptions: {},
151
+ // Are we running in debug mode
152
+ debug: typeof options.debug === 'boolean' ? options.debug : false,
153
+ // localThresholdMS
154
+ localThresholdMS: options.localThresholdMS || 15,
155
+ // Client info
156
+ clientInfo: createClientInfo(options)
157
+ };
158
+
159
+ // Set the client info
160
+ this.s.options.clientInfo = createClientInfo(options);
161
+
162
+ // Log info warning if the socketTimeout < haInterval as it will cause
163
+ // a lot of recycled connections to happen.
164
+ if (
165
+ this.s.logger.isWarn() &&
166
+ this.s.options.socketTimeout !== 0 &&
167
+ this.s.options.socketTimeout < this.s.haInterval
168
+ ) {
169
+ this.s.logger.warn(
170
+ f(
171
+ 'warning socketTimeout %s is less than haInterval %s. This might cause unnecessary server reconnections due to socket timeouts',
172
+ this.s.options.socketTimeout,
173
+ this.s.haInterval
174
+ )
175
+ );
176
+ }
177
+
178
+ // Disconnected state
179
+ this.state = DISCONNECTED;
180
+
181
+ // Current proxies we are connecting to
182
+ this.connectingProxies = [];
183
+ // Currently connected proxies
184
+ this.connectedProxies = [];
185
+ // Disconnected proxies
186
+ this.disconnectedProxies = [];
187
+ // Index of proxy to run operations against
188
+ this.index = 0;
189
+ // High availability timeout id
190
+ this.haTimeoutId = null;
191
+ // Last ismaster
192
+ this.ismaster = null;
193
+
194
+ // Description of the Replicaset
195
+ this.topologyDescription = {
196
+ topologyType: 'Unknown',
197
+ servers: []
198
+ };
199
+
200
+ // Highest clusterTime seen in responses from the current deployment
201
+ this.clusterTime = null;
202
+
203
+ // Add event listener
204
+ EventEmitter.call(this);
205
+ };
206
+
207
+ inherits(Mongos, EventEmitter);
208
+ Object.assign(Mongos.prototype, SessionMixins);
209
+
210
+ Object.defineProperty(Mongos.prototype, 'type', {
211
+ enumerable: true,
212
+ get: function() {
213
+ return 'mongos';
214
+ }
215
+ });
216
+
217
+ Object.defineProperty(Mongos.prototype, 'parserType', {
218
+ enumerable: true,
219
+ get: function() {
220
+ return BSON.native ? 'c++' : 'js';
221
+ }
222
+ });
223
+
224
+ Object.defineProperty(Mongos.prototype, 'logicalSessionTimeoutMinutes', {
225
+ enumerable: true,
226
+ get: function() {
227
+ if (!this.ismaster) return null;
228
+ return this.ismaster.logicalSessionTimeoutMinutes || null;
229
+ }
230
+ });
231
+
232
+ /**
233
+ * Emit event if it exists
234
+ * @method
235
+ */
236
+ function emitSDAMEvent(self, event, description) {
237
+ if (self.listeners(event).length > 0) {
238
+ self.emit(event, description);
239
+ }
240
+ }
241
+
242
+ const SERVER_EVENTS = ['serverDescriptionChanged', 'error', 'close', 'timeout', 'parseError'];
243
+ function destroyServer(server, options, callback) {
244
+ options = options || {};
245
+ SERVER_EVENTS.forEach(event => server.removeAllListeners(event));
246
+ server.destroy(options, callback);
247
+ }
248
+
249
+ /**
250
+ * Initiate server connect
251
+ */
252
+ Mongos.prototype.connect = function(options) {
253
+ var self = this;
254
+ // Add any connect level options to the internal state
255
+ this.s.connectOptions = options || {};
256
+
257
+ // Set connecting state
258
+ stateTransition(this, CONNECTING);
259
+
260
+ // Create server instances
261
+ var servers = this.s.seedlist.map(function(x) {
262
+ const server = new Server(
263
+ Object.assign({}, self.s.options, x, options, {
264
+ reconnect: false,
265
+ monitoring: false,
266
+ parent: self,
267
+ clientInfo: clone(self.s.clientInfo)
268
+ })
269
+ );
270
+
271
+ relayEvents(server, self, ['serverDescriptionChanged']);
272
+ return server;
273
+ });
274
+
275
+ // Emit the topology opening event
276
+ emitSDAMEvent(this, 'topologyOpening', { topologyId: this.id });
277
+
278
+ // Start all server connections
279
+ connectProxies(self, servers);
280
+ };
281
+
282
+ /**
283
+ * Authenticate the topology.
284
+ * @method
285
+ * @param {MongoCredentials} credentials The credentials for authentication we are using
286
+ * @param {authResultCallback} callback A callback function
287
+ */
288
+ Mongos.prototype.auth = function(credentials, callback) {
289
+ if (typeof callback === 'function') callback(null, null);
290
+ };
291
+
292
+ function handleEvent(self) {
293
+ return function() {
294
+ if (self.state === DESTROYED) return;
295
+ // Move to list of disconnectedProxies
296
+ moveServerFrom(self.connectedProxies, self.disconnectedProxies, this);
297
+ // Emit the initial topology
298
+ emitTopologyDescriptionChanged(self);
299
+ // Emit the left signal
300
+ self.emit('left', 'mongos', this);
301
+ // Emit the sdam event
302
+ self.emit('serverClosed', {
303
+ topologyId: self.id,
304
+ address: this.name
305
+ });
306
+ };
307
+ }
308
+
309
+ function handleInitialConnectEvent(self, event) {
310
+ return function() {
311
+ var _this = this;
312
+
313
+ // Destroy the instance
314
+ if (self.state === DESTROYED) {
315
+ // Emit the initial topology
316
+ emitTopologyDescriptionChanged(self);
317
+ // Move from connectingProxies
318
+ moveServerFrom(self.connectingProxies, self.disconnectedProxies, this);
319
+ return this.destroy();
320
+ }
321
+
322
+ // Check the type of server
323
+ if (event === 'connect') {
324
+ // Get last known ismaster
325
+ self.ismaster = _this.lastIsMaster();
326
+
327
+ // Is this not a proxy, remove t
328
+ if (self.ismaster.msg === 'isdbgrid') {
329
+ // Add to the connectd list
330
+ for (let i = 0; i < self.connectedProxies.length; i++) {
331
+ if (self.connectedProxies[i].name === _this.name) {
332
+ // Move from connectingProxies
333
+ moveServerFrom(self.connectingProxies, self.disconnectedProxies, _this);
334
+ // Emit the initial topology
335
+ emitTopologyDescriptionChanged(self);
336
+ _this.destroy();
337
+ return self.emit('failed', _this);
338
+ }
339
+ }
340
+
341
+ // Remove the handlers
342
+ for (let i = 0; i < handlers.length; i++) {
343
+ _this.removeAllListeners(handlers[i]);
344
+ }
345
+
346
+ // Add stable state handlers
347
+ _this.on('error', handleEvent(self, 'error'));
348
+ _this.on('close', handleEvent(self, 'close'));
349
+ _this.on('timeout', handleEvent(self, 'timeout'));
350
+ _this.on('parseError', handleEvent(self, 'parseError'));
351
+
352
+ // Move from connecting proxies connected
353
+ moveServerFrom(self.connectingProxies, self.connectedProxies, _this);
354
+ // Emit the joined event
355
+ self.emit('joined', 'mongos', _this);
356
+ } else {
357
+ // Print warning if we did not find a mongos proxy
358
+ if (self.s.logger.isWarn()) {
359
+ var message = 'expected mongos proxy, but found replicaset member mongod for server %s';
360
+ // We have a standalone server
361
+ if (!self.ismaster.hosts) {
362
+ message = 'expected mongos proxy, but found standalone mongod for server %s';
363
+ }
364
+
365
+ self.s.logger.warn(f(message, _this.name));
366
+ }
367
+
368
+ // This is not a mongos proxy, remove it completely
369
+ removeProxyFrom(self.connectingProxies, _this);
370
+ // Emit the left event
371
+ self.emit('left', 'server', _this);
372
+ // Emit failed event
373
+ self.emit('failed', _this);
374
+ }
375
+ } else {
376
+ moveServerFrom(self.connectingProxies, self.disconnectedProxies, this);
377
+ // Emit the left event
378
+ self.emit('left', 'mongos', this);
379
+ // Emit failed event
380
+ self.emit('failed', this);
381
+ }
382
+
383
+ // Emit the initial topology
384
+ emitTopologyDescriptionChanged(self);
385
+
386
+ // Trigger topologyMonitor
387
+ if (self.connectingProxies.length === 0) {
388
+ // Emit connected if we are connected
389
+ if (self.connectedProxies.length > 0 && self.state === CONNECTING) {
390
+ // Set the state to connected
391
+ stateTransition(self, CONNECTED);
392
+ // Emit the connect event
393
+ self.emit('connect', self);
394
+ self.emit('fullsetup', self);
395
+ self.emit('all', self);
396
+ } else if (self.disconnectedProxies.length === 0) {
397
+ // Print warning if we did not find a mongos proxy
398
+ if (self.s.logger.isWarn()) {
399
+ self.s.logger.warn(
400
+ f('no mongos proxies found in seed list, did you mean to connect to a replicaset')
401
+ );
402
+ }
403
+
404
+ // Emit the error that no proxies were found
405
+ return self.emit('error', new MongoError('no mongos proxies found in seed list'));
406
+ }
407
+
408
+ // Topology monitor
409
+ topologyMonitor(self, { firstConnect: true });
410
+ }
411
+ };
412
+ }
413
+
414
+ function connectProxies(self, servers) {
415
+ // Update connectingProxies
416
+ self.connectingProxies = self.connectingProxies.concat(servers);
417
+
418
+ // Index used to interleaf the server connects, avoiding
419
+ // runtime issues on io constrained vm's
420
+ var timeoutInterval = 0;
421
+
422
+ function connect(server, timeoutInterval) {
423
+ setTimeout(function() {
424
+ // Emit opening server event
425
+ self.emit('serverOpening', {
426
+ topologyId: self.id,
427
+ address: server.name
428
+ });
429
+
430
+ // Emit the initial topology
431
+ emitTopologyDescriptionChanged(self);
432
+
433
+ // Add event handlers
434
+ server.once('close', handleInitialConnectEvent(self, 'close'));
435
+ server.once('timeout', handleInitialConnectEvent(self, 'timeout'));
436
+ server.once('parseError', handleInitialConnectEvent(self, 'parseError'));
437
+ server.once('error', handleInitialConnectEvent(self, 'error'));
438
+ server.once('connect', handleInitialConnectEvent(self, 'connect'));
439
+
440
+ // Command Monitoring events
441
+ relayEvents(server, self, ['commandStarted', 'commandSucceeded', 'commandFailed']);
442
+
443
+ // Start connection
444
+ server.connect(self.s.connectOptions);
445
+ }, timeoutInterval);
446
+ }
447
+ // Start all the servers
448
+ while (servers.length > 0) {
449
+ connect(servers.shift(), timeoutInterval++);
450
+ }
451
+ }
452
+
453
+ function pickProxy(self, session) {
454
+ // TODO: Destructure :)
455
+ const transaction = session && session.transaction;
456
+
457
+ if (transaction && transaction.server) {
458
+ if (transaction.server.isConnected()) {
459
+ return transaction.server;
460
+ } else {
461
+ transaction.unpinServer();
462
+ }
463
+ }
464
+
465
+ // Get the currently connected Proxies
466
+ var connectedProxies = self.connectedProxies.slice(0);
467
+
468
+ // Set lower bound
469
+ var lowerBoundLatency = Number.MAX_VALUE;
470
+
471
+ // Determine the lower bound for the Proxies
472
+ for (var i = 0; i < connectedProxies.length; i++) {
473
+ if (connectedProxies[i].lastIsMasterMS < lowerBoundLatency) {
474
+ lowerBoundLatency = connectedProxies[i].lastIsMasterMS;
475
+ }
476
+ }
477
+
478
+ // Filter out the possible servers
479
+ connectedProxies = connectedProxies.filter(function(server) {
480
+ if (
481
+ server.lastIsMasterMS <= lowerBoundLatency + self.s.localThresholdMS &&
482
+ server.isConnected()
483
+ ) {
484
+ return true;
485
+ }
486
+ });
487
+
488
+ let proxy;
489
+
490
+ // We have no connectedProxies pick first of the connected ones
491
+ if (connectedProxies.length === 0) {
492
+ proxy = self.connectedProxies[0];
493
+ } else {
494
+ // Get proxy
495
+ proxy = connectedProxies[self.index % connectedProxies.length];
496
+ // Update the index
497
+ self.index = (self.index + 1) % connectedProxies.length;
498
+ }
499
+
500
+ if (transaction && transaction.isActive && proxy && proxy.isConnected()) {
501
+ transaction.pinServer(proxy);
502
+ }
503
+
504
+ // Return the proxy
505
+ return proxy;
506
+ }
507
+
508
+ function moveServerFrom(from, to, proxy) {
509
+ for (var i = 0; i < from.length; i++) {
510
+ if (from[i].name === proxy.name) {
511
+ from.splice(i, 1);
512
+ }
513
+ }
514
+
515
+ for (i = 0; i < to.length; i++) {
516
+ if (to[i].name === proxy.name) {
517
+ to.splice(i, 1);
518
+ }
519
+ }
520
+
521
+ to.push(proxy);
522
+ }
523
+
524
+ function removeProxyFrom(from, proxy) {
525
+ for (var i = 0; i < from.length; i++) {
526
+ if (from[i].name === proxy.name) {
527
+ from.splice(i, 1);
528
+ }
529
+ }
530
+ }
531
+
532
+ function reconnectProxies(self, proxies, callback) {
533
+ // Count lefts
534
+ var count = proxies.length;
535
+
536
+ // Handle events
537
+ var _handleEvent = function(self, event) {
538
+ return function() {
539
+ var _self = this;
540
+ count = count - 1;
541
+
542
+ // Destroyed
543
+ if (self.state === DESTROYED || self.state === UNREFERENCED) {
544
+ moveServerFrom(self.connectingProxies, self.disconnectedProxies, _self);
545
+ return this.destroy();
546
+ }
547
+
548
+ if (event === 'connect') {
549
+ // Destroyed
550
+ if (self.state === DESTROYED || self.state === UNREFERENCED) {
551
+ moveServerFrom(self.connectingProxies, self.disconnectedProxies, _self);
552
+ return _self.destroy();
553
+ }
554
+
555
+ // Remove the handlers
556
+ for (var i = 0; i < handlers.length; i++) {
557
+ _self.removeAllListeners(handlers[i]);
558
+ }
559
+
560
+ // Add stable state handlers
561
+ _self.on('error', handleEvent(self, 'error'));
562
+ _self.on('close', handleEvent(self, 'close'));
563
+ _self.on('timeout', handleEvent(self, 'timeout'));
564
+ _self.on('parseError', handleEvent(self, 'parseError'));
565
+
566
+ // Move to the connected servers
567
+ moveServerFrom(self.connectingProxies, self.connectedProxies, _self);
568
+ // Emit topology Change
569
+ emitTopologyDescriptionChanged(self);
570
+ // Emit joined event
571
+ self.emit('joined', 'mongos', _self);
572
+ } else {
573
+ // Move from connectingProxies
574
+ moveServerFrom(self.connectingProxies, self.disconnectedProxies, _self);
575
+ this.destroy();
576
+ }
577
+
578
+ // Are we done finish up callback
579
+ if (count === 0) {
580
+ callback();
581
+ }
582
+ };
583
+ };
584
+
585
+ // No new servers
586
+ if (count === 0) {
587
+ return callback();
588
+ }
589
+
590
+ // Execute method
591
+ function execute(_server, i) {
592
+ setTimeout(function() {
593
+ // Destroyed
594
+ if (self.state === DESTROYED || self.state === UNREFERENCED) {
595
+ return;
596
+ }
597
+
598
+ // Create a new server instance
599
+ var server = new Server(
600
+ Object.assign({}, self.s.options, {
601
+ host: _server.name.split(':')[0],
602
+ port: parseInt(_server.name.split(':')[1], 10),
603
+ reconnect: false,
604
+ monitoring: false,
605
+ parent: self,
606
+ clientInfo: clone(self.s.clientInfo)
607
+ })
608
+ );
609
+
610
+ destroyServer(_server);
611
+ removeProxyFrom(self.disconnectedProxies, _server);
612
+
613
+ // Relay the server description change
614
+ relayEvents(server, self, ['serverDescriptionChanged']);
615
+
616
+ // Emit opening server event
617
+ self.emit('serverOpening', {
618
+ topologyId: server.s.topologyId !== -1 ? server.s.topologyId : self.id,
619
+ address: server.name
620
+ });
621
+
622
+ // Add temp handlers
623
+ server.once('connect', _handleEvent(self, 'connect'));
624
+ server.once('close', _handleEvent(self, 'close'));
625
+ server.once('timeout', _handleEvent(self, 'timeout'));
626
+ server.once('error', _handleEvent(self, 'error'));
627
+ server.once('parseError', _handleEvent(self, 'parseError'));
628
+
629
+ // Command Monitoring events
630
+ relayEvents(server, self, ['commandStarted', 'commandSucceeded', 'commandFailed']);
631
+
632
+ // Connect to proxy
633
+ self.connectingProxies.push(server);
634
+ server.connect(self.s.connectOptions);
635
+ }, i);
636
+ }
637
+
638
+ // Create new instances
639
+ for (var i = 0; i < proxies.length; i++) {
640
+ execute(proxies[i], i);
641
+ }
642
+ }
643
+
644
+ function topologyMonitor(self, options) {
645
+ options = options || {};
646
+
647
+ // Set momitoring timeout
648
+ self.haTimeoutId = setTimeout(function() {
649
+ if (self.state === DESTROYED || self.state === UNREFERENCED) return;
650
+ // If we have a primary and a disconnect handler, execute
651
+ // buffered operations
652
+ if (self.isConnected() && self.s.disconnectHandler) {
653
+ self.s.disconnectHandler.execute();
654
+ }
655
+
656
+ // Get the connectingServers
657
+ var proxies = self.connectedProxies.slice(0);
658
+ // Get the count
659
+ var count = proxies.length;
660
+
661
+ // If the count is zero schedule a new fast
662
+ function pingServer(_self, _server, cb) {
663
+ // Measure running time
664
+ var start = new Date().getTime();
665
+
666
+ // Emit the server heartbeat start
667
+ emitSDAMEvent(self, 'serverHeartbeatStarted', { connectionId: _server.name });
668
+
669
+ // Execute ismaster
670
+ _server.command(
671
+ 'admin.$cmd',
672
+ {
673
+ ismaster: true
674
+ },
675
+ {
676
+ monitoring: true,
677
+ socketTimeout: self.s.options.connectionTimeout || 2000
678
+ },
679
+ function(err, r) {
680
+ if (self.state === DESTROYED || self.state === UNREFERENCED) {
681
+ // Move from connectingProxies
682
+ moveServerFrom(self.connectedProxies, self.disconnectedProxies, _server);
683
+ _server.destroy();
684
+ return cb(err, r);
685
+ }
686
+
687
+ // Calculate latency
688
+ var latencyMS = new Date().getTime() - start;
689
+
690
+ // We had an error, remove it from the state
691
+ if (err) {
692
+ // Emit the server heartbeat failure
693
+ emitSDAMEvent(self, 'serverHeartbeatFailed', {
694
+ durationMS: latencyMS,
695
+ failure: err,
696
+ connectionId: _server.name
697
+ });
698
+ // Move from connected proxies to disconnected proxies
699
+ moveServerFrom(self.connectedProxies, self.disconnectedProxies, _server);
700
+ } else {
701
+ // Update the server ismaster
702
+ _server.ismaster = r.result;
703
+ _server.lastIsMasterMS = latencyMS;
704
+
705
+ // Server heart beat event
706
+ emitSDAMEvent(self, 'serverHeartbeatSucceeded', {
707
+ durationMS: latencyMS,
708
+ reply: r.result,
709
+ connectionId: _server.name
710
+ });
711
+ }
712
+
713
+ cb(err, r);
714
+ }
715
+ );
716
+ }
717
+
718
+ // No proxies initiate monitor again
719
+ if (proxies.length === 0) {
720
+ // Emit close event if any listeners registered
721
+ if (self.listeners('close').length > 0 && self.state === CONNECTING) {
722
+ self.emit('error', new MongoError('no mongos proxy available'));
723
+ } else {
724
+ self.emit('close', self);
725
+ }
726
+
727
+ // Attempt to connect to any unknown servers
728
+ return reconnectProxies(self, self.disconnectedProxies, function() {
729
+ if (self.state === DESTROYED || self.state === UNREFERENCED) return;
730
+
731
+ // Are we connected ? emit connect event
732
+ if (self.state === CONNECTING && options.firstConnect) {
733
+ self.emit('connect', self);
734
+ self.emit('fullsetup', self);
735
+ self.emit('all', self);
736
+ } else if (self.isConnected()) {
737
+ self.emit('reconnect', self);
738
+ } else if (!self.isConnected() && self.listeners('close').length > 0) {
739
+ self.emit('close', self);
740
+ }
741
+
742
+ // Perform topology monitor
743
+ topologyMonitor(self);
744
+ });
745
+ }
746
+
747
+ // Ping all servers
748
+ for (var i = 0; i < proxies.length; i++) {
749
+ pingServer(self, proxies[i], function() {
750
+ count = count - 1;
751
+
752
+ if (count === 0) {
753
+ if (self.state === DESTROYED || self.state === UNREFERENCED) return;
754
+
755
+ // Attempt to connect to any unknown servers
756
+ reconnectProxies(self, self.disconnectedProxies, function() {
757
+ if (self.state === DESTROYED || self.state === UNREFERENCED) return;
758
+ // Perform topology monitor
759
+ topologyMonitor(self);
760
+ });
761
+ }
762
+ });
763
+ }
764
+ }, self.s.haInterval);
765
+ }
766
+
767
+ /**
768
+ * Returns the last known ismaster document for this server
769
+ * @method
770
+ * @return {object}
771
+ */
772
+ Mongos.prototype.lastIsMaster = function() {
773
+ return this.ismaster;
774
+ };
775
+
776
+ /**
777
+ * Unref all connections belong to this server
778
+ * @method
779
+ */
780
+ Mongos.prototype.unref = function() {
781
+ // Transition state
782
+ stateTransition(this, UNREFERENCED);
783
+ // Get all proxies
784
+ var proxies = this.connectedProxies.concat(this.connectingProxies);
785
+ proxies.forEach(function(x) {
786
+ x.unref();
787
+ });
788
+
789
+ clearTimeout(this.haTimeoutId);
790
+ };
791
+
792
+ /**
793
+ * Destroy the server connection
794
+ * @param {boolean} [options.force=false] Force destroy the pool
795
+ * @method
796
+ */
797
+ Mongos.prototype.destroy = function(options, callback) {
798
+ if (this.haTimeoutId) {
799
+ clearTimeout(this.haTimeoutId);
800
+ }
801
+
802
+ const proxies = this.connectedProxies.concat(this.connectingProxies);
803
+ let serverCount = proxies.length;
804
+ const serverDestroyed = () => {
805
+ serverCount--;
806
+ if (serverCount > 0) {
807
+ return;
808
+ }
809
+
810
+ emitTopologyDescriptionChanged(this);
811
+ emitSDAMEvent(this, 'topologyClosed', { topologyId: this.id });
812
+ stateTransition(this, DESTROYED);
813
+ if (typeof callback === 'function') {
814
+ callback(null, null);
815
+ }
816
+ };
817
+
818
+ if (serverCount === 0) {
819
+ serverDestroyed();
820
+ return;
821
+ }
822
+
823
+ // Destroy all connecting servers
824
+ proxies.forEach(server => {
825
+ // Emit the sdam event
826
+ this.emit('serverClosed', {
827
+ topologyId: this.id,
828
+ address: server.name
829
+ });
830
+
831
+ destroyServer(server, options, serverDestroyed);
832
+ moveServerFrom(this.connectedProxies, this.disconnectedProxies, server);
833
+ });
834
+ };
835
+
836
+ /**
837
+ * Figure out if the server is connected
838
+ * @method
839
+ * @return {boolean}
840
+ */
841
+ Mongos.prototype.isConnected = function() {
842
+ return this.connectedProxies.length > 0;
843
+ };
844
+
845
+ /**
846
+ * Figure out if the server instance was destroyed by calling destroy
847
+ * @method
848
+ * @return {boolean}
849
+ */
850
+ Mongos.prototype.isDestroyed = function() {
851
+ return this.state === DESTROYED;
852
+ };
853
+
854
+ //
855
+ // Operations
856
+ //
857
+
858
+ function executeWriteOperation(args, options, callback) {
859
+ if (typeof options === 'function') (callback = options), (options = {});
860
+ options = options || {};
861
+
862
+ // TODO: once we drop Node 4, use destructuring either here or in arguments.
863
+ const self = args.self;
864
+ const op = args.op;
865
+ const ns = args.ns;
866
+ const ops = args.ops;
867
+
868
+ // Pick a server
869
+ let server = pickProxy(self, options.session);
870
+ // No server found error out
871
+ if (!server) return callback(new MongoError('no mongos proxy available'));
872
+
873
+ const willRetryWrite =
874
+ !args.retrying &&
875
+ !!options.retryWrites &&
876
+ options.session &&
877
+ isRetryableWritesSupported(self) &&
878
+ !options.session.inTransaction();
879
+
880
+ const handler = (err, result) => {
881
+ if (!err) return callback(null, result);
882
+ if (!isRetryableError(err) || !willRetryWrite) {
883
+ return callback(err);
884
+ }
885
+
886
+ // Pick another server
887
+ server = pickProxy(self, options.session);
888
+
889
+ // No server found error out with original error
890
+ if (!server) {
891
+ return callback(err);
892
+ }
893
+
894
+ const newArgs = Object.assign({}, args, { retrying: true });
895
+ return executeWriteOperation(newArgs, options, callback);
896
+ };
897
+
898
+ if (callback.operationId) {
899
+ handler.operationId = callback.operationId;
900
+ }
901
+
902
+ // increment and assign txnNumber
903
+ if (willRetryWrite) {
904
+ options.session.incrementTransactionNumber();
905
+ options.willRetryWrite = willRetryWrite;
906
+ }
907
+
908
+ // rerun the operation
909
+ server[op](ns, ops, options, handler);
910
+ }
911
+
912
+ /**
913
+ * Insert one or more documents
914
+ * @method
915
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
916
+ * @param {array} ops An array of documents to insert
917
+ * @param {boolean} [options.ordered=true] Execute in order or out of order
918
+ * @param {object} [options.writeConcern={}] Write concern for the operation
919
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
920
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
921
+ * @param {ClientSession} [options.session=null] Session to use for the operation
922
+ * @param {boolean} [options.retryWrites] Enable retryable writes for this operation
923
+ * @param {opResultCallback} callback A callback function
924
+ */
925
+ Mongos.prototype.insert = function(ns, ops, options, callback) {
926
+ if (typeof options === 'function') {
927
+ (callback = options), (options = {}), (options = options || {});
928
+ }
929
+
930
+ if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
931
+
932
+ // Not connected but we have a disconnecthandler
933
+ if (!this.isConnected() && this.s.disconnectHandler != null) {
934
+ return this.s.disconnectHandler.add('insert', ns, ops, options, callback);
935
+ }
936
+
937
+ // No mongos proxy available
938
+ if (!this.isConnected()) {
939
+ return callback(new MongoError('no mongos proxy available'));
940
+ }
941
+
942
+ // Execute write operation
943
+ executeWriteOperation({ self: this, op: 'insert', ns, ops }, options, callback);
944
+ };
945
+
946
+ /**
947
+ * Perform one or more update operations
948
+ * @method
949
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
950
+ * @param {array} ops An array of updates
951
+ * @param {boolean} [options.ordered=true] Execute in order or out of order
952
+ * @param {object} [options.writeConcern={}] Write concern for the operation
953
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
954
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
955
+ * @param {ClientSession} [options.session=null] Session to use for the operation
956
+ * @param {boolean} [options.retryWrites] Enable retryable writes for this operation
957
+ * @param {opResultCallback} callback A callback function
958
+ */
959
+ Mongos.prototype.update = function(ns, ops, options, callback) {
960
+ if (typeof options === 'function') {
961
+ (callback = options), (options = {}), (options = options || {});
962
+ }
963
+
964
+ if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
965
+
966
+ // Not connected but we have a disconnecthandler
967
+ if (!this.isConnected() && this.s.disconnectHandler != null) {
968
+ return this.s.disconnectHandler.add('update', ns, ops, options, callback);
969
+ }
970
+
971
+ // No mongos proxy available
972
+ if (!this.isConnected()) {
973
+ return callback(new MongoError('no mongos proxy available'));
974
+ }
975
+
976
+ // Execute write operation
977
+ executeWriteOperation({ self: this, op: 'update', ns, ops }, options, callback);
978
+ };
979
+
980
+ /**
981
+ * Perform one or more remove operations
982
+ * @method
983
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
984
+ * @param {array} ops An array of removes
985
+ * @param {boolean} [options.ordered=true] Execute in order or out of order
986
+ * @param {object} [options.writeConcern={}] Write concern for the operation
987
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
988
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
989
+ * @param {ClientSession} [options.session=null] Session to use for the operation
990
+ * @param {boolean} [options.retryWrites] Enable retryable writes for this operation
991
+ * @param {opResultCallback} callback A callback function
992
+ */
993
+ Mongos.prototype.remove = function(ns, ops, options, callback) {
994
+ if (typeof options === 'function') {
995
+ (callback = options), (options = {}), (options = options || {});
996
+ }
997
+
998
+ if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
999
+
1000
+ // Not connected but we have a disconnecthandler
1001
+ if (!this.isConnected() && this.s.disconnectHandler != null) {
1002
+ return this.s.disconnectHandler.add('remove', ns, ops, options, callback);
1003
+ }
1004
+
1005
+ // No mongos proxy available
1006
+ if (!this.isConnected()) {
1007
+ return callback(new MongoError('no mongos proxy available'));
1008
+ }
1009
+
1010
+ // Execute write operation
1011
+ executeWriteOperation({ self: this, op: 'remove', ns, ops }, options, callback);
1012
+ };
1013
+
1014
+ const RETRYABLE_WRITE_OPERATIONS = ['findAndModify', 'insert', 'update', 'delete'];
1015
+
1016
+ function isWriteCommand(command) {
1017
+ return RETRYABLE_WRITE_OPERATIONS.some(op => command[op]);
1018
+ }
1019
+
1020
+ /**
1021
+ * Execute a command
1022
+ * @method
1023
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
1024
+ * @param {object} cmd The command hash
1025
+ * @param {ReadPreference} [options.readPreference] Specify read preference if command supports it
1026
+ * @param {Connection} [options.connection] Specify connection object to execute command against
1027
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
1028
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
1029
+ * @param {ClientSession} [options.session=null] Session to use for the operation
1030
+ * @param {opResultCallback} callback A callback function
1031
+ */
1032
+ Mongos.prototype.command = function(ns, cmd, options, callback) {
1033
+ if (typeof options === 'function') {
1034
+ (callback = options), (options = {}), (options = options || {});
1035
+ }
1036
+
1037
+ if (this.state === DESTROYED) return callback(new MongoError(f('topology was destroyed')));
1038
+ var self = this;
1039
+
1040
+ // Pick a proxy
1041
+ var server = pickProxy(self, options.session);
1042
+
1043
+ // Topology is not connected, save the call in the provided store to be
1044
+ // Executed at some point when the handler deems it's reconnected
1045
+ if ((server == null || !server.isConnected()) && this.s.disconnectHandler != null) {
1046
+ return this.s.disconnectHandler.add('command', ns, cmd, options, callback);
1047
+ }
1048
+
1049
+ // No server returned we had an error
1050
+ if (server == null) {
1051
+ return callback(new MongoError('no mongos proxy available'));
1052
+ }
1053
+
1054
+ // Cloned options
1055
+ var clonedOptions = cloneOptions(options);
1056
+ clonedOptions.topology = self;
1057
+
1058
+ const willRetryWrite =
1059
+ !options.retrying &&
1060
+ options.retryWrites &&
1061
+ options.session &&
1062
+ isRetryableWritesSupported(self) &&
1063
+ !options.session.inTransaction() &&
1064
+ isWriteCommand(cmd);
1065
+
1066
+ const cb = (err, result) => {
1067
+ if (!err) return callback(null, result);
1068
+ if (!isRetryableError(err)) {
1069
+ return callback(err);
1070
+ }
1071
+
1072
+ if (willRetryWrite) {
1073
+ const newOptions = Object.assign({}, clonedOptions, { retrying: true });
1074
+ return this.command(ns, cmd, newOptions, callback);
1075
+ }
1076
+
1077
+ return callback(err);
1078
+ };
1079
+
1080
+ // increment and assign txnNumber
1081
+ if (willRetryWrite) {
1082
+ options.session.incrementTransactionNumber();
1083
+ options.willRetryWrite = willRetryWrite;
1084
+ }
1085
+
1086
+ // Execute the command
1087
+ server.command(ns, cmd, clonedOptions, cb);
1088
+ };
1089
+
1090
+ /**
1091
+ * Get a new cursor
1092
+ * @method
1093
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
1094
+ * @param {object|Long} cmd Can be either a command returning a cursor or a cursorId
1095
+ * @param {object} [options] Options for the cursor
1096
+ * @param {object} [options.batchSize=0] Batchsize for the operation
1097
+ * @param {array} [options.documents=[]] Initial documents list for cursor
1098
+ * @param {ReadPreference} [options.readPreference] Specify read preference if command supports it
1099
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
1100
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
1101
+ * @param {ClientSession} [options.session=null] Session to use for the operation
1102
+ * @param {object} [options.topology] The internal topology of the created cursor
1103
+ * @returns {Cursor}
1104
+ */
1105
+ Mongos.prototype.cursor = function(ns, cmd, options) {
1106
+ options = options || {};
1107
+ const topology = options.topology || this;
1108
+
1109
+ // Set up final cursor type
1110
+ var FinalCursor = options.cursorFactory || this.s.Cursor;
1111
+
1112
+ // Return the cursor
1113
+ return new FinalCursor(topology, ns, cmd, options);
1114
+ };
1115
+
1116
+ /**
1117
+ * Selects a server
1118
+ *
1119
+ * @method
1120
+ * @param {function} selector Unused
1121
+ * @param {ReadPreference} [options.readPreference] Unused
1122
+ * @param {ClientSession} [options.session] Specify a session if it is being used
1123
+ * @param {function} callback
1124
+ */
1125
+ Mongos.prototype.selectServer = function(selector, options, callback) {
1126
+ if (typeof selector === 'function' && typeof callback === 'undefined')
1127
+ (callback = selector), (selector = undefined), (options = {});
1128
+ if (typeof options === 'function')
1129
+ (callback = options), (options = selector), (selector = undefined);
1130
+ options = options || {};
1131
+
1132
+ const server = pickProxy(this, options.session);
1133
+ if (this.s.debug) this.emit('pickedServer', null, server);
1134
+ callback(null, server);
1135
+ };
1136
+
1137
+ /**
1138
+ * All raw connections
1139
+ * @method
1140
+ * @return {Connection[]}
1141
+ */
1142
+ Mongos.prototype.connections = function() {
1143
+ var connections = [];
1144
+
1145
+ for (var i = 0; i < this.connectedProxies.length; i++) {
1146
+ connections = connections.concat(this.connectedProxies[i].connections());
1147
+ }
1148
+
1149
+ return connections;
1150
+ };
1151
+
1152
+ function emitTopologyDescriptionChanged(self) {
1153
+ if (self.listeners('topologyDescriptionChanged').length > 0) {
1154
+ var topology = 'Unknown';
1155
+ if (self.connectedProxies.length > 0) {
1156
+ topology = 'Sharded';
1157
+ }
1158
+
1159
+ // Generate description
1160
+ var description = {
1161
+ topologyType: topology,
1162
+ servers: []
1163
+ };
1164
+
1165
+ // All proxies
1166
+ var proxies = self.disconnectedProxies.concat(self.connectingProxies);
1167
+
1168
+ // Add all the disconnected proxies
1169
+ description.servers = description.servers.concat(
1170
+ proxies.map(function(x) {
1171
+ var description = x.getDescription();
1172
+ description.type = 'Unknown';
1173
+ return description;
1174
+ })
1175
+ );
1176
+
1177
+ // Add all the connected proxies
1178
+ description.servers = description.servers.concat(
1179
+ self.connectedProxies.map(function(x) {
1180
+ var description = x.getDescription();
1181
+ description.type = 'Mongos';
1182
+ return description;
1183
+ })
1184
+ );
1185
+
1186
+ // Get the diff
1187
+ var diffResult = diff(self.topologyDescription, description);
1188
+
1189
+ // Create the result
1190
+ var result = {
1191
+ topologyId: self.id,
1192
+ previousDescription: self.topologyDescription,
1193
+ newDescription: description,
1194
+ diff: diffResult
1195
+ };
1196
+
1197
+ // Emit the topologyDescription change
1198
+ if (diffResult.servers.length > 0) {
1199
+ self.emit('topologyDescriptionChanged', result);
1200
+ }
1201
+
1202
+ // Set the new description
1203
+ self.topologyDescription = description;
1204
+ }
1205
+ }
1206
+
1207
+ /**
1208
+ * A mongos connect event, used to verify that the connection is up and running
1209
+ *
1210
+ * @event Mongos#connect
1211
+ * @type {Mongos}
1212
+ */
1213
+
1214
+ /**
1215
+ * A mongos reconnect event, used to verify that the mongos topology has reconnected
1216
+ *
1217
+ * @event Mongos#reconnect
1218
+ * @type {Mongos}
1219
+ */
1220
+
1221
+ /**
1222
+ * A mongos fullsetup event, used to signal that all topology members have been contacted.
1223
+ *
1224
+ * @event Mongos#fullsetup
1225
+ * @type {Mongos}
1226
+ */
1227
+
1228
+ /**
1229
+ * A mongos all event, used to signal that all topology members have been contacted.
1230
+ *
1231
+ * @event Mongos#all
1232
+ * @type {Mongos}
1233
+ */
1234
+
1235
+ /**
1236
+ * A server member left the mongos list
1237
+ *
1238
+ * @event Mongos#left
1239
+ * @type {Mongos}
1240
+ * @param {string} type The type of member that left (mongos)
1241
+ * @param {Server} server The server object that left
1242
+ */
1243
+
1244
+ /**
1245
+ * A server member joined the mongos list
1246
+ *
1247
+ * @event Mongos#joined
1248
+ * @type {Mongos}
1249
+ * @param {string} type The type of member that left (mongos)
1250
+ * @param {Server} server The server object that joined
1251
+ */
1252
+
1253
+ /**
1254
+ * A server opening SDAM monitoring event
1255
+ *
1256
+ * @event Mongos#serverOpening
1257
+ * @type {object}
1258
+ */
1259
+
1260
+ /**
1261
+ * A server closed SDAM monitoring event
1262
+ *
1263
+ * @event Mongos#serverClosed
1264
+ * @type {object}
1265
+ */
1266
+
1267
+ /**
1268
+ * A server description SDAM change monitoring event
1269
+ *
1270
+ * @event Mongos#serverDescriptionChanged
1271
+ * @type {object}
1272
+ */
1273
+
1274
+ /**
1275
+ * A topology open SDAM event
1276
+ *
1277
+ * @event Mongos#topologyOpening
1278
+ * @type {object}
1279
+ */
1280
+
1281
+ /**
1282
+ * A topology closed SDAM event
1283
+ *
1284
+ * @event Mongos#topologyClosed
1285
+ * @type {object}
1286
+ */
1287
+
1288
+ /**
1289
+ * A topology structure SDAM change event
1290
+ *
1291
+ * @event Mongos#topologyDescriptionChanged
1292
+ * @type {object}
1293
+ */
1294
+
1295
+ /**
1296
+ * A topology serverHeartbeatStarted SDAM event
1297
+ *
1298
+ * @event Mongos#serverHeartbeatStarted
1299
+ * @type {object}
1300
+ */
1301
+
1302
+ /**
1303
+ * A topology serverHeartbeatFailed SDAM event
1304
+ *
1305
+ * @event Mongos#serverHeartbeatFailed
1306
+ * @type {object}
1307
+ */
1308
+
1309
+ /**
1310
+ * A topology serverHeartbeatSucceeded SDAM change event
1311
+ *
1312
+ * @event Mongos#serverHeartbeatSucceeded
1313
+ * @type {object}
1314
+ */
1315
+
1316
+ /**
1317
+ * An event emitted indicating a command was started, if command monitoring is enabled
1318
+ *
1319
+ * @event Mongos#commandStarted
1320
+ * @type {object}
1321
+ */
1322
+
1323
+ /**
1324
+ * An event emitted indicating a command succeeded, if command monitoring is enabled
1325
+ *
1326
+ * @event Mongos#commandSucceeded
1327
+ * @type {object}
1328
+ */
1329
+
1330
+ /**
1331
+ * An event emitted indicating a command failed, if command monitoring is enabled
1332
+ *
1333
+ * @event Mongos#commandFailed
1334
+ * @type {object}
1335
+ */
1336
+
1337
+ module.exports = Mongos;