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,984 @@
1
+ 'use strict';
2
+
3
+ var inherits = require('util').inherits,
4
+ f = require('util').format,
5
+ EventEmitter = require('events').EventEmitter,
6
+ ReadPreference = require('./read_preference'),
7
+ Logger = require('../connection/logger'),
8
+ debugOptions = require('../connection/utils').debugOptions,
9
+ retrieveBSON = require('../connection/utils').retrieveBSON,
10
+ Pool = require('../connection/pool'),
11
+ MongoError = require('../error').MongoError,
12
+ MongoNetworkError = require('../error').MongoNetworkError,
13
+ wireProtocol = require('../wireprotocol'),
14
+ BasicCursor = require('../cursor'),
15
+ sdam = require('./shared'),
16
+ createClientInfo = require('./shared').createClientInfo,
17
+ createCompressionInfo = require('./shared').createCompressionInfo,
18
+ resolveClusterTime = require('./shared').resolveClusterTime,
19
+ SessionMixins = require('./shared').SessionMixins,
20
+ relayEvents = require('../utils').relayEvents;
21
+
22
+ const collationNotSupported = require('../utils').collationNotSupported;
23
+
24
+ // Used for filtering out fields for loggin
25
+ var debugFields = [
26
+ 'reconnect',
27
+ 'reconnectTries',
28
+ 'reconnectInterval',
29
+ 'emitError',
30
+ 'cursorFactory',
31
+ 'host',
32
+ 'port',
33
+ 'size',
34
+ 'keepAlive',
35
+ 'keepAliveInitialDelay',
36
+ 'noDelay',
37
+ 'connectionTimeout',
38
+ 'checkServerIdentity',
39
+ 'socketTimeout',
40
+ 'ssl',
41
+ 'ca',
42
+ 'crl',
43
+ 'cert',
44
+ 'key',
45
+ 'rejectUnauthorized',
46
+ 'promoteLongs',
47
+ 'promoteValues',
48
+ 'promoteBuffers',
49
+ 'servername'
50
+ ];
51
+
52
+ // Server instance id
53
+ var id = 0;
54
+ var serverAccounting = false;
55
+ var servers = {};
56
+ var BSON = retrieveBSON();
57
+
58
+ /**
59
+ * Creates a new Server instance
60
+ * @class
61
+ * @param {boolean} [options.reconnect=true] Server will attempt to reconnect on loss of connection
62
+ * @param {number} [options.reconnectTries=30] Server attempt to reconnect #times
63
+ * @param {number} [options.reconnectInterval=1000] Server will wait # milliseconds between retries
64
+ * @param {number} [options.monitoring=true] Enable the server state monitoring (calling ismaster at monitoringInterval)
65
+ * @param {number} [options.monitoringInterval=5000] The interval of calling ismaster when monitoring is enabled.
66
+ * @param {Cursor} [options.cursorFactory=Cursor] The cursor factory class used for all query cursors
67
+ * @param {string} options.host The server host
68
+ * @param {number} options.port The server port
69
+ * @param {number} [options.size=5] Server connection pool size
70
+ * @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
71
+ * @param {number} [options.keepAliveInitialDelay=300000] Initial delay before TCP keep alive enabled
72
+ * @param {boolean} [options.noDelay=true] TCP Connection no delay
73
+ * @param {number} [options.connectionTimeout=30000] TCP Connection timeout setting
74
+ * @param {number} [options.socketTimeout=360000] TCP Socket timeout setting
75
+ * @param {boolean} [options.ssl=false] Use SSL for connection
76
+ * @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.
77
+ * @param {Buffer} [options.ca] SSL Certificate store binary buffer
78
+ * @param {Buffer} [options.crl] SSL Certificate revocation store binary buffer
79
+ * @param {Buffer} [options.cert] SSL Certificate binary buffer
80
+ * @param {Buffer} [options.key] SSL Key file binary buffer
81
+ * @param {string} [options.passphrase] SSL Certificate pass phrase
82
+ * @param {boolean} [options.rejectUnauthorized=true] Reject unauthorized server certificates
83
+ * @param {string} [options.servername=null] String containing the server name requested via TLS SNI.
84
+ * @param {boolean} [options.promoteLongs=true] Convert Long values from the db into Numbers if they fit into 53 bits
85
+ * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
86
+ * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
87
+ * @param {string} [options.appname=null] Application name, passed in on ismaster call and logged in mongod server logs. Maximum size 128 bytes.
88
+ * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
89
+ * @param {boolean} [options.monitorCommands=false] Enable command monitoring for this topology
90
+ * @return {Server} A cursor instance
91
+ * @fires Server#connect
92
+ * @fires Server#close
93
+ * @fires Server#error
94
+ * @fires Server#timeout
95
+ * @fires Server#parseError
96
+ * @fires Server#reconnect
97
+ * @fires Server#reconnectFailed
98
+ * @fires Server#serverHeartbeatStarted
99
+ * @fires Server#serverHeartbeatSucceeded
100
+ * @fires Server#serverHeartbeatFailed
101
+ * @fires Server#topologyOpening
102
+ * @fires Server#topologyClosed
103
+ * @fires Server#topologyDescriptionChanged
104
+ * @property {string} type the topology type.
105
+ * @property {string} parserType the parser type used (c++ or js).
106
+ */
107
+ var Server = function(options) {
108
+ options = options || {};
109
+
110
+ // Add event listener
111
+ EventEmitter.call(this);
112
+
113
+ // Server instance id
114
+ this.id = id++;
115
+
116
+ // Internal state
117
+ this.s = {
118
+ // Options
119
+ options: options,
120
+ // Logger
121
+ logger: Logger('Server', options),
122
+ // Factory overrides
123
+ Cursor: options.cursorFactory || BasicCursor,
124
+ // BSON instance
125
+ bson:
126
+ options.bson ||
127
+ new BSON([
128
+ BSON.Binary,
129
+ BSON.Code,
130
+ BSON.DBRef,
131
+ BSON.Decimal128,
132
+ BSON.Double,
133
+ BSON.Int32,
134
+ BSON.Long,
135
+ BSON.Map,
136
+ BSON.MaxKey,
137
+ BSON.MinKey,
138
+ BSON.ObjectId,
139
+ BSON.BSONRegExp,
140
+ BSON.Symbol,
141
+ BSON.Timestamp
142
+ ]),
143
+ // Pool
144
+ pool: null,
145
+ // Disconnect handler
146
+ disconnectHandler: options.disconnectHandler,
147
+ // Monitor thread (keeps the connection alive)
148
+ monitoring: typeof options.monitoring === 'boolean' ? options.monitoring : true,
149
+ // Is the server in a topology
150
+ inTopology: !!options.parent,
151
+ // Monitoring timeout
152
+ monitoringInterval:
153
+ typeof options.monitoringInterval === 'number' ? options.monitoringInterval : 5000,
154
+ // Topology id
155
+ topologyId: -1,
156
+ compression: { compressors: createCompressionInfo(options) },
157
+ // Optional parent topology
158
+ parent: options.parent
159
+ };
160
+
161
+ // If this is a single deployment we need to track the clusterTime here
162
+ if (!this.s.parent) {
163
+ this.s.clusterTime = null;
164
+ }
165
+
166
+ // Curent ismaster
167
+ this.ismaster = null;
168
+ // Current ping time
169
+ this.lastIsMasterMS = -1;
170
+ // The monitoringProcessId
171
+ this.monitoringProcessId = null;
172
+ // Initial connection
173
+ this.initialConnect = true;
174
+ // Default type
175
+ this._type = 'server';
176
+ // Set the client info
177
+ this.clientInfo = createClientInfo(options);
178
+
179
+ // Max Stalleness values
180
+ // last time we updated the ismaster state
181
+ this.lastUpdateTime = 0;
182
+ // Last write time
183
+ this.lastWriteDate = 0;
184
+ // Stalleness
185
+ this.staleness = 0;
186
+ };
187
+
188
+ inherits(Server, EventEmitter);
189
+ Object.assign(Server.prototype, SessionMixins);
190
+
191
+ Object.defineProperty(Server.prototype, 'type', {
192
+ enumerable: true,
193
+ get: function() {
194
+ return this._type;
195
+ }
196
+ });
197
+
198
+ Object.defineProperty(Server.prototype, 'parserType', {
199
+ enumerable: true,
200
+ get: function() {
201
+ return BSON.native ? 'c++' : 'js';
202
+ }
203
+ });
204
+
205
+ Object.defineProperty(Server.prototype, 'logicalSessionTimeoutMinutes', {
206
+ enumerable: true,
207
+ get: function() {
208
+ if (!this.ismaster) return null;
209
+ return this.ismaster.logicalSessionTimeoutMinutes || null;
210
+ }
211
+ });
212
+
213
+ // In single server deployments we track the clusterTime directly on the topology, however
214
+ // in Mongos and ReplSet deployments we instead need to delegate the clusterTime up to the
215
+ // tracking objects so we can ensure we are gossiping the maximum time received from the
216
+ // server.
217
+ Object.defineProperty(Server.prototype, 'clusterTime', {
218
+ enumerable: true,
219
+ set: function(clusterTime) {
220
+ const settings = this.s.parent ? this.s.parent : this.s;
221
+ resolveClusterTime(settings, clusterTime);
222
+ },
223
+ get: function() {
224
+ const settings = this.s.parent ? this.s.parent : this.s;
225
+ return settings.clusterTime || null;
226
+ }
227
+ });
228
+
229
+ Server.enableServerAccounting = function() {
230
+ serverAccounting = true;
231
+ servers = {};
232
+ };
233
+
234
+ Server.disableServerAccounting = function() {
235
+ serverAccounting = false;
236
+ };
237
+
238
+ Server.servers = function() {
239
+ return servers;
240
+ };
241
+
242
+ Object.defineProperty(Server.prototype, 'name', {
243
+ enumerable: true,
244
+ get: function() {
245
+ return this.s.options.host + ':' + this.s.options.port;
246
+ }
247
+ });
248
+
249
+ function disconnectHandler(self, type, ns, cmd, options, callback) {
250
+ // Topology is not connected, save the call in the provided store to be
251
+ // Executed at some point when the handler deems it's reconnected
252
+ if (
253
+ !self.s.pool.isConnected() &&
254
+ self.s.options.reconnect &&
255
+ self.s.disconnectHandler != null &&
256
+ !options.monitoring
257
+ ) {
258
+ self.s.disconnectHandler.add(type, ns, cmd, options, callback);
259
+ return true;
260
+ }
261
+
262
+ // If we have no connection error
263
+ if (!self.s.pool.isConnected()) {
264
+ callback(new MongoError(f('no connection available to server %s', self.name)));
265
+ return true;
266
+ }
267
+ }
268
+
269
+ function monitoringProcess(self) {
270
+ return function() {
271
+ // Pool was destroyed do not continue process
272
+ if (self.s.pool.isDestroyed()) return;
273
+ // Emit monitoring Process event
274
+ self.emit('monitoring', self);
275
+ // Perform ismaster call
276
+ // Get start time
277
+ var start = new Date().getTime();
278
+
279
+ // Execute the ismaster query
280
+ self.command(
281
+ 'admin.$cmd',
282
+ { ismaster: true },
283
+ {
284
+ socketTimeout:
285
+ typeof self.s.options.connectionTimeout !== 'number'
286
+ ? 2000
287
+ : self.s.options.connectionTimeout,
288
+ monitoring: true
289
+ },
290
+ (err, result) => {
291
+ // Set initial lastIsMasterMS
292
+ self.lastIsMasterMS = new Date().getTime() - start;
293
+ if (self.s.pool.isDestroyed()) return;
294
+ // Update the ismaster view if we have a result
295
+ if (result) {
296
+ self.ismaster = result.result;
297
+ }
298
+ // Re-schedule the monitoring process
299
+ self.monitoringProcessId = setTimeout(monitoringProcess(self), self.s.monitoringInterval);
300
+ }
301
+ );
302
+ };
303
+ }
304
+
305
+ var eventHandler = function(self, event) {
306
+ return function(err, conn) {
307
+ // Log information of received information if in info mode
308
+ if (self.s.logger.isInfo()) {
309
+ var object = err instanceof MongoError ? JSON.stringify(err) : {};
310
+ self.s.logger.info(
311
+ f('server %s fired event %s out with message %s', self.name, event, object)
312
+ );
313
+ }
314
+
315
+ // Handle connect event
316
+ if (event === 'connect') {
317
+ self.initialConnect = false;
318
+ self.ismaster = conn.ismaster;
319
+ self.lastIsMasterMS = conn.lastIsMasterMS;
320
+ if (conn.agreedCompressor) {
321
+ self.s.pool.options.agreedCompressor = conn.agreedCompressor;
322
+ }
323
+
324
+ if (conn.zlibCompressionLevel) {
325
+ self.s.pool.options.zlibCompressionLevel = conn.zlibCompressionLevel;
326
+ }
327
+
328
+ if (conn.ismaster.$clusterTime) {
329
+ const $clusterTime = conn.ismaster.$clusterTime;
330
+ self.clusterTime = $clusterTime;
331
+ }
332
+
333
+ // It's a proxy change the type so
334
+ // the wireprotocol will send $readPreference
335
+ if (self.ismaster.msg === 'isdbgrid') {
336
+ self._type = 'mongos';
337
+ }
338
+
339
+ // Have we defined self monitoring
340
+ if (self.s.monitoring) {
341
+ self.monitoringProcessId = setTimeout(monitoringProcess(self), self.s.monitoringInterval);
342
+ }
343
+
344
+ // Emit server description changed if something listening
345
+ sdam.emitServerDescriptionChanged(self, {
346
+ address: self.name,
347
+ arbiters: [],
348
+ hosts: [],
349
+ passives: [],
350
+ type: sdam.getTopologyType(self)
351
+ });
352
+
353
+ if (!self.s.inTopology) {
354
+ // Emit topology description changed if something listening
355
+ sdam.emitTopologyDescriptionChanged(self, {
356
+ topologyType: 'Single',
357
+ servers: [
358
+ {
359
+ address: self.name,
360
+ arbiters: [],
361
+ hosts: [],
362
+ passives: [],
363
+ type: sdam.getTopologyType(self)
364
+ }
365
+ ]
366
+ });
367
+ }
368
+
369
+ // Log the ismaster if available
370
+ if (self.s.logger.isInfo()) {
371
+ self.s.logger.info(
372
+ f('server %s connected with ismaster [%s]', self.name, JSON.stringify(self.ismaster))
373
+ );
374
+ }
375
+
376
+ // Emit connect
377
+ self.emit('connect', self);
378
+ } else if (
379
+ event === 'error' ||
380
+ event === 'parseError' ||
381
+ event === 'close' ||
382
+ event === 'timeout' ||
383
+ event === 'reconnect' ||
384
+ event === 'attemptReconnect' ||
385
+ 'reconnectFailed'
386
+ ) {
387
+ // Remove server instance from accounting
388
+ if (
389
+ serverAccounting &&
390
+ ['close', 'timeout', 'error', 'parseError', 'reconnectFailed'].indexOf(event) !== -1
391
+ ) {
392
+ // Emit toplogy opening event if not in topology
393
+ if (!self.s.inTopology) {
394
+ self.emit('topologyOpening', { topologyId: self.id });
395
+ }
396
+
397
+ delete servers[self.id];
398
+ }
399
+
400
+ if (event === 'close') {
401
+ // Closing emits a server description changed event going to unknown.
402
+ sdam.emitServerDescriptionChanged(self, {
403
+ address: self.name,
404
+ arbiters: [],
405
+ hosts: [],
406
+ passives: [],
407
+ type: 'Unknown'
408
+ });
409
+ }
410
+
411
+ // Reconnect failed return error
412
+ if (event === 'reconnectFailed') {
413
+ self.emit('reconnectFailed', err);
414
+ // Emit error if any listeners
415
+ if (self.listeners('error').length > 0) {
416
+ self.emit('error', err);
417
+ }
418
+ // Terminate
419
+ return;
420
+ }
421
+
422
+ // On first connect fail
423
+ if (
424
+ ['disconnected', 'connecting'].indexOf(self.s.pool.state) !== -1 &&
425
+ self.initialConnect &&
426
+ ['close', 'timeout', 'error', 'parseError'].indexOf(event) !== -1
427
+ ) {
428
+ self.initialConnect = false;
429
+ return self.emit(
430
+ 'error',
431
+ new MongoNetworkError(
432
+ f('failed to connect to server [%s] on first connect [%s]', self.name, err)
433
+ )
434
+ );
435
+ }
436
+
437
+ // Reconnect event, emit the server
438
+ if (event === 'reconnect') {
439
+ // Reconnecting emits a server description changed event going from unknown to the
440
+ // current server type.
441
+ sdam.emitServerDescriptionChanged(self, {
442
+ address: self.name,
443
+ arbiters: [],
444
+ hosts: [],
445
+ passives: [],
446
+ type: sdam.getTopologyType(self)
447
+ });
448
+ return self.emit(event, self);
449
+ }
450
+
451
+ // Emit the event
452
+ self.emit(event, err);
453
+ }
454
+ };
455
+ };
456
+
457
+ /**
458
+ * Initiate server connect
459
+ */
460
+ Server.prototype.connect = function(options) {
461
+ var self = this;
462
+ options = options || {};
463
+
464
+ // Set the connections
465
+ if (serverAccounting) servers[this.id] = this;
466
+
467
+ // Do not allow connect to be called on anything that's not disconnected
468
+ if (self.s.pool && !self.s.pool.isDisconnected() && !self.s.pool.isDestroyed()) {
469
+ throw new MongoError(f('server instance in invalid state %s', self.s.pool.state));
470
+ }
471
+
472
+ // Create a pool
473
+ self.s.pool = new Pool(this, Object.assign(self.s.options, options, { bson: this.s.bson }));
474
+
475
+ // Set up listeners
476
+ self.s.pool.on('close', eventHandler(self, 'close'));
477
+ self.s.pool.on('error', eventHandler(self, 'error'));
478
+ self.s.pool.on('timeout', eventHandler(self, 'timeout'));
479
+ self.s.pool.on('parseError', eventHandler(self, 'parseError'));
480
+ self.s.pool.on('connect', eventHandler(self, 'connect'));
481
+ self.s.pool.on('reconnect', eventHandler(self, 'reconnect'));
482
+ self.s.pool.on('reconnectFailed', eventHandler(self, 'reconnectFailed'));
483
+
484
+ // Set up listeners for command monitoring
485
+ relayEvents(self.s.pool, self, ['commandStarted', 'commandSucceeded', 'commandFailed']);
486
+
487
+ // Emit toplogy opening event if not in topology
488
+ if (!self.s.inTopology) {
489
+ this.emit('topologyOpening', { topologyId: self.id });
490
+ }
491
+
492
+ // Emit opening server event
493
+ self.emit('serverOpening', {
494
+ topologyId: self.s.topologyId !== -1 ? self.s.topologyId : self.id,
495
+ address: self.name
496
+ });
497
+
498
+ self.s.pool.connect();
499
+ };
500
+
501
+ /**
502
+ * Authenticate the topology.
503
+ * @method
504
+ * @param {MongoCredentials} credentials The credentials for authentication we are using
505
+ * @param {authResultCallback} callback A callback function
506
+ */
507
+ Server.prototype.auth = function(credentials, callback) {
508
+ if (typeof callback === 'function') callback(null, null);
509
+ };
510
+
511
+ /**
512
+ * Get the server description
513
+ * @method
514
+ * @return {object}
515
+ */
516
+ Server.prototype.getDescription = function() {
517
+ var ismaster = this.ismaster || {};
518
+ var description = {
519
+ type: sdam.getTopologyType(this),
520
+ address: this.name
521
+ };
522
+
523
+ // Add fields if available
524
+ if (ismaster.hosts) description.hosts = ismaster.hosts;
525
+ if (ismaster.arbiters) description.arbiters = ismaster.arbiters;
526
+ if (ismaster.passives) description.passives = ismaster.passives;
527
+ if (ismaster.setName) description.setName = ismaster.setName;
528
+ return description;
529
+ };
530
+
531
+ /**
532
+ * Returns the last known ismaster document for this server
533
+ * @method
534
+ * @return {object}
535
+ */
536
+ Server.prototype.lastIsMaster = function() {
537
+ return this.ismaster;
538
+ };
539
+
540
+ /**
541
+ * Unref all connections belong to this server
542
+ * @method
543
+ */
544
+ Server.prototype.unref = function() {
545
+ this.s.pool.unref();
546
+ };
547
+
548
+ /**
549
+ * Figure out if the server is connected
550
+ * @method
551
+ * @return {boolean}
552
+ */
553
+ Server.prototype.isConnected = function() {
554
+ if (!this.s.pool) return false;
555
+ return this.s.pool.isConnected();
556
+ };
557
+
558
+ /**
559
+ * Figure out if the server instance was destroyed by calling destroy
560
+ * @method
561
+ * @return {boolean}
562
+ */
563
+ Server.prototype.isDestroyed = function() {
564
+ if (!this.s.pool) return false;
565
+ return this.s.pool.isDestroyed();
566
+ };
567
+
568
+ function basicWriteValidations(self) {
569
+ if (!self.s.pool) return new MongoError('server instance is not connected');
570
+ if (self.s.pool.isDestroyed()) return new MongoError('server instance pool was destroyed');
571
+ }
572
+
573
+ function basicReadValidations(self, options) {
574
+ basicWriteValidations(self, options);
575
+
576
+ if (options.readPreference && !(options.readPreference instanceof ReadPreference)) {
577
+ throw new Error('readPreference must be an instance of ReadPreference');
578
+ }
579
+ }
580
+
581
+ /**
582
+ * Execute a command
583
+ * @method
584
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
585
+ * @param {object} cmd The command hash
586
+ * @param {ReadPreference} [options.readPreference] Specify read preference if command supports it
587
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
588
+ * @param {Boolean} [options.checkKeys=false] Specify if the bson parser should validate keys.
589
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
590
+ * @param {Boolean} [options.fullResult=false] Return the full envelope instead of just the result document.
591
+ * @param {ClientSession} [options.session=null] Session to use for the operation
592
+ * @param {opResultCallback} callback A callback function
593
+ */
594
+ Server.prototype.command = function(ns, cmd, options, callback) {
595
+ var self = this;
596
+ if (typeof options === 'function') {
597
+ (callback = options), (options = {}), (options = options || {});
598
+ }
599
+
600
+ var result = basicReadValidations(self, options);
601
+ if (result) return callback(result);
602
+
603
+ // Clone the options
604
+ options = Object.assign({}, options, { wireProtocolCommand: false });
605
+
606
+ // Debug log
607
+ if (self.s.logger.isDebug())
608
+ self.s.logger.debug(
609
+ f(
610
+ 'executing command [%s] against %s',
611
+ JSON.stringify({
612
+ ns: ns,
613
+ cmd: cmd,
614
+ options: debugOptions(debugFields, options)
615
+ }),
616
+ self.name
617
+ )
618
+ );
619
+
620
+ // If we are not connected or have a disconnectHandler specified
621
+ if (disconnectHandler(self, 'command', ns, cmd, options, callback)) return;
622
+
623
+ // error if collation not supported
624
+ if (collationNotSupported(this, cmd)) {
625
+ return callback(new MongoError(`server ${this.name} does not support collation`));
626
+ }
627
+
628
+ wireProtocol.command(self, ns, cmd, options, callback);
629
+ };
630
+
631
+ /**
632
+ * Execute a query against the server
633
+ *
634
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
635
+ * @param {object} cmd The command document for the query
636
+ * @param {object} options Optional settings
637
+ * @param {function} callback
638
+ */
639
+ Server.prototype.query = function(ns, cmd, cursorState, options, callback) {
640
+ wireProtocol.query(this, ns, cmd, cursorState, options, callback);
641
+ };
642
+
643
+ /**
644
+ * Execute a `getMore` against the server
645
+ *
646
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
647
+ * @param {object} cursorState State data associated with the cursor calling this method
648
+ * @param {object} options Optional settings
649
+ * @param {function} callback
650
+ */
651
+ Server.prototype.getMore = function(ns, cursorState, batchSize, options, callback) {
652
+ wireProtocol.getMore(this, ns, cursorState, batchSize, options, callback);
653
+ };
654
+
655
+ /**
656
+ * Execute a `killCursors` command against the server
657
+ *
658
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
659
+ * @param {object} cursorState State data associated with the cursor calling this method
660
+ * @param {function} callback
661
+ */
662
+ Server.prototype.killCursors = function(ns, cursorState, callback) {
663
+ wireProtocol.killCursors(this, ns, cursorState, callback);
664
+ };
665
+
666
+ /**
667
+ * Insert one or more documents
668
+ * @method
669
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
670
+ * @param {array} ops An array of documents to insert
671
+ * @param {boolean} [options.ordered=true] Execute in order or out of order
672
+ * @param {object} [options.writeConcern={}] Write concern for the operation
673
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
674
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
675
+ * @param {ClientSession} [options.session=null] Session to use for the operation
676
+ * @param {opResultCallback} callback A callback function
677
+ */
678
+ Server.prototype.insert = function(ns, ops, options, callback) {
679
+ var self = this;
680
+ if (typeof options === 'function') {
681
+ (callback = options), (options = {}), (options = options || {});
682
+ }
683
+
684
+ var result = basicWriteValidations(self, options);
685
+ if (result) return callback(result);
686
+
687
+ // If we are not connected or have a disconnectHandler specified
688
+ if (disconnectHandler(self, 'insert', ns, ops, options, callback)) return;
689
+
690
+ // Setup the docs as an array
691
+ ops = Array.isArray(ops) ? ops : [ops];
692
+
693
+ // Execute write
694
+ return wireProtocol.insert(self, ns, ops, options, callback);
695
+ };
696
+
697
+ /**
698
+ * Perform one or more update operations
699
+ * @method
700
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
701
+ * @param {array} ops An array of updates
702
+ * @param {boolean} [options.ordered=true] Execute in order or out of order
703
+ * @param {object} [options.writeConcern={}] Write concern for the operation
704
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
705
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
706
+ * @param {ClientSession} [options.session=null] Session to use for the operation
707
+ * @param {opResultCallback} callback A callback function
708
+ */
709
+ Server.prototype.update = function(ns, ops, options, callback) {
710
+ var self = this;
711
+ if (typeof options === 'function') {
712
+ (callback = options), (options = {}), (options = options || {});
713
+ }
714
+
715
+ var result = basicWriteValidations(self, options);
716
+ if (result) return callback(result);
717
+
718
+ // If we are not connected or have a disconnectHandler specified
719
+ if (disconnectHandler(self, 'update', ns, ops, options, callback)) return;
720
+
721
+ // error if collation not supported
722
+ if (collationNotSupported(this, options)) {
723
+ return callback(new MongoError(`server ${this.name} does not support collation`));
724
+ }
725
+
726
+ // Setup the docs as an array
727
+ ops = Array.isArray(ops) ? ops : [ops];
728
+ // Execute write
729
+ return wireProtocol.update(self, ns, ops, options, callback);
730
+ };
731
+
732
+ /**
733
+ * Perform one or more remove operations
734
+ * @method
735
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
736
+ * @param {array} ops An array of removes
737
+ * @param {boolean} [options.ordered=true] Execute in order or out of order
738
+ * @param {object} [options.writeConcern={}] Write concern for the operation
739
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
740
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
741
+ * @param {ClientSession} [options.session=null] Session to use for the operation
742
+ * @param {opResultCallback} callback A callback function
743
+ */
744
+ Server.prototype.remove = function(ns, ops, options, callback) {
745
+ var self = this;
746
+ if (typeof options === 'function') {
747
+ (callback = options), (options = {}), (options = options || {});
748
+ }
749
+
750
+ var result = basicWriteValidations(self, options);
751
+ if (result) return callback(result);
752
+
753
+ // If we are not connected or have a disconnectHandler specified
754
+ if (disconnectHandler(self, 'remove', ns, ops, options, callback)) return;
755
+
756
+ // error if collation not supported
757
+ if (collationNotSupported(this, options)) {
758
+ return callback(new MongoError(`server ${this.name} does not support collation`));
759
+ }
760
+
761
+ // Setup the docs as an array
762
+ ops = Array.isArray(ops) ? ops : [ops];
763
+ // Execute write
764
+ return wireProtocol.remove(self, ns, ops, options, callback);
765
+ };
766
+
767
+ /**
768
+ * Get a new cursor
769
+ * @method
770
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
771
+ * @param {object|Long} cmd Can be either a command returning a cursor or a cursorId
772
+ * @param {object} [options] Options for the cursor
773
+ * @param {object} [options.batchSize=0] Batchsize for the operation
774
+ * @param {array} [options.documents=[]] Initial documents list for cursor
775
+ * @param {ReadPreference} [options.readPreference] Specify read preference if command supports it
776
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
777
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
778
+ * @param {ClientSession} [options.session=null] Session to use for the operation
779
+ * @param {object} [options.topology] The internal topology of the created cursor
780
+ * @returns {Cursor}
781
+ */
782
+ Server.prototype.cursor = function(ns, cmd, options) {
783
+ options = options || {};
784
+ const topology = options.topology || this;
785
+
786
+ // Set up final cursor type
787
+ var FinalCursor = options.cursorFactory || this.s.Cursor;
788
+
789
+ // Return the cursor
790
+ return new FinalCursor(topology, ns, cmd, options);
791
+ };
792
+
793
+ /**
794
+ * Compare two server instances
795
+ * @method
796
+ * @param {Server} server Server to compare equality against
797
+ * @return {boolean}
798
+ */
799
+ Server.prototype.equals = function(server) {
800
+ if (typeof server === 'string') return this.name.toLowerCase() === server.toLowerCase();
801
+ if (server.name) return this.name.toLowerCase() === server.name.toLowerCase();
802
+ return false;
803
+ };
804
+
805
+ /**
806
+ * All raw connections
807
+ * @method
808
+ * @return {Connection[]}
809
+ */
810
+ Server.prototype.connections = function() {
811
+ return this.s.pool.allConnections();
812
+ };
813
+
814
+ /**
815
+ * Selects a server
816
+ * @method
817
+ * @param {function} selector Unused
818
+ * @param {ReadPreference} [options.readPreference] Unused
819
+ * @param {ClientSession} [options.session] Unused
820
+ * @return {Server}
821
+ */
822
+ Server.prototype.selectServer = function(selector, options, callback) {
823
+ if (typeof selector === 'function' && typeof callback === 'undefined')
824
+ (callback = selector), (selector = undefined), (options = {});
825
+ if (typeof options === 'function')
826
+ (callback = options), (options = selector), (selector = undefined);
827
+
828
+ callback(null, this);
829
+ };
830
+
831
+ var listeners = ['close', 'error', 'timeout', 'parseError', 'connect'];
832
+
833
+ /**
834
+ * Destroy the server connection
835
+ * @method
836
+ * @param {boolean} [options.emitClose=false] Emit close event on destroy
837
+ * @param {boolean} [options.emitDestroy=false] Emit destroy event on destroy
838
+ * @param {boolean} [options.force=false] Force destroy the pool
839
+ */
840
+ Server.prototype.destroy = function(options, callback) {
841
+ if (this._destroyed) {
842
+ if (typeof callback === 'function') callback(null, null);
843
+ return;
844
+ }
845
+
846
+ options = options || {};
847
+ var self = this;
848
+
849
+ // Set the connections
850
+ if (serverAccounting) delete servers[this.id];
851
+
852
+ // Destroy the monitoring process if any
853
+ if (this.monitoringProcessId) {
854
+ clearTimeout(this.monitoringProcessId);
855
+ }
856
+
857
+ // No pool, return
858
+ if (!self.s.pool) {
859
+ this._destroyed = true;
860
+ if (typeof callback === 'function') callback(null, null);
861
+ return;
862
+ }
863
+
864
+ // Emit close event
865
+ if (options.emitClose) {
866
+ self.emit('close', self);
867
+ }
868
+
869
+ // Emit destroy event
870
+ if (options.emitDestroy) {
871
+ self.emit('destroy', self);
872
+ }
873
+
874
+ // Remove all listeners
875
+ listeners.forEach(function(event) {
876
+ self.s.pool.removeAllListeners(event);
877
+ });
878
+
879
+ // Emit opening server event
880
+ 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
+
886
+ // Emit toplogy opening event if not in topology
887
+ if (self.listeners('topologyClosed').length > 0 && !self.s.inTopology) {
888
+ self.emit('topologyClosed', { topologyId: self.id });
889
+ }
890
+
891
+ if (self.s.logger.isDebug()) {
892
+ self.s.logger.debug(f('destroy called on server %s', self.name));
893
+ }
894
+
895
+ // Destroy the pool
896
+ this.s.pool.destroy(options.force, callback);
897
+ this._destroyed = true;
898
+ };
899
+
900
+ /**
901
+ * A server connect event, used to verify that the connection is up and running
902
+ *
903
+ * @event Server#connect
904
+ * @type {Server}
905
+ */
906
+
907
+ /**
908
+ * A server reconnect event, used to verify that the server topology has reconnected
909
+ *
910
+ * @event Server#reconnect
911
+ * @type {Server}
912
+ */
913
+
914
+ /**
915
+ * A server opening SDAM monitoring event
916
+ *
917
+ * @event Server#serverOpening
918
+ * @type {object}
919
+ */
920
+
921
+ /**
922
+ * A server closed SDAM monitoring event
923
+ *
924
+ * @event Server#serverClosed
925
+ * @type {object}
926
+ */
927
+
928
+ /**
929
+ * A server description SDAM change monitoring event
930
+ *
931
+ * @event Server#serverDescriptionChanged
932
+ * @type {object}
933
+ */
934
+
935
+ /**
936
+ * A topology open SDAM event
937
+ *
938
+ * @event Server#topologyOpening
939
+ * @type {object}
940
+ */
941
+
942
+ /**
943
+ * A topology closed SDAM event
944
+ *
945
+ * @event Server#topologyClosed
946
+ * @type {object}
947
+ */
948
+
949
+ /**
950
+ * A topology structure SDAM change event
951
+ *
952
+ * @event Server#topologyDescriptionChanged
953
+ * @type {object}
954
+ */
955
+
956
+ /**
957
+ * Server reconnect failed
958
+ *
959
+ * @event Server#reconnectFailed
960
+ * @type {Error}
961
+ */
962
+
963
+ /**
964
+ * Server connection pool closed
965
+ *
966
+ * @event Server#close
967
+ * @type {object}
968
+ */
969
+
970
+ /**
971
+ * Server connection pool caused an error
972
+ *
973
+ * @event Server#error
974
+ * @type {Error}
975
+ */
976
+
977
+ /**
978
+ * Server destroyed was called
979
+ *
980
+ * @event Server#destroy
981
+ * @type {Server}
982
+ */
983
+
984
+ module.exports = Server;