mongodb 2.2.36 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cursor.js CHANGED
@@ -1,44 +1,45 @@
1
- "use strict";
2
-
3
- var inherits = require('util').inherits
4
- , f = require('util').format
5
- , formattedOrderClause = require('./utils').formattedOrderClause
6
- , handleCallback = require('./utils').handleCallback
7
- , ReadPreference = require('./read_preference')
8
- , MongoError = require('mongodb-core').MongoError
9
- , Readable = require('stream').Readable || require('readable-stream').Readable
10
- , Define = require('./metadata')
11
- , CoreCursor = require('mongodb-core').Cursor
12
- , Map = require('mongodb-core').BSON.Map
13
- , CoreReadPreference = require('mongodb-core').ReadPreference;
1
+ 'use strict';
2
+
3
+ var inherits = require('util').inherits,
4
+ f = require('util').format,
5
+ formattedOrderClause = require('./utils').formattedOrderClause,
6
+ handleCallback = require('./utils').handleCallback,
7
+ ReadPreference = require('mongodb-core').ReadPreference,
8
+ MongoError = require('mongodb-core').MongoError,
9
+ Readable = require('stream').Readable,
10
+ Define = require('./metadata'),
11
+ CoreCursor = require('mongodb-core').Cursor,
12
+ Map = require('mongodb-core').BSON.Map,
13
+ executeOperation = require('./utils').executeOperation;
14
14
 
15
15
  /**
16
16
  * @fileOverview The **Cursor** class is an internal class that embodies a cursor on MongoDB
17
17
  * allowing for iteration over the results returned from the underlying query. It supports
18
- * one by one document iteration, conversion to an array or can be iterated as a Node 0.10.X
18
+ * one by one document iteration, conversion to an array or can be iterated as a Node 4.X
19
19
  * or higher stream
20
20
  *
21
21
  * **CURSORS Cannot directly be instantiated**
22
22
  * @example
23
- * var MongoClient = require('mongodb').MongoClient,
24
- * test = require('assert');
23
+ * const MongoClient = require('mongodb').MongoClient;
24
+ * const test = require('assert');
25
25
  * // Connection url
26
- * var url = 'mongodb://localhost:27017/test';
26
+ * const url = 'mongodb://localhost:27017';
27
+ * // Database Name
28
+ * const dbName = 'test';
27
29
  * // Connect using MongoClient
28
- * MongoClient.connect(url, function(err, db) {
30
+ * MongoClient.connect(url, function(err, client) {
29
31
  * // Create a collection we want to drop later
30
- * var col = db.collection('createIndexExample1');
32
+ * const col = client.db(dbName).collection('createIndexExample1');
31
33
  * // Insert a bunch of documents
32
34
  * col.insert([{a:1, b:1}
33
35
  * , {a:2, b:2}, {a:3, b:3}
34
36
  * , {a:4, b:4}], {w:1}, function(err, result) {
35
37
  * test.equal(null, err);
36
- *
37
38
  * // Show that duplicate records got dropped
38
39
  * col.find({}).toArray(function(err, items) {
39
40
  * test.equal(null, err);
40
41
  * test.equal(4, items.length);
41
- * db.close();
42
+ * client.close();
42
43
  * });
43
44
  * });
44
45
  * });
@@ -99,7 +100,6 @@ var push = Array.prototype.push;
99
100
  */
100
101
  var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
101
102
  CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
102
- var self = this;
103
103
  var state = Cursor.INIT;
104
104
  var streamOptions = {};
105
105
 
@@ -109,61 +109,58 @@ var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
109
109
  var currentNumberOfRetries = numberOfRetries;
110
110
 
111
111
  // Get the promiseLibrary
112
- var promiseLibrary = options.promiseLibrary;
113
-
114
- // No promise library selected fall back
115
- if(!promiseLibrary) {
116
- promiseLibrary = typeof global.Promise == 'function' ?
117
- global.Promise : require('es6-promise').Promise;
118
- }
112
+ var promiseLibrary = options.promiseLibrary || Promise;
119
113
 
120
114
  // Set up
121
- Readable.call(this, {objectMode: true});
115
+ Readable.call(this, { objectMode: true });
122
116
 
123
117
  // Internal cursor state
124
118
  this.s = {
125
119
  // Tailable cursor options
126
- numberOfRetries: numberOfRetries
127
- , tailableRetryInterval: tailableRetryInterval
128
- , currentNumberOfRetries: currentNumberOfRetries
120
+ numberOfRetries: numberOfRetries,
121
+ tailableRetryInterval: tailableRetryInterval,
122
+ currentNumberOfRetries: currentNumberOfRetries,
129
123
  // State
130
- , state: state
124
+ state: state,
131
125
  // Stream options
132
- , streamOptions: streamOptions
126
+ streamOptions: streamOptions,
133
127
  // BSON
134
- , bson: bson
128
+ bson: bson,
135
129
  // Namespace
136
- , ns: ns
130
+ ns: ns,
137
131
  // Command
138
- , cmd: cmd
132
+ cmd: cmd,
139
133
  // Options
140
- , options: options
134
+ options: options,
141
135
  // Topology
142
- , topology: topology
136
+ topology: topology,
143
137
  // Topology options
144
- , topologyOptions: topologyOptions
138
+ topologyOptions: topologyOptions,
145
139
  // Promise library
146
- , promiseLibrary: promiseLibrary
140
+ promiseLibrary: promiseLibrary,
147
141
  // Current doc
148
- , currentDoc: null
149
- }
142
+ currentDoc: null,
143
+ // Optional ClientSession
144
+ session: options.session
145
+ };
150
146
 
151
147
  // Translate correctly
152
- if(self.s.options.noCursorTimeout == true) {
153
- self.addCursorFlag('noCursorTimeout', true);
148
+ if (this.s.options.noCursorTimeout === true) {
149
+ this.addCursorFlag('noCursorTimeout', true);
154
150
  }
155
151
 
156
152
  // Set the sort value
157
- this.sortValue = self.s.cmd.sort;
153
+ this.sortValue = this.s.cmd.sort;
158
154
 
159
155
  // Get the batchSize
160
- var batchSize = cmd.cursor && cmd.cursor.batchSize
161
- ? cmd.cursor && cmd.cursor.batchSize
162
- : (options.cursor && options.cursor.batchSize ? options.cursor.batchSize : 1000);
156
+ var batchSize =
157
+ cmd.cursor && cmd.cursor.batchSize
158
+ ? cmd.cursor && cmd.cursor.batchSize
159
+ : options.cursor && options.cursor.batchSize ? options.cursor.batchSize : 1000;
163
160
 
164
161
  // Set the batchSize
165
162
  this.setCursorBatchSize(batchSize);
166
- }
163
+ };
167
164
 
168
165
  /**
169
166
  * Cursor stream data event, fired for each document in the cursor.
@@ -199,11 +196,11 @@ inherits(Cursor, Readable);
199
196
  // Map core cursor _next method so we can apply mapping
200
197
  CoreCursor.prototype._next = CoreCursor.prototype.next;
201
198
 
202
- for(var name in CoreCursor.prototype) {
199
+ for (var name in CoreCursor.prototype) {
203
200
  Cursor.prototype[name] = CoreCursor.prototype[name];
204
201
  }
205
202
 
206
- var define = Cursor.define = new Define('Cursor', Cursor, true);
203
+ var define = (Cursor.define = new Define('Cursor', Cursor, true));
207
204
 
208
205
  /**
209
206
  * Check if there is any document still available in the cursor
@@ -213,39 +210,26 @@ var define = Cursor.define = new Define('Cursor', Cursor, true);
213
210
  * @return {Promise} returns Promise if no callback passed
214
211
  */
215
212
  Cursor.prototype.hasNext = function(callback) {
216
- var self = this;
213
+ return executeOperation(this.s.topology, hasNext, [this, callback], {
214
+ skipSessions: true
215
+ });
216
+ };
217
217
 
218
- // Execute using callback
219
- if(typeof callback == 'function') {
220
- if(self.s.currentDoc){
221
- return callback(null, true);
222
- } else {
223
- return nextObject(self, function(err, doc) {
224
- if (err) return callback(err, null);
225
- if (!doc) return callback(null, false);
226
- self.s.currentDoc = doc;
227
- callback(null, true);
228
- });
229
- }
218
+ const hasNext = (self, callback) => {
219
+ if (self.s.currentDoc) {
220
+ return callback(null, true);
230
221
  }
231
222
 
232
- // Return a Promise
233
- return new this.s.promiseLibrary(function(resolve, reject) {
234
- if(self.s.currentDoc){
235
- resolve(true);
236
- } else {
237
- nextObject(self, function(err, doc) {
238
- if(self.s.state == Cursor.CLOSED || self.isDead()) return resolve(false);
239
- if(err) return reject(err);
240
- if(!doc) return resolve(false);
241
- self.s.currentDoc = doc;
242
- resolve(true);
243
- });
244
- }
223
+ nextObject(self, function(err, doc) {
224
+ if (err) return callback(err, null);
225
+ if (self.s.state === Cursor.CLOSED || self.isDead()) return callback(null, false);
226
+ if (!doc) return callback(null, false);
227
+ self.s.currentDoc = doc;
228
+ callback(null, true);
245
229
  });
246
- }
230
+ };
247
231
 
248
- define.classMethod('hasNext', {callback: true, promise:true});
232
+ define.classMethod('hasNext', { callback: true, promise: true });
249
233
 
250
234
  /**
251
235
  * Get the next available document from the cursor, returns null if no more documents are available.
@@ -255,38 +239,24 @@ define.classMethod('hasNext', {callback: true, promise:true});
255
239
  * @return {Promise} returns Promise if no callback passed
256
240
  */
257
241
  Cursor.prototype.next = function(callback) {
258
- var self = this;
259
-
260
- // Execute using callback
261
- if(typeof callback == 'function') {
262
- // Return the currentDoc if someone called hasNext first
263
- if(self.s.currentDoc) {
264
- var doc = self.s.currentDoc;
265
- self.s.currentDoc = null;
266
- return callback(null, doc);
267
- }
242
+ return executeOperation(this.s.topology, next, [this, callback], {
243
+ skipSessions: true
244
+ });
245
+ };
268
246
 
269
- // Return the next object
270
- return nextObject(self, callback)
247
+ const next = (self, callback) => {
248
+ // Return the currentDoc if someone called hasNext first
249
+ if (self.s.currentDoc) {
250
+ var doc = self.s.currentDoc;
251
+ self.s.currentDoc = null;
252
+ return callback(null, doc);
271
253
  }
272
254
 
273
- // Return a Promise
274
- return new this.s.promiseLibrary(function(resolve, reject) {
275
- // Return the currentDoc if someone called hasNext first
276
- if(self.s.currentDoc) {
277
- var doc = self.s.currentDoc;
278
- self.s.currentDoc = null;
279
- return resolve(doc);
280
- }
281
-
282
- nextObject(self, function(err, r) {
283
- if(err) return reject(err);
284
- resolve(r);
285
- });
286
- });
287
- }
255
+ // Return the next object
256
+ nextObject(self, callback);
257
+ };
288
258
 
289
- define.classMethod('next', {callback: true, promise:true});
259
+ define.classMethod('next', { callback: true, promise: true });
290
260
 
291
261
  /**
292
262
  * Set the cursor query
@@ -295,12 +265,15 @@ define.classMethod('next', {callback: true, promise:true});
295
265
  * @return {Cursor}
296
266
  */
297
267
  Cursor.prototype.filter = function(filter) {
298
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
268
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
269
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
270
+ }
271
+
299
272
  this.s.cmd.query = filter;
300
273
  return this;
301
- }
274
+ };
302
275
 
303
- define.classMethod('filter', {callback: false, promise:false, returns: [Cursor]});
276
+ define.classMethod('filter', { callback: false, promise: false, returns: [Cursor] });
304
277
 
305
278
  /**
306
279
  * Set the cursor maxScan
@@ -309,12 +282,15 @@ define.classMethod('filter', {callback: false, promise:false, returns: [Cursor]}
309
282
  * @return {Cursor}
310
283
  */
311
284
  Cursor.prototype.maxScan = function(maxScan) {
312
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
285
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
286
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
287
+ }
288
+
313
289
  this.s.cmd.maxScan = maxScan;
314
290
  return this;
315
- }
291
+ };
316
292
 
317
- define.classMethod('maxScan', {callback: false, promise:false, returns: [Cursor]});
293
+ define.classMethod('maxScan', { callback: false, promise: false, returns: [Cursor] });
318
294
 
319
295
  /**
320
296
  * Set the cursor hint
@@ -323,12 +299,15 @@ define.classMethod('maxScan', {callback: false, promise:false, returns: [Cursor]
323
299
  * @return {Cursor}
324
300
  */
325
301
  Cursor.prototype.hint = function(hint) {
326
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
302
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
303
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
304
+ }
305
+
327
306
  this.s.cmd.hint = hint;
328
307
  return this;
329
- }
308
+ };
330
309
 
331
- define.classMethod('hint', {callback: false, promise:false, returns: [Cursor]});
310
+ define.classMethod('hint', { callback: false, promise: false, returns: [Cursor] });
332
311
 
333
312
  /**
334
313
  * Set the cursor min
@@ -337,12 +316,13 @@ define.classMethod('hint', {callback: false, promise:false, returns: [Cursor]});
337
316
  * @return {Cursor}
338
317
  */
339
318
  Cursor.prototype.min = function(min) {
340
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
319
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead())
320
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
341
321
  this.s.cmd.min = min;
342
322
  return this;
343
- }
323
+ };
344
324
 
345
- define.classMethod('min', {callback: false, promise:false, returns: [Cursor]});
325
+ define.classMethod('min', { callback: false, promise: false, returns: [Cursor] });
346
326
 
347
327
  /**
348
328
  * Set the cursor max
@@ -351,12 +331,15 @@ define.classMethod('min', {callback: false, promise:false, returns: [Cursor]});
351
331
  * @return {Cursor}
352
332
  */
353
333
  Cursor.prototype.max = function(max) {
354
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
334
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
335
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
336
+ }
337
+
355
338
  this.s.cmd.max = max;
356
339
  return this;
357
- }
340
+ };
358
341
 
359
- define.classMethod('max', {callback: false, promise:false, returns: [Cursor]});
342
+ define.classMethod('max', { callback: false, promise: false, returns: [Cursor] });
360
343
 
361
344
  /**
362
345
  * Set the cursor returnKey
@@ -365,12 +348,15 @@ define.classMethod('max', {callback: false, promise:false, returns: [Cursor]});
365
348
  * @return {Cursor}
366
349
  */
367
350
  Cursor.prototype.returnKey = function(value) {
368
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
351
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
352
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
353
+ }
354
+
369
355
  this.s.cmd.returnKey = value;
370
356
  return this;
371
- }
357
+ };
372
358
 
373
- define.classMethod('returnKey', {callback: false, promise:false, returns: [Cursor]});
359
+ define.classMethod('returnKey', { callback: false, promise: false, returns: [Cursor] });
374
360
 
375
361
  /**
376
362
  * Set the cursor showRecordId
@@ -379,12 +365,15 @@ define.classMethod('returnKey', {callback: false, promise:false, returns: [Curso
379
365
  * @return {Cursor}
380
366
  */
381
367
  Cursor.prototype.showRecordId = function(value) {
382
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
368
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
369
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
370
+ }
371
+
383
372
  this.s.cmd.showDiskLoc = value;
384
373
  return this;
385
- }
374
+ };
386
375
 
387
- define.classMethod('showRecordId', {callback: false, promise:false, returns: [Cursor]});
376
+ define.classMethod('showRecordId', { callback: false, promise: false, returns: [Cursor] });
388
377
 
389
378
  /**
390
379
  * Set the cursor snapshot
@@ -393,12 +382,15 @@ define.classMethod('showRecordId', {callback: false, promise:false, returns: [Cu
393
382
  * @return {Cursor}
394
383
  */
395
384
  Cursor.prototype.snapshot = function(value) {
396
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
385
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
386
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
387
+ }
388
+
397
389
  this.s.cmd.snapshot = value;
398
390
  return this;
399
- }
391
+ };
400
392
 
401
- define.classMethod('snapshot', {callback: false, promise:false, returns: [Cursor]});
393
+ define.classMethod('snapshot', { callback: false, promise: false, returns: [Cursor] });
402
394
 
403
395
  /**
404
396
  * Set a node.js specific cursor option
@@ -409,15 +401,23 @@ define.classMethod('snapshot', {callback: false, promise:false, returns: [Cursor
409
401
  * @return {Cursor}
410
402
  */
411
403
  Cursor.prototype.setCursorOption = function(field, value) {
412
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
413
- if(fields.indexOf(field) == -1) throw MongoError.create({message: f("option %s not a supported option %s", field, fields), driver:true });
404
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
405
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
406
+ }
407
+
408
+ if (fields.indexOf(field) === -1) {
409
+ throw MongoError.create({
410
+ message: f('option %s not a supported option %s', field, fields),
411
+ driver: true
412
+ });
413
+ }
414
+
414
415
  this.s[field] = value;
415
- if(field == 'numberOfRetries')
416
- this.s.currentNumberOfRetries = value;
416
+ if (field === 'numberOfRetries') this.s.currentNumberOfRetries = value;
417
417
  return this;
418
- }
418
+ };
419
419
 
420
- define.classMethod('setCursorOption', {callback: false, promise:false, returns: [Cursor]});
420
+ define.classMethod('setCursorOption', { callback: false, promise: false, returns: [Cursor] });
421
421
 
422
422
  /**
423
423
  * Add a cursor flag to the cursor
@@ -428,14 +428,26 @@ define.classMethod('setCursorOption', {callback: false, promise:false, returns:
428
428
  * @return {Cursor}
429
429
  */
430
430
  Cursor.prototype.addCursorFlag = function(flag, value) {
431
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
432
- if(flags.indexOf(flag) == -1) throw MongoError.create({message: f("flag %s not a supported flag %s", flag, flags), driver:true });
433
- if(typeof value != 'boolean') throw MongoError.create({message: f("flag %s must be a boolean value", flag), driver:true});
431
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
432
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
433
+ }
434
+
435
+ if (flags.indexOf(flag) === -1) {
436
+ throw MongoError.create({
437
+ message: f('flag %s not a supported flag %s', flag, flags),
438
+ driver: true
439
+ });
440
+ }
441
+
442
+ if (typeof value !== 'boolean') {
443
+ throw MongoError.create({ message: f('flag %s must be a boolean value', flag), driver: true });
444
+ }
445
+
434
446
  this.s.cmd[flag] = value;
435
447
  return this;
436
- }
448
+ };
437
449
 
438
- define.classMethod('addCursorFlag', {callback: false, promise:false, returns: [Cursor]});
450
+ define.classMethod('addCursorFlag', { callback: false, promise: false, returns: [Cursor] });
439
451
 
440
452
  /**
441
453
  * Add a query modifier to the cursor query
@@ -446,18 +458,24 @@ define.classMethod('addCursorFlag', {callback: false, promise:false, returns: [C
446
458
  * @return {Cursor}
447
459
  */
448
460
  Cursor.prototype.addQueryModifier = function(name, value) {
449
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
450
- if(name[0] != '$') throw MongoError.create({message: f("%s is not a valid query modifier"), driver:true});
461
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
462
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
463
+ }
464
+
465
+ if (name[0] !== '$') {
466
+ throw MongoError.create({ message: f('%s is not a valid query modifier'), driver: true });
467
+ }
468
+
451
469
  // Strip of the $
452
470
  var field = name.substr(1);
453
471
  // Set on the command
454
472
  this.s.cmd[field] = value;
455
473
  // Deal with the special case for sort
456
- if(field == 'orderby') this.s.cmd.sort = this.s.cmd[field];
474
+ if (field === 'orderby') this.s.cmd.sort = this.s.cmd[field];
457
475
  return this;
458
- }
476
+ };
459
477
 
460
- define.classMethod('addQueryModifier', {callback: false, promise:false, returns: [Cursor]});
478
+ define.classMethod('addQueryModifier', { callback: false, promise: false, returns: [Cursor] });
461
479
 
462
480
  /**
463
481
  * Add a comment to the cursor query allowing for tracking the comment in the log.
@@ -467,12 +485,15 @@ define.classMethod('addQueryModifier', {callback: false, promise:false, returns:
467
485
  * @return {Cursor}
468
486
  */
469
487
  Cursor.prototype.comment = function(value) {
470
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
488
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
489
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
490
+ }
491
+
471
492
  this.s.cmd.comment = value;
472
493
  return this;
473
- }
494
+ };
474
495
 
475
- define.classMethod('comment', {callback: false, promise:false, returns: [Cursor]});
496
+ define.classMethod('comment', { callback: false, promise: false, returns: [Cursor] });
476
497
 
477
498
  /**
478
499
  * Set a maxAwaitTimeMS on a tailing cursor query to allow to customize the timeout value for the option awaitData (Only supported on MongoDB 3.2 or higher, ignored otherwise)
@@ -482,13 +503,19 @@ define.classMethod('comment', {callback: false, promise:false, returns: [Cursor]
482
503
  * @return {Cursor}
483
504
  */
484
505
  Cursor.prototype.maxAwaitTimeMS = function(value) {
485
- if(typeof value != 'number') throw MongoError.create({message: "maxAwaitTimeMS must be a number", driver:true});
486
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
506
+ if (typeof value !== 'number') {
507
+ throw MongoError.create({ message: 'maxAwaitTimeMS must be a number', driver: true });
508
+ }
509
+
510
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
511
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
512
+ }
513
+
487
514
  this.s.cmd.maxAwaitTimeMS = value;
488
515
  return this;
489
- }
516
+ };
490
517
 
491
- define.classMethod('maxAwaitTimeMS', {callback: false, promise:false, returns: [Cursor]});
518
+ define.classMethod('maxAwaitTimeMS', { callback: false, promise: false, returns: [Cursor] });
492
519
 
493
520
  /**
494
521
  * Set a maxTimeMS on the cursor query, allowing for hard timeout limits on queries (Only supported on MongoDB 2.6 or higher)
@@ -498,17 +525,23 @@ define.classMethod('maxAwaitTimeMS', {callback: false, promise:false, returns: [
498
525
  * @return {Cursor}
499
526
  */
500
527
  Cursor.prototype.maxTimeMS = function(value) {
501
- if(typeof value != 'number') throw MongoError.create({message: "maxTimeMS must be a number", driver:true});
502
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
528
+ if (typeof value !== 'number') {
529
+ throw MongoError.create({ message: 'maxTimeMS must be a number', driver: true });
530
+ }
531
+
532
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
533
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
534
+ }
535
+
503
536
  this.s.cmd.maxTimeMS = value;
504
537
  return this;
505
- }
538
+ };
506
539
 
507
- define.classMethod('maxTimeMS', {callback: false, promise:false, returns: [Cursor]});
540
+ define.classMethod('maxTimeMS', { callback: false, promise: false, returns: [Cursor] });
508
541
 
509
542
  Cursor.prototype.maxTimeMs = Cursor.prototype.maxTimeMS;
510
543
 
511
- define.classMethod('maxTimeMs', {callback: false, promise:false, returns: [Cursor]});
544
+ define.classMethod('maxTimeMs', { callback: false, promise: false, returns: [Cursor] });
512
545
 
513
546
  /**
514
547
  * Sets a field projection for the query.
@@ -518,12 +551,15 @@ define.classMethod('maxTimeMs', {callback: false, promise:false, returns: [Curso
518
551
  * @return {Cursor}
519
552
  */
520
553
  Cursor.prototype.project = function(value) {
521
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
554
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
555
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
556
+ }
557
+
522
558
  this.s.cmd.fields = value;
523
559
  return this;
524
- }
560
+ };
525
561
 
526
- define.classMethod('project', {callback: false, promise:false, returns: [Cursor]});
562
+ define.classMethod('project', { callback: false, promise: false, returns: [Cursor] });
527
563
 
528
564
  /**
529
565
  * Sets the sort order of the cursor query.
@@ -534,39 +570,49 @@ define.classMethod('project', {callback: false, promise:false, returns: [Cursor]
534
570
  * @return {Cursor}
535
571
  */
536
572
  Cursor.prototype.sort = function(keyOrList, direction) {
537
- if(this.s.options.tailable) throw MongoError.create({message: "Tailable cursor doesn't support sorting", driver:true});
538
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
573
+ if (this.s.options.tailable) {
574
+ throw MongoError.create({ message: "Tailable cursor doesn't support sorting", driver: true });
575
+ }
576
+
577
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
578
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
579
+ }
580
+
539
581
  var order = keyOrList;
540
582
 
541
583
  // We have an array of arrays, we need to preserve the order of the sort
542
584
  // so we will us a Map
543
- if(Array.isArray(order) && Array.isArray(order[0])) {
544
- order = new Map(order.map(function(x) {
545
- var value = [x[0], null];
546
- if(x[1] == 'asc') {
547
- value[1] = 1;
548
- } else if(x[1] == 'desc') {
549
- value[1] = -1;
550
- } else if(x[1] == 1 || x[1] == -1) {
551
- value[1] = x[1];
552
- } else {
553
- throw new MongoError("Illegal sort clause, must be of the form [['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]");
554
- }
585
+ if (Array.isArray(order) && Array.isArray(order[0])) {
586
+ order = new Map(
587
+ order.map(function(x) {
588
+ var value = [x[0], null];
589
+ if (x[1] === 'asc') {
590
+ value[1] = 1;
591
+ } else if (x[1] === 'desc') {
592
+ value[1] = -1;
593
+ } else if (x[1] === 1 || x[1] === -1) {
594
+ value[1] = x[1];
595
+ } else {
596
+ throw new MongoError(
597
+ "Illegal sort clause, must be of the form [['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"
598
+ );
599
+ }
555
600
 
556
- return value;
557
- }));
601
+ return value;
602
+ })
603
+ );
558
604
  }
559
605
 
560
- if(direction != null) {
606
+ if (direction != null) {
561
607
  order = [[keyOrList, direction]];
562
608
  }
563
609
 
564
610
  this.s.cmd.sort = order;
565
611
  this.sortValue = order;
566
612
  return this;
567
- }
613
+ };
568
614
 
569
- define.classMethod('sort', {callback: false, promise:false, returns: [Cursor]});
615
+ define.classMethod('sort', { callback: false, promise: false, returns: [Cursor] });
570
616
 
571
617
  /**
572
618
  * Set the batch size for the cursor.
@@ -576,15 +622,24 @@ define.classMethod('sort', {callback: false, promise:false, returns: [Cursor]});
576
622
  * @return {Cursor}
577
623
  */
578
624
  Cursor.prototype.batchSize = function(value) {
579
- if(this.s.options.tailable) throw MongoError.create({message: "Tailable cursor doesn't support batchSize", driver:true});
580
- if(this.s.state == Cursor.CLOSED || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
581
- if(typeof value != 'number') throw MongoError.create({message: "batchSize requires an integer", driver:true});
625
+ if (this.s.options.tailable) {
626
+ throw MongoError.create({ message: "Tailable cursor doesn't support batchSize", driver: true });
627
+ }
628
+
629
+ if (this.s.state === Cursor.CLOSED || this.isDead()) {
630
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
631
+ }
632
+
633
+ if (typeof value !== 'number') {
634
+ throw MongoError.create({ message: 'batchSize requires an integer', driver: true });
635
+ }
636
+
582
637
  this.s.cmd.batchSize = value;
583
638
  this.setCursorBatchSize(value);
584
639
  return this;
585
- }
640
+ };
586
641
 
587
- define.classMethod('batchSize', {callback: false, promise:false, returns: [Cursor]});
642
+ define.classMethod('batchSize', { callback: false, promise: false, returns: [Cursor] });
588
643
 
589
644
  /**
590
645
  * Set the collation options for the cursor.
@@ -596,9 +651,9 @@ define.classMethod('batchSize', {callback: false, promise:false, returns: [Curso
596
651
  Cursor.prototype.collation = function(value) {
597
652
  this.s.cmd.collation = value;
598
653
  return this;
599
- }
654
+ };
600
655
 
601
- define.classMethod('collation', {callback: false, promise:false, returns: [Cursor]});
656
+ define.classMethod('collation', { callback: false, promise: false, returns: [Cursor] });
602
657
 
603
658
  /**
604
659
  * Set the limit for the cursor.
@@ -608,16 +663,25 @@ define.classMethod('collation', {callback: false, promise:false, returns: [Curso
608
663
  * @return {Cursor}
609
664
  */
610
665
  Cursor.prototype.limit = function(value) {
611
- if(this.s.options.tailable) throw MongoError.create({message: "Tailable cursor doesn't support limit", driver:true});
612
- if(this.s.state == Cursor.OPEN || this.s.state == Cursor.CLOSED || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
613
- if(typeof value != 'number') throw MongoError.create({message: "limit requires an integer", driver:true});
666
+ if (this.s.options.tailable) {
667
+ throw MongoError.create({ message: "Tailable cursor doesn't support limit", driver: true });
668
+ }
669
+
670
+ if (this.s.state === Cursor.OPEN || this.s.state === Cursor.CLOSED || this.isDead()) {
671
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
672
+ }
673
+
674
+ if (typeof value !== 'number') {
675
+ throw MongoError.create({ message: 'limit requires an integer', driver: true });
676
+ }
677
+
614
678
  this.s.cmd.limit = value;
615
679
  // this.cursorLimit = value;
616
680
  this.setCursorLimit(value);
617
681
  return this;
618
- }
682
+ };
619
683
 
620
- define.classMethod('limit', {callback: false, promise:false, returns: [Cursor]});
684
+ define.classMethod('limit', { callback: false, promise: false, returns: [Cursor] });
621
685
 
622
686
  /**
623
687
  * Set the skip for the cursor.
@@ -627,15 +691,24 @@ define.classMethod('limit', {callback: false, promise:false, returns: [Cursor]})
627
691
  * @return {Cursor}
628
692
  */
629
693
  Cursor.prototype.skip = function(value) {
630
- if(this.s.options.tailable) throw MongoError.create({message: "Tailable cursor doesn't support skip", driver:true});
631
- if(this.s.state == Cursor.OPEN || this.s.state == Cursor.CLOSED || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
632
- if(typeof value != 'number') throw MongoError.create({message: "skip requires an integer", driver:true});
694
+ if (this.s.options.tailable) {
695
+ throw MongoError.create({ message: "Tailable cursor doesn't support skip", driver: true });
696
+ }
697
+
698
+ if (this.s.state === Cursor.OPEN || this.s.state === Cursor.CLOSED || this.isDead()) {
699
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
700
+ }
701
+
702
+ if (typeof value !== 'number') {
703
+ throw MongoError.create({ message: 'skip requires an integer', driver: true });
704
+ }
705
+
633
706
  this.s.cmd.skip = value;
634
707
  this.setCursorSkip(value);
635
708
  return this;
636
- }
709
+ };
637
710
 
638
- define.classMethod('skip', {callback: false, promise:false, returns: [Cursor]});
711
+ define.classMethod('skip', { callback: false, promise: false, returns: [Cursor] });
639
712
 
640
713
  /**
641
714
  * The callback format for results
@@ -656,22 +729,17 @@ define.classMethod('skip', {callback: false, promise:false, returns: [Cursor]});
656
729
  * @return {null}
657
730
  */
658
731
 
659
- /**
660
- * Get the next available document from the cursor, returns null if no more documents are available.
661
- * @method
662
- * @param {Cursor~resultCallback} [callback] The result callback.
663
- * @throws {MongoError}
664
- * @deprecated
665
- * @return {Promise} returns Promise if no callback passed
666
- */
667
- Cursor.prototype.nextObject = Cursor.prototype.next;
668
-
732
+ // Get the next available document from the cursor, returns null if no more documents are available.
669
733
  var nextObject = function(self, callback) {
670
- if(self.s.state == Cursor.CLOSED || self.isDead && self.isDead()) return handleCallback(callback, MongoError.create({message: "Cursor is closed", driver:true}));
671
- if(self.s.state == Cursor.INIT && self.s.cmd.sort) {
734
+ if (self.s.state === Cursor.CLOSED || (self.isDead && self.isDead()))
735
+ return handleCallback(
736
+ callback,
737
+ MongoError.create({ message: 'Cursor is closed', driver: true })
738
+ );
739
+ if (self.s.state === Cursor.INIT && self.s.cmd.sort) {
672
740
  try {
673
741
  self.s.cmd.sort = formattedOrderClause(self.s.cmd.sort);
674
- } catch(err) {
742
+ } catch (err) {
675
743
  return handleCallback(callback, err);
676
744
  }
677
745
  }
@@ -679,31 +747,25 @@ var nextObject = function(self, callback) {
679
747
  // Get the next object
680
748
  self._next(function(err, doc) {
681
749
  self.s.state = Cursor.OPEN;
682
- if(err) return handleCallback(callback, err);
750
+ if (err) return handleCallback(callback, err);
683
751
  handleCallback(callback, null, doc);
684
752
  });
685
- }
686
-
687
- define.classMethod('nextObject', {callback: true, promise:true});
753
+ };
688
754
 
689
755
  // Trampoline emptying the number of retrieved items
690
756
  // without incurring a nextTick operation
691
757
  var loop = function(self, callback) {
692
758
  // No more items we are done
693
- if(self.bufferedCount() == 0) return;
759
+ if (self.bufferedCount() === 0) return;
694
760
  // Get the next document
695
761
  self._next(callback);
696
762
  // Loop
697
763
  return loop;
698
- }
699
-
700
- Cursor.prototype.next = Cursor.prototype.nextObject;
701
-
702
- define.classMethod('next', {callback: true, promise:true});
764
+ };
703
765
 
704
766
  /**
705
767
  * Iterates over all the documents for this cursor. As with **{cursor.toArray}**,
706
- * not all of the elements will be iterated if this cursor had been previously accessed.
768
+ * not all of the elements will be iterated if this cursor had been previouly accessed.
707
769
  * In that case, **{cursor.rewind}** can be used to reset the cursor. However, unlike
708
770
  * **{cursor.toArray}**, the cursor will only hold a maximum of batch size elements
709
771
  * at any given time if batch size is specified. Otherwise, the caller is responsible
@@ -723,37 +785,39 @@ Cursor.prototype.each = function(callback) {
723
785
  _each(this, callback);
724
786
  };
725
787
 
726
- define.classMethod('each', {callback: true, promise:false});
788
+ define.classMethod('each', { callback: true, promise: false });
727
789
 
728
790
  // Run the each loop
729
791
  var _each = function(self, callback) {
730
- if(!callback) throw MongoError.create({message: 'callback is mandatory', driver:true});
731
- if(self.isNotified()) return;
732
- if(self.s.state == Cursor.CLOSED || self.isDead()) {
733
- return handleCallback(callback, MongoError.create({message: "Cursor is closed", driver:true}));
792
+ if (!callback) throw MongoError.create({ message: 'callback is mandatory', driver: true });
793
+ if (self.isNotified()) return;
794
+ if (self.s.state === Cursor.CLOSED || self.isDead()) {
795
+ return handleCallback(
796
+ callback,
797
+ MongoError.create({ message: 'Cursor is closed', driver: true })
798
+ );
734
799
  }
735
800
 
736
- if(self.s.state == Cursor.INIT) self.s.state = Cursor.OPEN;
801
+ if (self.s.state === Cursor.INIT) self.s.state = Cursor.OPEN;
737
802
 
738
803
  // Define function to avoid global scope escape
739
804
  var fn = null;
740
805
  // Trampoline all the entries
741
- if(self.bufferedCount() > 0) {
742
- while(fn = loop(self, callback)) fn(self, callback);
806
+ if (self.bufferedCount() > 0) {
807
+ while ((fn = loop(self, callback))) fn(self, callback);
743
808
  _each(self, callback);
744
809
  } else {
745
810
  self.next(function(err, item) {
746
- if(err) return handleCallback(callback, err);
747
- if(item == null) {
748
- self.s.state = Cursor.CLOSED;
749
- return handleCallback(callback, null, null);
811
+ if (err) return handleCallback(callback, err);
812
+ if (item == null) {
813
+ return self.close({ skipKillCursors: true }, () => handleCallback(callback, null, null));
750
814
  }
751
815
 
752
- if(handleCallback(callback, null, item) == false) return;
816
+ if (handleCallback(callback, null, item) === false) return;
753
817
  _each(self, callback);
754
- })
818
+ });
755
819
  }
756
- }
820
+ };
757
821
 
758
822
  /**
759
823
  * The callback format for the forEach iterator method
@@ -776,19 +840,25 @@ var _each = function(self, callback) {
776
840
  * @return {null}
777
841
  */
778
842
  Cursor.prototype.forEach = function(iterator, callback) {
779
- this.each(function(err, doc){
780
- if(err) { callback(err); return false; }
781
- if(doc != null) { iterator(doc); return true; }
782
- if(doc == null && callback) {
843
+ this.each(function(err, doc) {
844
+ if (err) {
845
+ callback(err);
846
+ return false;
847
+ }
848
+ if (doc != null) {
849
+ iterator(doc);
850
+ return true;
851
+ }
852
+ if (doc == null && callback) {
783
853
  var internalCallback = callback;
784
854
  callback = null;
785
855
  internalCallback(null);
786
856
  return false;
787
857
  }
788
858
  });
789
- }
859
+ };
790
860
 
791
- define.classMethod('forEach', {callback: true, promise:false});
861
+ define.classMethod('forEach', { callback: true, promise: false });
792
862
 
793
863
  /**
794
864
  * Set the ReadPreference for the cursor.
@@ -797,20 +867,26 @@ define.classMethod('forEach', {callback: true, promise:false});
797
867
  * @throws {MongoError}
798
868
  * @return {Cursor}
799
869
  */
800
- Cursor.prototype.setReadPreference = function(r) {
801
- if(this.s.state != Cursor.INIT) throw MongoError.create({message: 'cannot change cursor readPreference after cursor has been accessed', driver:true});
802
- if(r instanceof ReadPreference) {
803
- this.s.options.readPreference = new CoreReadPreference(r.mode, r.tags, {maxStalenessSeconds: r.maxStalenessSeconds});
804
- } else if(typeof r == 'string'){
805
- this.s.options.readPreference = new CoreReadPreference(r);
806
- } else if(r instanceof CoreReadPreference) {
807
- this.s.options.readPreference = r;
870
+ Cursor.prototype.setReadPreference = function(readPreference) {
871
+ if (this.s.state !== Cursor.INIT) {
872
+ throw MongoError.create({
873
+ message: 'cannot change cursor readPreference after cursor has been accessed',
874
+ driver: true
875
+ });
876
+ }
877
+
878
+ if (readPreference instanceof ReadPreference) {
879
+ this.s.options.readPreference = readPreference;
880
+ } else if (typeof readPreference === 'string') {
881
+ this.s.options.readPreference = new ReadPreference(readPreference);
882
+ } else {
883
+ throw new TypeError('Invalid read preference: ' + readPreference);
808
884
  }
809
885
 
810
886
  return this;
811
- }
887
+ };
812
888
 
813
- define.classMethod('setReadPreference', {callback: false, promise:false, returns: [Cursor]});
889
+ define.classMethod('setReadPreference', { callback: false, promise: false, returns: [Cursor] });
814
890
 
815
891
  /**
816
892
  * The callback format for results
@@ -822,7 +898,7 @@ define.classMethod('setReadPreference', {callback: false, promise:false, returns
822
898
  /**
823
899
  * Returns an array of documents. The caller is responsible for making sure that there
824
900
  * is enough memory to store the results. Note that the array only contain partial
825
- * results when this cursor had been previously accessed. In that case,
901
+ * results when this cursor had been previouly accessed. In that case,
826
902
  * cursor.rewind() can be used to reset the cursor.
827
903
  * @method
828
904
  * @param {Cursor~toArrayResultCallback} [callback] The result callback.
@@ -831,19 +907,17 @@ define.classMethod('setReadPreference', {callback: false, promise:false, returns
831
907
  */
832
908
  Cursor.prototype.toArray = function(callback) {
833
909
  var self = this;
834
- if(self.s.options.tailable) throw MongoError.create({message: 'Tailable cursor cannot be converted to array', driver:true});
835
-
836
- // Execute using callback
837
- if(typeof callback == 'function') return toArray(self, callback);
838
-
839
- // Return a Promise
840
- return new this.s.promiseLibrary(function(resolve, reject) {
841
- toArray(self, function(err, r) {
842
- if(err) return reject(err);
843
- resolve(r);
910
+ if (self.s.options.tailable) {
911
+ throw MongoError.create({
912
+ message: 'Tailable cursor cannot be converted to array',
913
+ driver: true
844
914
  });
915
+ }
916
+
917
+ return executeOperation(this.s.topology, toArray, [this, callback], {
918
+ skipSessions: true
845
919
  });
846
- }
920
+ };
847
921
 
848
922
  var toArray = function(self, callback) {
849
923
  var items = [];
@@ -855,21 +929,20 @@ var toArray = function(self, callback) {
855
929
  // Fetch all the documents
856
930
  var fetchDocs = function() {
857
931
  self._next(function(err, doc) {
858
- if(err) return handleCallback(callback, err);
859
- if(doc == null) {
860
- self.s.state = Cursor.CLOSED;
861
- return handleCallback(callback, null, items);
932
+ if (err) return handleCallback(callback, err);
933
+ if (doc == null) {
934
+ return self.close({ skipKillCursors: true }, () => handleCallback(callback, null, items));
862
935
  }
863
936
 
864
937
  // Add doc to items
865
- items.push(doc)
938
+ items.push(doc);
866
939
 
867
940
  // Get all buffered objects
868
- if(self.bufferedCount() > 0) {
869
- var docs = self.readBufferedDocuments(self.bufferedCount())
941
+ if (self.bufferedCount() > 0) {
942
+ var docs = self.readBufferedDocuments(self.bufferedCount());
870
943
 
871
944
  // Transform the doc if transform method added
872
- if(self.s.transforms && typeof self.s.transforms.doc == 'function') {
945
+ if (self.s.transforms && typeof self.s.transforms.doc === 'function') {
873
946
  docs = docs.map(self.s.transforms.doc);
874
947
  }
875
948
 
@@ -878,13 +951,13 @@ var toArray = function(self, callback) {
878
951
 
879
952
  // Attempt a fetch
880
953
  fetchDocs();
881
- })
882
- }
954
+ });
955
+ };
883
956
 
884
957
  fetchDocs();
885
- }
958
+ };
886
959
 
887
- define.classMethod('toArray', {callback: true, promise:true});
960
+ define.classMethod('toArray', { callback: true, promise: true });
888
961
 
889
962
  /**
890
963
  * The callback format for results
@@ -900,102 +973,120 @@ define.classMethod('toArray', {callback: true, promise:true});
900
973
  * @param {object} [options=null] Optional settings.
901
974
  * @param {number} [options.skip=null] The number of documents to skip.
902
975
  * @param {number} [options.limit=null] The maximum amounts to count before aborting.
903
- * @param {number} [options.maxTimeMS=null] Number of milliseconds to wait before aborting the query.
976
+ * @param {number} [options.maxTimeMS=null] Number of miliseconds to wait before aborting the query.
904
977
  * @param {string} [options.hint=null] An index name hint for the query.
905
978
  * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
906
979
  * @param {Cursor~countResultCallback} [callback] The result callback.
907
980
  * @return {Promise} returns Promise if no callback passed
908
981
  */
909
982
  Cursor.prototype.count = function(applySkipLimit, opts, callback) {
910
- var self = this;
911
- if(self.s.cmd.query == null) throw MongoError.create({message: "count can only be used with find command", driver:true});
912
- if(typeof opts == 'function') callback = opts, opts = {};
983
+ if (this.s.cmd.query == null)
984
+ throw MongoError.create({ message: 'count can only be used with find command', driver: true });
985
+ if (typeof opts === 'function') (callback = opts), (opts = {});
913
986
  opts = opts || {};
914
987
 
915
- // Execute using callback
916
- if(typeof callback == 'function') return count(self, applySkipLimit, opts, callback);
917
-
918
- // Return a Promise
919
- return new this.s.promiseLibrary(function(resolve, reject) {
920
- count(self, applySkipLimit, opts, function(err, r) {
921
- if(err) return reject(err);
922
- resolve(r);
923
- });
988
+ return executeOperation(this.s.topology, count, [this, applySkipLimit, opts, callback], {
989
+ skipSessions: true
924
990
  });
925
991
  };
926
992
 
927
993
  var count = function(self, applySkipLimit, opts, callback) {
928
- if(typeof applySkipLimit == 'function') {
994
+ if (typeof applySkipLimit === 'function') {
929
995
  callback = applySkipLimit;
930
996
  applySkipLimit = true;
931
997
  }
932
998
 
933
- if(applySkipLimit) {
934
- if(typeof self.cursorSkip() == 'number') opts.skip = self.cursorSkip();
935
- if(typeof self.cursorLimit() == 'number') opts.limit = self.cursorLimit();
999
+ if (applySkipLimit) {
1000
+ if (typeof self.cursorSkip() === 'number') opts.skip = self.cursorSkip();
1001
+ if (typeof self.cursorLimit() === 'number') opts.limit = self.cursorLimit();
936
1002
  }
937
1003
 
938
1004
  // Command
939
1005
  var delimiter = self.s.ns.indexOf('.');
940
1006
 
941
1007
  var command = {
942
- 'count': self.s.ns.substr(delimiter+1), 'query': self.s.cmd.query
943
- }
1008
+ count: self.s.ns.substr(delimiter + 1),
1009
+ query: self.s.cmd.query
1010
+ };
944
1011
 
945
1012
  // Apply a readConcern if set
946
- if(self.s.cmd.readConcern) {
1013
+ if (self.s.cmd.readConcern) {
947
1014
  command.readConcern = self.s.cmd.readConcern;
948
1015
  }
949
1016
 
950
1017
  // Apply a hint if set
951
- if(self.s.cmd.hint) {
1018
+ if (self.s.cmd.hint) {
952
1019
  command.hint = self.s.cmd.hint;
953
1020
  }
954
1021
 
955
- if(typeof opts.maxTimeMS == 'number') {
1022
+ if (typeof opts.maxTimeMS === 'number') {
956
1023
  command.maxTimeMS = opts.maxTimeMS;
957
- } else if(self.s.cmd && typeof self.s.cmd.maxTimeMS == 'number') {
1024
+ } else if (self.s.cmd && typeof self.s.cmd.maxTimeMS === 'number') {
958
1025
  command.maxTimeMS = self.s.cmd.maxTimeMS;
959
1026
  }
960
1027
 
961
1028
  // Merge in any options
962
- if(opts.skip) command.skip = opts.skip;
963
- if(opts.limit) command.limit = opts.limit;
964
- if(self.s.options.hint) command.hint = self.s.options.hint;
1029
+ if (opts.skip) command.skip = opts.skip;
1030
+ if (opts.limit) command.limit = opts.limit;
1031
+ if (self.s.options.hint) command.hint = self.s.options.hint;
965
1032
 
966
1033
  // Set cursor server to the same as the topology
967
- self.server = self.topology;
1034
+ self.server = self.topology.s.coreTopology;
968
1035
 
969
1036
  // Execute the command
970
- self.topology.command(f("%s.$cmd", self.s.ns.substr(0, delimiter))
971
- , command, function(err, result) {
972
- callback(err, result ? result.result.n : null)
973
- }, self.options);
974
- }
1037
+ self.s.topology.command(
1038
+ f('%s.$cmd', self.s.ns.substr(0, delimiter)),
1039
+ command,
1040
+ function(err, result) {
1041
+ callback(err, result ? result.result.n : null);
1042
+ },
1043
+ self.options
1044
+ );
1045
+ };
975
1046
 
976
- define.classMethod('count', {callback: true, promise:true});
1047
+ define.classMethod('count', { callback: true, promise: true });
977
1048
 
978
1049
  /**
979
1050
  * Close the cursor, sending a KillCursor command and emitting close.
980
1051
  * @method
1052
+ * @param {object} [options] Optional settings.
1053
+ * @param {boolean} [options.skipKillCursors] Bypass calling killCursors when closing the cursor.
981
1054
  * @param {Cursor~resultCallback} [callback] The result callback.
982
1055
  * @return {Promise} returns Promise if no callback passed
983
1056
  */
984
- Cursor.prototype.close = function(callback) {
1057
+ Cursor.prototype.close = function(options, callback) {
1058
+ if (typeof options === 'function') (callback = options), (options = {});
1059
+ options = Object.assign({}, { skipKillCursors: false }, options);
1060
+
985
1061
  this.s.state = Cursor.CLOSED;
986
- // Kill the cursor
987
- this.kill();
988
- // Emit the close event for the cursor
989
- this.emit('close');
990
- // Callback if provided
991
- if(typeof callback == 'function') return handleCallback(callback, null, this);
992
- // Return a Promise
993
- return new this.s.promiseLibrary(function(resolve) {
994
- resolve();
995
- });
996
- }
1062
+ if (!options.skipKillCursors) {
1063
+ // Kill the cursor
1064
+ this.kill();
1065
+ }
997
1066
 
998
- define.classMethod('close', {callback: true, promise:true});
1067
+ const completeClose = () => {
1068
+ // Emit the close event for the cursor
1069
+ this.emit('close');
1070
+
1071
+ // Callback if provided
1072
+ if (typeof callback === 'function') {
1073
+ return handleCallback(callback, null, this);
1074
+ }
1075
+
1076
+ // Return a Promise
1077
+ return new this.s.promiseLibrary(function(resolve) {
1078
+ resolve();
1079
+ });
1080
+ };
1081
+
1082
+ if (this.s.session) {
1083
+ return this.s.session.endSession(() => completeClose());
1084
+ }
1085
+
1086
+ return completeClose();
1087
+ };
1088
+
1089
+ define.classMethod('close', { callback: true, promise: true });
999
1090
 
1000
1091
  /**
1001
1092
  * Map all documents using the provided function
@@ -1004,16 +1095,18 @@ define.classMethod('close', {callback: true, promise:true});
1004
1095
  * @return {Cursor}
1005
1096
  */
1006
1097
  Cursor.prototype.map = function(transform) {
1007
- if(this.cursorState.transforms && this.cursorState.transforms.doc) {
1098
+ if (this.cursorState.transforms && this.cursorState.transforms.doc) {
1008
1099
  var oldTransform = this.cursorState.transforms.doc;
1009
- this.cursorState.transforms.doc = function (doc) { return transform(oldTransform(doc)); };
1100
+ this.cursorState.transforms.doc = function(doc) {
1101
+ return transform(oldTransform(doc));
1102
+ };
1010
1103
  } else {
1011
1104
  this.cursorState.transforms = { doc: transform };
1012
1105
  }
1013
1106
  return this;
1014
- }
1107
+ };
1015
1108
 
1016
- define.classMethod('map', {callback: false, promise:false, returns: [Cursor]});
1109
+ define.classMethod('map', { callback: false, promise: false, returns: [Cursor] });
1017
1110
 
1018
1111
  /**
1019
1112
  * Is the cursor closed
@@ -1022,17 +1115,17 @@ define.classMethod('map', {callback: false, promise:false, returns: [Cursor]});
1022
1115
  */
1023
1116
  Cursor.prototype.isClosed = function() {
1024
1117
  return this.isDead();
1025
- }
1118
+ };
1026
1119
 
1027
- define.classMethod('isClosed', {callback: false, promise:false, returns: [Boolean]});
1120
+ define.classMethod('isClosed', { callback: false, promise: false, returns: [Boolean] });
1028
1121
 
1029
1122
  Cursor.prototype.destroy = function(err) {
1030
- if(err) this.emit('error', err);
1123
+ if (err) this.emit('error', err);
1031
1124
  this.pause();
1032
1125
  this.close();
1033
- }
1126
+ };
1034
1127
 
1035
- define.classMethod('destroy', {callback: false, promise:false});
1128
+ define.classMethod('destroy', { callback: false, promise: false });
1036
1129
 
1037
1130
  /**
1038
1131
  * Return a modified Readable stream including a possible transform method.
@@ -1044,9 +1137,9 @@ define.classMethod('destroy', {callback: false, promise:false});
1044
1137
  Cursor.prototype.stream = function(options) {
1045
1138
  this.s.streamOptions = options || {};
1046
1139
  return this;
1047
- }
1140
+ };
1048
1141
 
1049
- define.classMethod('stream', {callback: false, promise:false, returns: [Cursor]});
1142
+ define.classMethod('stream', { callback: false, promise: false, returns: [Cursor] });
1050
1143
 
1051
1144
  /**
1052
1145
  * Execute the explain for the cursor
@@ -1055,41 +1148,33 @@ define.classMethod('stream', {callback: false, promise:false, returns: [Cursor]}
1055
1148
  * @return {Promise} returns Promise if no callback passed
1056
1149
  */
1057
1150
  Cursor.prototype.explain = function(callback) {
1058
- var self = this;
1059
1151
  this.s.cmd.explain = true;
1060
1152
 
1061
1153
  // Do we have a readConcern
1062
- if(this.s.cmd.readConcern) {
1154
+ if (this.s.cmd.readConcern) {
1063
1155
  delete this.s.cmd['readConcern'];
1064
1156
  }
1065
1157
 
1066
- // Execute using callback
1067
- if(typeof callback == 'function') return this._next(callback);
1068
-
1069
- // Return a Promise
1070
- return new this.s.promiseLibrary(function(resolve, reject) {
1071
- self._next(function(err, r) {
1072
- if(err) return reject(err);
1073
- resolve(r);
1074
- });
1158
+ return executeOperation(this.s.topology, this._next.bind(this), [callback], {
1159
+ skipSessions: true
1075
1160
  });
1076
- }
1161
+ };
1077
1162
 
1078
- define.classMethod('explain', {callback: true, promise:true});
1163
+ define.classMethod('explain', { callback: true, promise: true });
1079
1164
 
1080
1165
  Cursor.prototype._read = function() {
1081
1166
  var self = this;
1082
- if(self.s.state == Cursor.CLOSED || self.isDead()) {
1167
+ if (self.s.state === Cursor.CLOSED || self.isDead()) {
1083
1168
  return self.push(null);
1084
1169
  }
1085
1170
 
1086
1171
  // Get the next item
1087
- self.nextObject(function(err, result) {
1088
- if(err) {
1089
- if(self.listeners('error') && self.listeners('error').length > 0) {
1172
+ self.next(function(err, result) {
1173
+ if (err) {
1174
+ if (self.listeners('error') && self.listeners('error').length > 0) {
1090
1175
  self.emit('error', err);
1091
1176
  }
1092
- if(!self.isDead()) self.close();
1177
+ if (!self.isDead()) self.close();
1093
1178
 
1094
1179
  // Emit end event
1095
1180
  self.emit('end');
@@ -1097,22 +1182,33 @@ Cursor.prototype._read = function() {
1097
1182
  }
1098
1183
 
1099
1184
  // If we provided a transformation method
1100
- if(typeof self.s.streamOptions.transform == 'function' && result != null) {
1185
+ if (typeof self.s.streamOptions.transform === 'function' && result != null) {
1101
1186
  return self.push(self.s.streamOptions.transform(result));
1102
1187
  }
1103
1188
 
1104
1189
  // If we provided a map function
1105
- if(self.cursorState.transforms && typeof self.cursorState.transforms.doc == 'function' && result != null) {
1190
+ if (
1191
+ self.cursorState.transforms &&
1192
+ typeof self.cursorState.transforms.doc === 'function' &&
1193
+ result != null
1194
+ ) {
1106
1195
  return self.push(self.cursorState.transforms.doc(result));
1107
1196
  }
1108
1197
 
1109
1198
  // Return the result
1110
1199
  self.push(result);
1200
+
1201
+ if (result === null && self.isDead()) {
1202
+ self.once('end', () => {
1203
+ self.close();
1204
+ self.emit('finish');
1205
+ });
1206
+ }
1111
1207
  });
1112
- }
1208
+ };
1113
1209
 
1114
1210
  Object.defineProperty(Cursor.prototype, 'readPreference', {
1115
- enumerable:true,
1211
+ enumerable: true,
1116
1212
  get: function() {
1117
1213
  if (!this || !this.s) {
1118
1214
  return null;