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,752 @@
1
+ 'use strict';
2
+
3
+ const Logger = require('./connection/logger');
4
+ const retrieveBSON = require('./connection/utils').retrieveBSON;
5
+ const MongoError = require('./error').MongoError;
6
+ const MongoNetworkError = require('./error').MongoNetworkError;
7
+ const mongoErrorContextSymbol = require('./error').mongoErrorContextSymbol;
8
+ const f = require('util').format;
9
+ const collationNotSupported = require('./utils').collationNotSupported;
10
+ const ReadPreference = require('./topologies/read_preference');
11
+ const isUnifiedTopology = require('./utils').isUnifiedTopology;
12
+
13
+ const BSON = retrieveBSON();
14
+ const Long = BSON.Long;
15
+
16
+ /**
17
+ * This is a cursor results callback
18
+ *
19
+ * @callback resultCallback
20
+ * @param {error} error An error object. Set to null if no error present
21
+ * @param {object} document
22
+ */
23
+
24
+ /**
25
+ * @fileOverview The **Cursor** class is an internal class that embodies a cursor on MongoDB
26
+ * allowing for iteration over the results returned from the underlying query.
27
+ *
28
+ * **CURSORS Cannot directly be instantiated**
29
+ */
30
+
31
+ /**
32
+ * Creates a new Cursor, not to be used directly
33
+ * @class
34
+ * @param {object} topology The server topology instance.
35
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
36
+ * @param {{object}|Long} cmd The selector (can be a command or a cursorId)
37
+ * @param {object} [options=null] Optional settings.
38
+ * @param {object} [options.batchSize=1000] Batchsize for the operation
39
+ * @param {array} [options.documents=[]] Initial documents list for cursor
40
+ * @param {object} [options.transforms=null] Transform methods for the cursor results
41
+ * @param {function} [options.transforms.query] Transform the value returned from the initial query
42
+ * @param {function} [options.transforms.doc] Transform each document returned from Cursor.prototype.next
43
+ * @return {Cursor} A cursor instance
44
+ * @property {number} cursorBatchSize The current cursorBatchSize for the cursor
45
+ * @property {number} cursorLimit The current cursorLimit for the cursor
46
+ * @property {number} cursorSkip The current cursorSkip for the cursor
47
+ */
48
+ var Cursor = function(topology, ns, cmd, options) {
49
+ options = options || {};
50
+
51
+ // Cursor pool
52
+ this.pool = null;
53
+ // Cursor server
54
+ this.server = null;
55
+
56
+ // Do we have a not connected handler
57
+ this.disconnectHandler = options.disconnectHandler;
58
+
59
+ // Set local values
60
+ this.bson = topology.s.bson;
61
+ this.ns = ns;
62
+ this.cmd = cmd;
63
+ this.options = options;
64
+ this.topology = topology;
65
+
66
+ // All internal state
67
+ this.cursorState = {
68
+ cursorId: null,
69
+ cmd: cmd,
70
+ documents: options.documents || [],
71
+ cursorIndex: 0,
72
+ dead: false,
73
+ killed: false,
74
+ init: false,
75
+ notified: false,
76
+ limit: options.limit || cmd.limit || 0,
77
+ skip: options.skip || cmd.skip || 0,
78
+ batchSize: options.batchSize || cmd.batchSize || 1000,
79
+ currentLimit: 0,
80
+ // Result field name if not a cursor (contains the array of results)
81
+ transforms: options.transforms,
82
+ raw: options.raw || (cmd && cmd.raw)
83
+ };
84
+
85
+ if (typeof options.session === 'object') {
86
+ this.cursorState.session = options.session;
87
+ }
88
+
89
+ // Add promoteLong to cursor state
90
+ const topologyOptions = topology.s.options;
91
+ if (typeof topologyOptions.promoteLongs === 'boolean') {
92
+ this.cursorState.promoteLongs = topologyOptions.promoteLongs;
93
+ } else if (typeof options.promoteLongs === 'boolean') {
94
+ this.cursorState.promoteLongs = options.promoteLongs;
95
+ }
96
+
97
+ // Add promoteValues to cursor state
98
+ if (typeof topologyOptions.promoteValues === 'boolean') {
99
+ this.cursorState.promoteValues = topologyOptions.promoteValues;
100
+ } else if (typeof options.promoteValues === 'boolean') {
101
+ this.cursorState.promoteValues = options.promoteValues;
102
+ }
103
+
104
+ // Add promoteBuffers to cursor state
105
+ if (typeof topologyOptions.promoteBuffers === 'boolean') {
106
+ this.cursorState.promoteBuffers = topologyOptions.promoteBuffers;
107
+ } else if (typeof options.promoteBuffers === 'boolean') {
108
+ this.cursorState.promoteBuffers = options.promoteBuffers;
109
+ }
110
+
111
+ if (topologyOptions.reconnect) {
112
+ this.cursorState.reconnect = topologyOptions.reconnect;
113
+ }
114
+
115
+ // Logger
116
+ this.logger = Logger('Cursor', topologyOptions);
117
+
118
+ //
119
+ // Did we pass in a cursor id
120
+ if (typeof cmd === 'number') {
121
+ this.cursorState.cursorId = Long.fromNumber(cmd);
122
+ this.cursorState.lastCursorId = this.cursorState.cursorId;
123
+ } else if (cmd instanceof Long) {
124
+ this.cursorState.cursorId = cmd;
125
+ this.cursorState.lastCursorId = cmd;
126
+ }
127
+ };
128
+
129
+ Cursor.prototype.setCursorBatchSize = function(value) {
130
+ this.cursorState.batchSize = value;
131
+ };
132
+
133
+ Cursor.prototype.cursorBatchSize = function() {
134
+ return this.cursorState.batchSize;
135
+ };
136
+
137
+ Cursor.prototype.setCursorLimit = function(value) {
138
+ this.cursorState.limit = value;
139
+ };
140
+
141
+ Cursor.prototype.cursorLimit = function() {
142
+ return this.cursorState.limit;
143
+ };
144
+
145
+ Cursor.prototype.setCursorSkip = function(value) {
146
+ this.cursorState.skip = value;
147
+ };
148
+
149
+ Cursor.prototype.cursorSkip = function() {
150
+ return this.cursorState.skip;
151
+ };
152
+
153
+ Cursor.prototype._endSession = function(options, callback) {
154
+ if (typeof options === 'function') {
155
+ callback = options;
156
+ options = {};
157
+ }
158
+ options = options || {};
159
+
160
+ const session = this.cursorState.session;
161
+
162
+ if (session && (options.force || session.owner === this)) {
163
+ this.cursorState.session = undefined;
164
+ session.endSession(callback);
165
+ return true;
166
+ }
167
+
168
+ if (callback) {
169
+ callback();
170
+ }
171
+ return false;
172
+ };
173
+
174
+ //
175
+ // Handle callback (including any exceptions thrown)
176
+ var handleCallback = function(callback, err, result) {
177
+ try {
178
+ callback(err, result);
179
+ } catch (err) {
180
+ process.nextTick(function() {
181
+ throw err;
182
+ });
183
+ }
184
+ };
185
+
186
+ // Internal methods
187
+ Cursor.prototype._getMore = function(callback) {
188
+ if (this.logger.isDebug())
189
+ this.logger.debug(f('schedule getMore call for query [%s]', JSON.stringify(this.query)));
190
+
191
+ // Set the current batchSize
192
+ var batchSize = this.cursorState.batchSize;
193
+ if (
194
+ this.cursorState.limit > 0 &&
195
+ this.cursorState.currentLimit + batchSize > this.cursorState.limit
196
+ ) {
197
+ batchSize = this.cursorState.limit - this.cursorState.currentLimit;
198
+ }
199
+
200
+ this.server.getMore(this.ns, this.cursorState, batchSize, this.options, callback);
201
+ };
202
+
203
+ /**
204
+ * Clone the cursor
205
+ * @method
206
+ * @return {Cursor}
207
+ */
208
+ Cursor.prototype.clone = function() {
209
+ return this.topology.cursor(this.ns, this.cmd, this.options);
210
+ };
211
+
212
+ /**
213
+ * Checks if the cursor is dead
214
+ * @method
215
+ * @return {boolean} A boolean signifying if the cursor is dead or not
216
+ */
217
+ Cursor.prototype.isDead = function() {
218
+ return this.cursorState.dead === true;
219
+ };
220
+
221
+ /**
222
+ * Checks if the cursor was killed by the application
223
+ * @method
224
+ * @return {boolean} A boolean signifying if the cursor was killed by the application
225
+ */
226
+ Cursor.prototype.isKilled = function() {
227
+ return this.cursorState.killed === true;
228
+ };
229
+
230
+ /**
231
+ * Checks if the cursor notified it's caller about it's death
232
+ * @method
233
+ * @return {boolean} A boolean signifying if the cursor notified the callback
234
+ */
235
+ Cursor.prototype.isNotified = function() {
236
+ return this.cursorState.notified === true;
237
+ };
238
+
239
+ /**
240
+ * Returns current buffered documents length
241
+ * @method
242
+ * @return {number} The number of items in the buffered documents
243
+ */
244
+ Cursor.prototype.bufferedCount = function() {
245
+ return this.cursorState.documents.length - this.cursorState.cursorIndex;
246
+ };
247
+
248
+ /**
249
+ * Returns current buffered documents
250
+ * @method
251
+ * @return {Array} An array of buffered documents
252
+ */
253
+ Cursor.prototype.readBufferedDocuments = function(number) {
254
+ var unreadDocumentsLength = this.cursorState.documents.length - this.cursorState.cursorIndex;
255
+ var length = number < unreadDocumentsLength ? number : unreadDocumentsLength;
256
+ var elements = this.cursorState.documents.slice(
257
+ this.cursorState.cursorIndex,
258
+ this.cursorState.cursorIndex + length
259
+ );
260
+
261
+ // Transform the doc with passed in transformation method if provided
262
+ if (this.cursorState.transforms && typeof this.cursorState.transforms.doc === 'function') {
263
+ // Transform all the elements
264
+ for (var i = 0; i < elements.length; i++) {
265
+ elements[i] = this.cursorState.transforms.doc(elements[i]);
266
+ }
267
+ }
268
+
269
+ // Ensure we do not return any more documents than the limit imposed
270
+ // Just return the number of elements up to the limit
271
+ if (
272
+ this.cursorState.limit > 0 &&
273
+ this.cursorState.currentLimit + elements.length > this.cursorState.limit
274
+ ) {
275
+ elements = elements.slice(0, this.cursorState.limit - this.cursorState.currentLimit);
276
+ this.kill();
277
+ }
278
+
279
+ // Adjust current limit
280
+ this.cursorState.currentLimit = this.cursorState.currentLimit + elements.length;
281
+ this.cursorState.cursorIndex = this.cursorState.cursorIndex + elements.length;
282
+
283
+ // Return elements
284
+ return elements;
285
+ };
286
+
287
+ /**
288
+ * Kill the cursor
289
+ * @method
290
+ * @param {resultCallback} callback A callback function
291
+ */
292
+ Cursor.prototype.kill = function(callback) {
293
+ // Set cursor to dead
294
+ this.cursorState.dead = true;
295
+ this.cursorState.killed = true;
296
+ // Remove documents
297
+ this.cursorState.documents = [];
298
+
299
+ // If no cursor id just return
300
+ if (
301
+ this.cursorState.cursorId == null ||
302
+ this.cursorState.cursorId.isZero() ||
303
+ this.cursorState.init === false
304
+ ) {
305
+ if (callback) callback(null, null);
306
+ return;
307
+ }
308
+
309
+ this.server.killCursors(this.ns, this.cursorState, callback);
310
+ };
311
+
312
+ /**
313
+ * Resets the cursor
314
+ * @method
315
+ * @return {null}
316
+ */
317
+ Cursor.prototype.rewind = function() {
318
+ if (this.cursorState.init) {
319
+ if (!this.cursorState.dead) {
320
+ this.kill();
321
+ }
322
+
323
+ this.cursorState.currentLimit = 0;
324
+ this.cursorState.init = false;
325
+ this.cursorState.dead = false;
326
+ this.cursorState.killed = false;
327
+ this.cursorState.notified = false;
328
+ this.cursorState.documents = [];
329
+ this.cursorState.cursorId = null;
330
+ this.cursorState.cursorIndex = 0;
331
+ }
332
+ };
333
+
334
+ /**
335
+ * Validate if the pool is dead and return error
336
+ */
337
+ var isConnectionDead = function(self, callback) {
338
+ if (self.pool && self.pool.isDestroyed()) {
339
+ self.cursorState.killed = true;
340
+ const err = new MongoNetworkError(
341
+ f('connection to host %s:%s was destroyed', self.pool.host, self.pool.port)
342
+ );
343
+ _setCursorNotifiedImpl(self, () => callback(err));
344
+ return true;
345
+ }
346
+
347
+ return false;
348
+ };
349
+
350
+ /**
351
+ * Validate if the cursor is dead but was not explicitly killed by user
352
+ */
353
+ var isCursorDeadButNotkilled = function(self, callback) {
354
+ // Cursor is dead but not marked killed, return null
355
+ if (self.cursorState.dead && !self.cursorState.killed) {
356
+ self.cursorState.killed = true;
357
+ setCursorNotified(self, callback);
358
+ return true;
359
+ }
360
+
361
+ return false;
362
+ };
363
+
364
+ /**
365
+ * Validate if the cursor is dead and was killed by user
366
+ */
367
+ var isCursorDeadAndKilled = function(self, callback) {
368
+ if (self.cursorState.dead && self.cursorState.killed) {
369
+ handleCallback(callback, new MongoError('cursor is dead'));
370
+ return true;
371
+ }
372
+
373
+ return false;
374
+ };
375
+
376
+ /**
377
+ * Validate if the cursor was killed by the user
378
+ */
379
+ var isCursorKilled = function(self, callback) {
380
+ if (self.cursorState.killed) {
381
+ setCursorNotified(self, callback);
382
+ return true;
383
+ }
384
+
385
+ return false;
386
+ };
387
+
388
+ /**
389
+ * Mark cursor as being dead and notified
390
+ */
391
+ var setCursorDeadAndNotified = function(self, callback) {
392
+ self.cursorState.dead = true;
393
+ setCursorNotified(self, callback);
394
+ };
395
+
396
+ /**
397
+ * Mark cursor as being notified
398
+ */
399
+ var setCursorNotified = function(self, callback) {
400
+ _setCursorNotifiedImpl(self, () => handleCallback(callback, null, null));
401
+ };
402
+
403
+ var _setCursorNotifiedImpl = function(self, callback) {
404
+ self.cursorState.notified = true;
405
+ self.cursorState.documents = [];
406
+ self.cursorState.cursorIndex = 0;
407
+ if (self._endSession) {
408
+ return self._endSession(undefined, () => callback());
409
+ }
410
+
411
+ return callback();
412
+ };
413
+
414
+ var nextFunction = function(self, callback) {
415
+ // We have notified about it
416
+ if (self.cursorState.notified) {
417
+ return callback(new Error('cursor is exhausted'));
418
+ }
419
+
420
+ // Cursor is killed return null
421
+ if (isCursorKilled(self, callback)) return;
422
+
423
+ // Cursor is dead but not marked killed, return null
424
+ if (isCursorDeadButNotkilled(self, callback)) return;
425
+
426
+ // We have a dead and killed cursor, attempting to call next should error
427
+ if (isCursorDeadAndKilled(self, callback)) return;
428
+
429
+ // We have just started the cursor
430
+ if (!self.cursorState.init) {
431
+ // Topology is not connected, save the call in the provided store to be
432
+ // Executed at some point when the handler deems it's reconnected
433
+ if (!self.topology.isConnected(self.options)) {
434
+ // Only need this for single server, because repl sets and mongos
435
+ // will always continue trying to reconnect
436
+ if (self.topology._type === 'server' && !self.topology.s.options.reconnect) {
437
+ // Reconnect is disabled, so we'll never reconnect
438
+ return callback(new MongoError('no connection available'));
439
+ }
440
+
441
+ if (self.disconnectHandler != null) {
442
+ if (self.topology.isDestroyed()) {
443
+ // Topology was destroyed, so don't try to wait for it to reconnect
444
+ return callback(new MongoError('Topology was destroyed'));
445
+ }
446
+
447
+ self.disconnectHandler.addObjectAndMethod('cursor', self, 'next', [callback], callback);
448
+ return;
449
+ }
450
+ }
451
+
452
+ self._initializeCursor((err, result) => {
453
+ if (err || result === null) {
454
+ callback(err, result);
455
+ return;
456
+ }
457
+
458
+ nextFunction(self, callback);
459
+ });
460
+
461
+ return;
462
+ }
463
+
464
+ if (self.cursorState.limit > 0 && self.cursorState.currentLimit >= self.cursorState.limit) {
465
+ // Ensure we kill the cursor on the server
466
+ self.kill();
467
+ // Set cursor in dead and notified state
468
+ return setCursorDeadAndNotified(self, callback);
469
+ } else if (
470
+ self.cursorState.cursorIndex === self.cursorState.documents.length &&
471
+ !Long.ZERO.equals(self.cursorState.cursorId)
472
+ ) {
473
+ // Ensure an empty cursor state
474
+ self.cursorState.documents = [];
475
+ self.cursorState.cursorIndex = 0;
476
+
477
+ // Check if topology is destroyed
478
+ if (self.topology.isDestroyed())
479
+ return callback(
480
+ new MongoNetworkError('connection destroyed, not possible to instantiate cursor')
481
+ );
482
+
483
+ // Check if connection is dead and return if not possible to
484
+ // execute a getMore on this connection
485
+ if (isConnectionDead(self, callback)) return;
486
+
487
+ // Execute the next get more
488
+ self._getMore(function(err, doc, connection) {
489
+ if (err) {
490
+ if (err instanceof MongoError) {
491
+ err[mongoErrorContextSymbol].isGetMore = true;
492
+ }
493
+
494
+ return handleCallback(callback, err);
495
+ }
496
+
497
+ if (self.cursorState.cursorId && self.cursorState.cursorId.isZero() && self._endSession) {
498
+ self._endSession();
499
+ }
500
+
501
+ // Save the returned connection to ensure all getMore's fire over the same connection
502
+ self.connection = connection;
503
+
504
+ // Tailable cursor getMore result, notify owner about it
505
+ // No attempt is made here to retry, this is left to the user of the
506
+ // core module to handle to keep core simple
507
+ if (
508
+ self.cursorState.documents.length === 0 &&
509
+ self.cmd.tailable &&
510
+ Long.ZERO.equals(self.cursorState.cursorId)
511
+ ) {
512
+ // No more documents in the tailed cursor
513
+ return handleCallback(
514
+ callback,
515
+ new MongoError({
516
+ message: 'No more documents in tailed cursor',
517
+ tailable: self.cmd.tailable,
518
+ awaitData: self.cmd.awaitData
519
+ })
520
+ );
521
+ } else if (
522
+ self.cursorState.documents.length === 0 &&
523
+ self.cmd.tailable &&
524
+ !Long.ZERO.equals(self.cursorState.cursorId)
525
+ ) {
526
+ return nextFunction(self, callback);
527
+ }
528
+
529
+ if (self.cursorState.limit > 0 && self.cursorState.currentLimit >= self.cursorState.limit) {
530
+ return setCursorDeadAndNotified(self, callback);
531
+ }
532
+
533
+ nextFunction(self, callback);
534
+ });
535
+ } else if (
536
+ self.cursorState.documents.length === self.cursorState.cursorIndex &&
537
+ self.cmd.tailable &&
538
+ Long.ZERO.equals(self.cursorState.cursorId)
539
+ ) {
540
+ return handleCallback(
541
+ callback,
542
+ new MongoError({
543
+ message: 'No more documents in tailed cursor',
544
+ tailable: self.cmd.tailable,
545
+ awaitData: self.cmd.awaitData
546
+ })
547
+ );
548
+ } else if (
549
+ self.cursorState.documents.length === self.cursorState.cursorIndex &&
550
+ Long.ZERO.equals(self.cursorState.cursorId)
551
+ ) {
552
+ setCursorDeadAndNotified(self, callback);
553
+ } else {
554
+ if (self.cursorState.limit > 0 && self.cursorState.currentLimit >= self.cursorState.limit) {
555
+ // Ensure we kill the cursor on the server
556
+ self.kill();
557
+ // Set cursor in dead and notified state
558
+ return setCursorDeadAndNotified(self, callback);
559
+ }
560
+
561
+ // Increment the current cursor limit
562
+ self.cursorState.currentLimit += 1;
563
+
564
+ // Get the document
565
+ var doc = self.cursorState.documents[self.cursorState.cursorIndex++];
566
+
567
+ // Doc overflow
568
+ if (!doc || doc.$err) {
569
+ // Ensure we kill the cursor on the server
570
+ self.kill();
571
+ // Set cursor in dead and notified state
572
+ return setCursorDeadAndNotified(self, function() {
573
+ handleCallback(callback, new MongoError(doc ? doc.$err : undefined));
574
+ });
575
+ }
576
+
577
+ // Transform the doc with passed in transformation method if provided
578
+ if (self.cursorState.transforms && typeof self.cursorState.transforms.doc === 'function') {
579
+ doc = self.cursorState.transforms.doc(doc);
580
+ }
581
+
582
+ // Return the document
583
+ handleCallback(callback, null, doc);
584
+ }
585
+ };
586
+
587
+ Cursor.prototype._initializeCursor = function(callback) {
588
+ const cursor = this;
589
+
590
+ // NOTE: this goes away once cursors use `executeOperation`
591
+ if (isUnifiedTopology(cursor.topology) && cursor.topology.shouldCheckForSessionSupport()) {
592
+ cursor.topology.selectServer(ReadPreference.primaryPreferred, err => {
593
+ if (err) {
594
+ callback(err);
595
+ return;
596
+ }
597
+
598
+ cursor.next(callback);
599
+ });
600
+
601
+ return;
602
+ }
603
+
604
+ // Very explicitly choose what is passed to selectServer
605
+ const serverSelectOptions = {};
606
+ if (cursor.cursorState.session) {
607
+ serverSelectOptions.session = cursor.cursorState.session;
608
+ }
609
+ if (cursor.options.readPreference) {
610
+ serverSelectOptions.readPreference = cursor.options.readPreference;
611
+ }
612
+
613
+ return cursor.topology.selectServer(serverSelectOptions, (err, server) => {
614
+ if (err) {
615
+ const disconnectHandler = cursor.disconnectHandler;
616
+ if (disconnectHandler != null) {
617
+ return disconnectHandler.addObjectAndMethod('cursor', cursor, 'next', [callback], callback);
618
+ }
619
+
620
+ return callback(err);
621
+ }
622
+
623
+ cursor.server = server;
624
+ cursor.cursorState.init = true;
625
+ if (collationNotSupported(cursor.server, cursor.cmd)) {
626
+ return callback(new MongoError(`server ${cursor.server.name} does not support collation`));
627
+ }
628
+
629
+ function done(err, result) {
630
+ if (
631
+ cursor.cursorState.cursorId &&
632
+ cursor.cursorState.cursorId.isZero() &&
633
+ cursor._endSession
634
+ ) {
635
+ cursor._endSession();
636
+ }
637
+
638
+ if (
639
+ cursor.cursorState.documents.length === 0 &&
640
+ cursor.cursorState.cursorId &&
641
+ cursor.cursorState.cursorId.isZero() &&
642
+ !cursor.cmd.tailable &&
643
+ !cursor.cmd.awaitData
644
+ ) {
645
+ return setCursorNotified(cursor, callback);
646
+ }
647
+
648
+ callback(err, result);
649
+ }
650
+
651
+ // NOTE: this is a special internal method for cloning a cursor, consider removing
652
+ if (cursor.cursorState.cursorId != null) {
653
+ return done();
654
+ }
655
+
656
+ const queryCallback = (err, r) => {
657
+ if (err) {
658
+ return done(err);
659
+ }
660
+
661
+ const result = r.message;
662
+ if (result.queryFailure) {
663
+ return done(new MongoError(result.documents[0]), null);
664
+ }
665
+
666
+ // Check if we have a command cursor
667
+ if (
668
+ Array.isArray(result.documents) &&
669
+ result.documents.length === 1 &&
670
+ (!cursor.cmd.find || (cursor.cmd.find && cursor.cmd.virtual === false)) &&
671
+ (typeof result.documents[0].cursor !== 'string' ||
672
+ result.documents[0]['$err'] ||
673
+ result.documents[0]['errmsg'] ||
674
+ Array.isArray(result.documents[0].result))
675
+ ) {
676
+ // We have an error document, return the error
677
+ if (result.documents[0]['$err'] || result.documents[0]['errmsg']) {
678
+ return done(new MongoError(result.documents[0]), null);
679
+ }
680
+
681
+ // We have a cursor document
682
+ if (result.documents[0].cursor != null && typeof result.documents[0].cursor !== 'string') {
683
+ var id = result.documents[0].cursor.id;
684
+ // If we have a namespace change set the new namespace for getmores
685
+ if (result.documents[0].cursor.ns) {
686
+ cursor.ns = result.documents[0].cursor.ns;
687
+ }
688
+ // Promote id to long if needed
689
+ cursor.cursorState.cursorId = typeof id === 'number' ? Long.fromNumber(id) : id;
690
+ cursor.cursorState.lastCursorId = cursor.cursorState.cursorId;
691
+ cursor.cursorState.operationTime = result.documents[0].operationTime;
692
+
693
+ // If we have a firstBatch set it
694
+ if (Array.isArray(result.documents[0].cursor.firstBatch)) {
695
+ cursor.cursorState.documents = result.documents[0].cursor.firstBatch; //.reverse();
696
+ }
697
+
698
+ // Return after processing command cursor
699
+ return done(null, result);
700
+ }
701
+
702
+ if (Array.isArray(result.documents[0].result)) {
703
+ cursor.cursorState.documents = result.documents[0].result;
704
+ cursor.cursorState.cursorId = Long.ZERO;
705
+ return done(null, result);
706
+ }
707
+ }
708
+
709
+ // Otherwise fall back to regular find path
710
+ const cursorId = result.cursorId || 0;
711
+ cursor.cursorState.cursorId = cursorId instanceof Long ? cursorId : Long.fromNumber(cursorId);
712
+ cursor.cursorState.documents = result.documents;
713
+ cursor.cursorState.lastCursorId = result.cursorId;
714
+
715
+ // Transform the results with passed in transformation method if provided
716
+ if (
717
+ cursor.cursorState.transforms &&
718
+ typeof cursor.cursorState.transforms.query === 'function'
719
+ ) {
720
+ cursor.cursorState.documents = cursor.cursorState.transforms.query(result);
721
+ }
722
+
723
+ done(null, result);
724
+ };
725
+
726
+ if (cursor.logger.isDebug()) {
727
+ cursor.logger.debug(
728
+ `issue initial query [${JSON.stringify(cursor.cmd)}] with flags [${JSON.stringify(
729
+ cursor.query
730
+ )}]`
731
+ );
732
+ }
733
+
734
+ if (cursor.cmd.find != null) {
735
+ server.query(cursor.ns, cursor.cmd, cursor.cursorState, cursor.options, queryCallback);
736
+ return;
737
+ }
738
+
739
+ server.command(cursor.ns, cursor.cmd, cursor.options, queryCallback);
740
+ });
741
+ };
742
+
743
+ /**
744
+ * Retrieve the next document from the cursor
745
+ * @method
746
+ * @param {resultCallback} callback A callback function
747
+ */
748
+ Cursor.prototype.next = function(callback) {
749
+ nextFunction(this, callback);
750
+ };
751
+
752
+ module.exports = Cursor;