mongodb 2.1.0-alpha → 2.1.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.
Files changed (63) hide show
  1. package/HISTORY.md +574 -429
  2. package/Makefile +2 -5
  3. package/README.md +108 -15
  4. package/conf.json +17 -13
  5. package/index.js +13 -2
  6. package/lib/admin.js +113 -47
  7. package/lib/aggregation_cursor.js +56 -28
  8. package/lib/apm.js +608 -0
  9. package/lib/bulk/common.js +7 -7
  10. package/lib/bulk/ordered.js +56 -17
  11. package/lib/bulk/unordered.js +52 -14
  12. package/lib/collection.js +671 -212
  13. package/lib/command_cursor.js +60 -32
  14. package/lib/cursor.js +313 -115
  15. package/lib/db.js +264 -105
  16. package/lib/gridfs/chunk.js +26 -29
  17. package/lib/gridfs/grid_store.js +150 -64
  18. package/lib/gridfs-stream/download.js +310 -0
  19. package/lib/gridfs-stream/index.js +335 -0
  20. package/lib/gridfs-stream/upload.js +450 -0
  21. package/lib/metadata.js +64 -0
  22. package/lib/mongo_client.js +69 -39
  23. package/lib/mongos.js +65 -20
  24. package/lib/replset.js +69 -34
  25. package/lib/server.js +35 -1
  26. package/lib/topology_base.js +22 -10
  27. package/lib/url_parser.js +111 -13
  28. package/lib/utils.js +9 -8
  29. package/mongolabs.js +427 -0
  30. package/package.json +8 -6
  31. package/t.js +68 -51
  32. package/test.js +12 -0
  33. package/test_boot/boot.sh +3 -0
  34. package/test_boot/ca.pem +49 -0
  35. package/test_boot/client.pem +48 -0
  36. package/test_boot/client_password.pem +51 -0
  37. package/test_boot/connect.js +29 -0
  38. package/test_boot/data/WiredTiger +2 -0
  39. package/test_boot/data/WiredTiger.lock +1 -0
  40. package/test_boot/data/WiredTiger.turtle +6 -0
  41. package/test_boot/data/WiredTiger.wt +0 -0
  42. package/test_boot/data/WiredTigerLAS.wt +0 -0
  43. package/test_boot/data/_mdb_catalog.wt +0 -0
  44. package/test_boot/data/collection-0-757073248613337118.wt +0 -0
  45. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-44-37Z-00000 +0 -0
  46. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-45-15Z-00000 +0 -0
  47. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-46-31Z-00000 +0 -0
  48. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-47-25Z-00000 +0 -0
  49. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-49-07Z-00000 +0 -0
  50. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-50-41Z-00000 +0 -0
  51. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-50-53Z-00000 +0 -0
  52. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-52-31Z-00000 +0 -0
  53. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-54-53Z-00000 +0 -0
  54. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-55-09Z-00000 +0 -0
  55. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-55-38Z-00000 +0 -0
  56. package/test_boot/data/index-1-757073248613337118.wt +0 -0
  57. package/test_boot/data/mongod.lock +0 -0
  58. package/test_boot/data/sizeStorer.wt +0 -0
  59. package/test_boot/data/storage.bson +0 -0
  60. package/test_boot/server_password.pem +51 -0
  61. package/.travis.yml +0 -10
  62. package/t1.js +0 -59
  63. package/wercker.yml +0 -19
package/lib/cursor.js CHANGED
@@ -11,7 +11,9 @@ var inherits = require('util').inherits
11
11
  , ReadPreference = require('./read_preference')
12
12
  , MongoError = require('mongodb-core').MongoError
13
13
  , Readable = require('stream').Readable || require('readable-stream').Readable
14
+ , Define = require('./metadata')
14
15
  , CoreCursor = require('mongodb-core').Cursor
16
+ , Map = require('mongodb-core').BSON.Map
15
17
  , Query = require('mongodb-core').Query
16
18
  , CoreReadPreference = require('mongodb-core').ReadPreference;
17
19
 
@@ -71,7 +73,34 @@ var fields = ['numberOfRetries', 'tailableRetryInterval'];
71
73
  * @fires Cursor#readable
72
74
  * @return {Cursor} a Cursor instance.
73
75
  * @example
74
- * Some example
76
+ * Cursor cursor options.
77
+ *
78
+ * collection.find({}).project({a:1}) // Create a projection of field a
79
+ * collection.find({}).skip(1).limit(10) // Skip 1 and limit 10
80
+ * collection.find({}).batchSize(5) // Set batchSize on cursor to 5
81
+ * collection.find({}).filter({a:1}) // Set query on the cursor
82
+ * collection.find({}).comment('add a comment') // Add a comment to the query, allowing to correlate queries
83
+ * collection.find({}).addCursorFlag('tailable', true) // Set cursor as tailable
84
+ * collection.find({}).addCursorFlag('oplogReplay', true) // Set cursor as oplogReplay
85
+ * collection.find({}).addCursorFlag('noCursorTimeout', true) // Set cursor as noCursorTimeout
86
+ * collection.find({}).addCursorFlag('awaitData', true) // Set cursor as awaitData
87
+ * collection.find({}).addCursorFlag('exhaust', true) // Set cursor as exhaust
88
+ * collection.find({}).addCursorFlag('partial', true) // Set cursor as partial
89
+ * collection.find({}).addQueryModifier('$orderby', {a:1}) // Set $orderby {a:1}
90
+ * collection.find({}).max(10) // Set the cursor maxScan
91
+ * collection.find({}).maxScan(10) // Set the cursor maxScan
92
+ * collection.find({}).maxTimeMS(1000) // Set the cursor maxTimeMS
93
+ * collection.find({}).min(100) // Set the cursor min
94
+ * collection.find({}).returnKey(10) // Set the cursor returnKey
95
+ * collection.find({}).setReadPreference(ReadPreference.PRIMARY) // Set the cursor readPreference
96
+ * collection.find({}).showRecordId(true) // Set the cursor showRecordId
97
+ * collection.find({}).snapshot(true) // Set the cursor snapshot
98
+ * collection.find({}).sort([['a', 1]]) // Sets the sort order of the cursor query
99
+ * collection.find({}).hint('a_1') // Set the cursor hint
100
+ *
101
+ * All options are chainable, so one can do the following.
102
+ *
103
+ * collection.find({}).maxTimeMS(1000).maxScan(100).skip(1).toArray(..)
75
104
  */
76
105
  var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
77
106
  CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
@@ -119,7 +148,7 @@ var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
119
148
  // Topology options
120
149
  , topologyOptions: topologyOptions
121
150
  // Promise library
122
- , promiseLibrary: promiseLibrary
151
+ , promiseLibrary: promiseLibrary
123
152
  // Current doc
124
153
  , currentDoc: null
125
154
  }
@@ -127,7 +156,6 @@ var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
127
156
  // Legacy fields
128
157
  this.timeout = self.s.options.noCursorTimeout == true;
129
158
  this.sortValue = self.s.cmd.sort;
130
- this.readPreference = self.s.options.readPreference;
131
159
  }
132
160
 
133
161
  /**
@@ -168,43 +196,54 @@ for(var name in CoreCursor.prototype) {
168
196
  Cursor.prototype[name] = CoreCursor.prototype[name];
169
197
  }
170
198
 
199
+ var define = Cursor.define = new Define('Cursor', Cursor, true);
200
+
171
201
  /**
172
202
  * Check if there is any document still available in the cursor
173
203
  * @method
174
- * @param {Cursor~resultCallback} callback The result callback.
204
+ * @param {Cursor~resultCallback} [callback] The result callback.
175
205
  * @throws {MongoError}
176
- * @deprecated
177
206
  * @return {Promise} returns Promise if no callback passed
178
207
  */
179
208
  Cursor.prototype.hasNext = function(callback) {
180
209
  var self = this;
181
210
 
182
211
  // Execute using callback
183
- if(typeof callback == 'function') return nextObject(self, function(err, doc) {
184
- if(!doc) return callback(null, false);
185
- self.s.currentDoc = doc;
186
- callback(null, true);
187
- });
212
+ if(typeof callback == 'function') {
213
+ if(self.s.currentDoc){
214
+ return callback(null, true);
215
+ } else {
216
+ return nextObject(self, function(err, doc) {
217
+ if(!doc) return callback(null, false);
218
+ self.s.currentDoc = doc;
219
+ callback(null, true);
220
+ });
221
+ }
222
+ }
188
223
 
189
224
  // Return a Promise
190
225
  return new this.s.promiseLibrary(function(resolve, reject) {
191
- nextObject(self, function(err, doc) {
192
- if(self.s.state == Cursor.CLOSED || self.isDead()) return resolve(false);
193
- if(err) return reject(err);
194
- if(!doc) return resolve(false);
195
- self.s.currentDoc = doc;
196
- resolve(true);
197
- });
226
+ if(self.s.currentDoc){
227
+ resolve(true);
228
+ } else {
229
+ nextObject(self, function(err, doc) {
230
+ if(self.s.state == Cursor.CLOSED || self.isDead()) return resolve(false);
231
+ if(err) return reject(err);
232
+ if(!doc) return resolve(false);
233
+ self.s.currentDoc = doc;
234
+ resolve(true);
235
+ });
236
+ }
198
237
  });
199
238
  }
200
239
 
240
+ define.classMethod('hasNext', {callback: true, promise:true});
201
241
 
202
242
  /**
203
243
  * Get the next available document from the cursor, returns null if no more documents are available.
204
244
  * @method
205
- * @param {Cursor~resultCallback} callback The result callback.
245
+ * @param {Cursor~resultCallback} [callback] The result callback.
206
246
  * @throws {MongoError}
207
- * @deprecated
208
247
  * @return {Promise} returns Promise if no callback passed
209
248
  */
210
249
  Cursor.prototype.next = function(callback) {
@@ -234,11 +273,13 @@ Cursor.prototype.next = function(callback) {
234
273
 
235
274
  nextObject(self, function(err, r) {
236
275
  if(err) return reject(err);
237
- resolve(r);
276
+ resolve(r);
238
277
  });
239
278
  });
240
279
  }
241
280
 
281
+ define.classMethod('next', {callback: true, promise:true});
282
+
242
283
  /**
243
284
  * Set the cursor query
244
285
  * @method
@@ -246,11 +287,111 @@ Cursor.prototype.next = function(callback) {
246
287
  * @return {Cursor}
247
288
  */
248
289
  Cursor.prototype.filter = function(filter) {
249
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw new MongoError("Cursor is closed");
290
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
250
291
  this.s.cmd.query = filter;
251
292
  return this;
252
293
  }
253
294
 
295
+ define.classMethod('filter', {callback: false, promise:false, returns: [Cursor]});
296
+
297
+ /**
298
+ * Set the cursor maxScan
299
+ * @method
300
+ * @param {object} maxScan Constrains the query to only scan the specified number of documents when fulfilling the query
301
+ * @return {Cursor}
302
+ */
303
+ Cursor.prototype.maxScan = function(maxScan) {
304
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
305
+ this.s.cmd.maxScan = maxScan;
306
+ return this;
307
+ }
308
+
309
+ define.classMethod('maxScan', {callback: false, promise:false, returns: [Cursor]});
310
+
311
+ /**
312
+ * Set the cursor hint
313
+ * @method
314
+ * @param {object} hint If specified, then the query system will only consider plans using the hinted index.
315
+ * @return {Cursor}
316
+ */
317
+ Cursor.prototype.hint = function(hint) {
318
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
319
+ this.s.cmd.hint = hint;
320
+ return this;
321
+ }
322
+
323
+ define.classMethod('hint', {callback: false, promise:false, returns: [Cursor]});
324
+
325
+ /**
326
+ * Set the cursor min
327
+ * @method
328
+ * @param {object} min Specify a $min value to specify the inclusive lower bound for a specific index in order to constrain the results of find(). The $min specifies the lower bound for all keys of a specific index in order.
329
+ * @return {Cursor}
330
+ */
331
+ Cursor.prototype.min = function(min) {
332
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
333
+ this.s.cmd.min = min;
334
+ return this;
335
+ }
336
+
337
+ define.classMethod('min', {callback: false, promise:false, returns: [Cursor]});
338
+
339
+ /**
340
+ * Set the cursor max
341
+ * @method
342
+ * @param {object} max Specify a $max value to specify the exclusive upper bound for a specific index in order to constrain the results of find(). The $max specifies the upper bound for all keys of a specific index in order.
343
+ * @return {Cursor}
344
+ */
345
+ Cursor.prototype.max = function(max) {
346
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
347
+ this.s.cmd.max = max;
348
+ return this;
349
+ }
350
+
351
+ define.classMethod('max', {callback: false, promise:false, returns: [Cursor]});
352
+
353
+ /**
354
+ * Set the cursor returnKey
355
+ * @method
356
+ * @param {object} returnKey Only return the index field or fields for the results of the query. If $returnKey is set to true and the query does not use an index to perform the read operation, the returned documents will not contain any fields. Use one of the following forms:
357
+ * @return {Cursor}
358
+ */
359
+ Cursor.prototype.returnKey = function(value) {
360
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
361
+ this.s.cmd.returnKey = value;
362
+ return this;
363
+ }
364
+
365
+ define.classMethod('returnKey', {callback: false, promise:false, returns: [Cursor]});
366
+
367
+ /**
368
+ * Set the cursor showRecordId
369
+ * @method
370
+ * @param {object} showRecordId The $showDiskLoc option has now been deprecated and replaced with the showRecordId field. $showDiskLoc will still be accepted for OP_QUERY stye find.
371
+ * @return {Cursor}
372
+ */
373
+ Cursor.prototype.showRecordId = function(value) {
374
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
375
+ this.s.cmd.showDiskLoc = value;
376
+ return this;
377
+ }
378
+
379
+ define.classMethod('showRecordId', {callback: false, promise:false, returns: [Cursor]});
380
+
381
+ /**
382
+ * Set the cursor snapshot
383
+ * @method
384
+ * @param {object} snapshot The $snapshot operator prevents the cursor from returning a document more than once because an intervening write operation results in a move of the document.
385
+ * @return {Cursor}
386
+ */
387
+ Cursor.prototype.snapshot = function(value) {
388
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
389
+ this.s.cmd.snapshot = value;
390
+ return this;
391
+ }
392
+
393
+ define.classMethod('snapshot', {callback: false, promise:false, returns: [Cursor]});
394
+
254
395
  /**
255
396
  * Set a node.js specific cursor option
256
397
  * @method
@@ -260,14 +401,16 @@ Cursor.prototype.filter = function(filter) {
260
401
  * @return {Cursor}
261
402
  */
262
403
  Cursor.prototype.setCursorOption = function(field, value) {
263
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw new MongoError("Cursor is closed");
264
- if(fields.indexOf(field) == -1) throw new MongoError(f("option %s not a supported option %s", field, fields));
404
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
405
+ if(fields.indexOf(field) == -1) throw MongoError.create({message: f("option %s not a supported option %s", field, fields), driver:true });
265
406
  this.s[field] = value;
266
- if(field == 'numberOfRetries')
407
+ if(field == 'numberOfRetries')
267
408
  this.s.currentNumberOfRetries = value;
268
409
  return this;
269
410
  }
270
411
 
412
+ define.classMethod('setCursorOption', {callback: false, promise:false, returns: [Cursor]});
413
+
271
414
  /**
272
415
  * Add a cursor flag to the cursor
273
416
  * @method
@@ -277,13 +420,15 @@ Cursor.prototype.setCursorOption = function(field, value) {
277
420
  * @return {Cursor}
278
421
  */
279
422
  Cursor.prototype.addCursorFlag = function(flag, value) {
280
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw new MongoError("Cursor is closed");
281
- if(flags.indexOf(flag) == -1) throw new MongoError(f("flag %s not a supported flag %s", flag, flags));
282
- if(typeof value != 'boolean') throw new MongoError(f("flag %s must be a boolean value", flag));
423
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
424
+ if(flags.indexOf(flag) == -1) throw MongoError.create({message: f("flag %s not a supported flag %s", flag, flags), driver:true });
425
+ if(typeof value != 'boolean') throw MongoError.create({message: f("flag %s must be a boolean value", flag), driver:true});
283
426
  this.s.cmd[flag] = value;
284
427
  return this;
285
428
  }
286
429
 
430
+ define.classMethod('addCursorFlag', {callback: false, promise:false, returns: [Cursor]});
431
+
287
432
  /**
288
433
  * Add a query modifier to the cursor query
289
434
  * @method
@@ -293,8 +438,8 @@ Cursor.prototype.addCursorFlag = function(flag, value) {
293
438
  * @return {Cursor}
294
439
  */
295
440
  Cursor.prototype.addQueryModifier = function(name, value) {
296
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw new MongoError("Cursor is closed");
297
- if(name[0] != '$') throw new MongoError(f("%s is not a valid query modifier"));
441
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
442
+ if(name[0] != '$') throw MongoError.create({message: f("%s is not a valid query modifier"), driver:true});
298
443
  // Strip of the $
299
444
  var field = name.substr(1);
300
445
  // Set on the command
@@ -304,6 +449,8 @@ Cursor.prototype.addQueryModifier = function(name, value) {
304
449
  return this;
305
450
  }
306
451
 
452
+ define.classMethod('addQueryModifier', {callback: false, promise:false, returns: [Cursor]});
453
+
307
454
  /**
308
455
  * Add a comment to the cursor query allowing for tracking the comment in the log.
309
456
  * @method
@@ -312,11 +459,29 @@ Cursor.prototype.addQueryModifier = function(name, value) {
312
459
  * @return {Cursor}
313
460
  */
314
461
  Cursor.prototype.comment = function(value) {
315
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw new MongoError("Cursor is closed");
462
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
316
463
  this.s.cmd.comment = value;
317
464
  return this;
318
465
  }
319
466
 
467
+ define.classMethod('comment', {callback: false, promise:false, returns: [Cursor]});
468
+
469
+ /**
470
+ * 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)
471
+ * @method
472
+ * @param {number} value Number of milliseconds to wait before aborting the tailed query.
473
+ * @throws {MongoError}
474
+ * @return {Cursor}
475
+ */
476
+ Cursor.prototype.maxAwaitTimeMS = function(value) {
477
+ if(typeof value != 'number') throw MongoError.create({message: "maxAwaitTimeMS must be a number", driver:true});
478
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
479
+ this.s.cmd.maxAwaitTimeMS = value;
480
+ return this;
481
+ }
482
+
483
+ define.classMethod('maxAwaitTimeMS', {callback: false, promise:false, returns: [Cursor]});
484
+
320
485
  /**
321
486
  * Set a maxTimeMS on the cursor query, allowing for hard timeout limits on queries (Only supported on MongoDB 2.6 or higher)
322
487
  * @method
@@ -325,14 +490,18 @@ Cursor.prototype.comment = function(value) {
325
490
  * @return {Cursor}
326
491
  */
327
492
  Cursor.prototype.maxTimeMS = function(value) {
328
- if(typeof value != 'number') throw new MongoError("maxTimeMS must be a number");
329
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw new MongoError("Cursor is closed");
493
+ if(typeof value != 'number') throw MongoError.create({message: "maxTimeMS must be a number", driver:true});
494
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
330
495
  this.s.cmd.maxTimeMS = value;
331
496
  return this;
332
497
  }
333
498
 
499
+ define.classMethod('maxTimeMS', {callback: false, promise:false, returns: [Cursor]});
500
+
334
501
  Cursor.prototype.maxTimeMs = Cursor.prototype.maxTimeMS;
335
502
 
503
+ define.classMethod('maxTimeMs', {callback: false, promise:false, returns: [Cursor]});
504
+
336
505
  /**
337
506
  * Sets a field projection for the query.
338
507
  * @method
@@ -341,11 +510,13 @@ Cursor.prototype.maxTimeMs = Cursor.prototype.maxTimeMS;
341
510
  * @return {Cursor}
342
511
  */
343
512
  Cursor.prototype.project = function(value) {
344
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw new MongoError("Cursor is closed");
513
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
345
514
  this.s.cmd.fields = value;
346
515
  return this;
347
516
  }
348
517
 
518
+ define.classMethod('project', {callback: false, promise:false, returns: [Cursor]});
519
+
349
520
  /**
350
521
  * Sets the sort order of the cursor query.
351
522
  * @method
@@ -355,10 +526,29 @@ Cursor.prototype.project = function(value) {
355
526
  * @return {Cursor}
356
527
  */
357
528
  Cursor.prototype.sort = function(keyOrList, direction) {
358
- if(this.s.options.tailable) throw new MongoError("Tailable cursor doesn't support sorting");
359
- if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw new MongoError("Cursor is closed");
529
+ if(this.s.options.tailable) throw MongoError.create({message: "Tailable cursor doesn't support sorting", driver:true});
530
+ if(this.s.state == Cursor.CLOSED || this.s.state == Cursor.OPEN || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
360
531
  var order = keyOrList;
361
532
 
533
+ // We have an array of arrays, we need to preserve the order of the sort
534
+ // so we will us a Map
535
+ if(Array.isArray(order) && Array.isArray(order[0])) {
536
+ order = new Map(order.map(function(x) {
537
+ var value = [x[0], null];
538
+ if(x[1] == 'asc') {
539
+ value[1] = 1;
540
+ } else if(x[1] == 'desc') {
541
+ value[1] = -1;
542
+ } else if(x[1] == 1 || x[1] == -1) {
543
+ value[1] = x[1];
544
+ } else {
545
+ throw new MongoError("Illegal sort clause, must be of the form [['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]");
546
+ }
547
+
548
+ return value;
549
+ }));
550
+ }
551
+
362
552
  if(direction != null) {
363
553
  order = [[keyOrList, direction]];
364
554
  }
@@ -368,6 +558,8 @@ Cursor.prototype.sort = function(keyOrList, direction) {
368
558
  return this;
369
559
  }
370
560
 
561
+ define.classMethod('sort', {callback: false, promise:false, returns: [Cursor]});
562
+
371
563
  /**
372
564
  * Set the batch size for the cursor.
373
565
  * @method
@@ -376,14 +568,16 @@ Cursor.prototype.sort = function(keyOrList, direction) {
376
568
  * @return {Cursor}
377
569
  */
378
570
  Cursor.prototype.batchSize = function(value) {
379
- if(this.s.options.tailable) throw new MongoError("Tailable cursor doesn't support limit");
380
- if(this.s.state == Cursor.CLOSED || this.isDead()) throw new MongoError("Cursor is closed");
381
- if(typeof value != 'number') throw new MongoError("batchSize requires an integer");
571
+ if(this.s.options.tailable) throw MongoError.create({message: "Tailable cursor doesn't support batchSize", driver:true});
572
+ if(this.s.state == Cursor.CLOSED || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
573
+ if(typeof value != 'number') throw MongoError.create({message: "batchSize requires an integer", driver:true});
382
574
  this.s.cmd.batchSize = value;
383
575
  this.setCursorBatchSize(value);
384
576
  return this;
385
577
  }
386
578
 
579
+ define.classMethod('batchSize', {callback: false, promise:false, returns: [Cursor]});
580
+
387
581
  /**
388
582
  * Set the limit for the cursor.
389
583
  * @method
@@ -392,15 +586,17 @@ Cursor.prototype.batchSize = function(value) {
392
586
  * @return {Cursor}
393
587
  */
394
588
  Cursor.prototype.limit = function(value) {
395
- if(this.s.options.tailable) throw new MongoError("Tailable cursor doesn't support limit");
396
- if(this.s.state == Cursor.OPEN || this.s.state == Cursor.CLOSED || this.isDead()) throw new MongoError("Cursor is closed");
397
- if(typeof value != 'number') throw new MongoError("limit requires an integer");
589
+ if(this.s.options.tailable) throw MongoError.create({message: "Tailable cursor doesn't support limit", driver:true});
590
+ if(this.s.state == Cursor.OPEN || this.s.state == Cursor.CLOSED || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
591
+ if(typeof value != 'number') throw MongoError.create({message: "limit requires an integer", driver:true});
398
592
  this.s.cmd.limit = value;
399
593
  // this.cursorLimit = value;
400
594
  this.setCursorLimit(value);
401
595
  return this;
402
596
  }
403
597
 
598
+ define.classMethod('limit', {callback: false, promise:false, returns: [Cursor]});
599
+
404
600
  /**
405
601
  * Set the skip for the cursor.
406
602
  * @method
@@ -409,14 +605,16 @@ Cursor.prototype.limit = function(value) {
409
605
  * @return {Cursor}
410
606
  */
411
607
  Cursor.prototype.skip = function(value) {
412
- if(this.s.options.tailable) throw new MongoError("Tailable cursor doesn't support skip");
413
- if(this.s.state == Cursor.OPEN || this.s.state == Cursor.CLOSED || this.isDead()) throw new MongoError("Cursor is closed");
414
- if(typeof value != 'number') throw new MongoError("skip requires an integer");
608
+ if(this.s.options.tailable) throw MongoError.create({message: "Tailable cursor doesn't support skip", driver:true});
609
+ if(this.s.state == Cursor.OPEN || this.s.state == Cursor.CLOSED || this.isDead()) throw MongoError.create({message: "Cursor is closed", driver:true});
610
+ if(typeof value != 'number') throw MongoError.create({message: "skip requires an integer", driver:true});
415
611
  this.s.cmd.skip = value;
416
612
  this.setCursorSkip(value);
417
613
  return this;
418
614
  }
419
615
 
616
+ define.classMethod('skip', {callback: false, promise:false, returns: [Cursor]});
617
+
420
618
  /**
421
619
  * The callback format for results
422
620
  * @callback Cursor~resultCallback
@@ -424,48 +622,6 @@ Cursor.prototype.skip = function(value) {
424
622
  * @param {(object|null|boolean)} result The result object if the command was executed successfully.
425
623
  */
426
624
 
427
- /**
428
- * Set the new batchSize of the cursor
429
- * @function Cursor.prototype.setBatchSize
430
- * @param {number} value The new batchSize for the cursor
431
- * @return {null}
432
- */
433
-
434
- /**
435
- * Get the batchSize of the cursor
436
- * @function Cursor.prototype.batchSize
437
- * @param {number} value The current batchSize for the cursor
438
- * @return {null}
439
- */
440
-
441
- /**
442
- * Set the new skip value of the cursor
443
- * @function Cursor.prototype.setCursorSkip
444
- * @param {number} value The new skip for the cursor
445
- * @return {null}
446
- */
447
-
448
- /**
449
- * Get the skip value of the cursor
450
- * @function Cursor.prototype.cursorSkip
451
- * @param {number} value The current skip value for the cursor
452
- * @return {null}
453
- */
454
-
455
- /**
456
- * Set the new limit value of the cursor
457
- * @function Cursor.prototype.setCursorLimit
458
- * @param {number} value The new limit for the cursor
459
- * @return {null}
460
- */
461
-
462
- /**
463
- * Get the limit value of the cursor
464
- * @function Cursor.prototype.cursorLimit
465
- * @param {number} value The current limit value for the cursor
466
- * @return {null}
467
- */
468
-
469
625
  /**
470
626
  * Clone the cursor
471
627
  * @function external:CoreCursor#clone
@@ -481,7 +637,7 @@ Cursor.prototype.skip = function(value) {
481
637
  /**
482
638
  * Get the next available document from the cursor, returns null if no more documents are available.
483
639
  * @method
484
- * @param {Cursor~resultCallback} callback The result callback.
640
+ * @param {Cursor~resultCallback} [callback] The result callback.
485
641
  * @throws {MongoError}
486
642
  * @deprecated
487
643
  * @return {Promise} returns Promise if no callback passed
@@ -489,7 +645,7 @@ Cursor.prototype.skip = function(value) {
489
645
  Cursor.prototype.nextObject = Cursor.prototype.next;
490
646
 
491
647
  var nextObject = function(self, callback) {
492
- if(self.s.state == Cursor.CLOSED || self.isDead()) return handleCallback(callback, new MongoError("Cursor is closed"));
648
+ if(self.s.state == Cursor.CLOSED || self.isDead && self.isDead()) return handleCallback(callback, MongoError.create({message: "Cursor is closed", driver:true}));
493
649
  if(self.s.state == Cursor.INIT && self.s.cmd.sort) {
494
650
  try {
495
651
  self.s.cmd.sort = formattedOrderClause(self.s.cmd.sort);
@@ -497,12 +653,17 @@ var nextObject = function(self, callback) {
497
653
  return handleCallback(callback, err);
498
654
  }
499
655
  }
656
+
500
657
  // Get the next object
501
658
  self._next(function(err, doc) {
502
659
  if(err && err.tailable && self.s.currentNumberOfRetries == 0) return callback(err);
503
660
  if(err && err.tailable && self.s.currentNumberOfRetries > 0) {
504
661
  self.s.currentNumberOfRetries = self.s.currentNumberOfRetries - 1;
662
+
505
663
  return setTimeout(function() {
664
+ // Rewind the cursor only when it has not actually read any documents yet
665
+ if(self.cursorState.currentLimit == 0) self.rewind();
666
+ // Read the next document, forcing a re-issue of query if no cursorId exists
506
667
  self.nextObject(callback);
507
668
  }, self.s.tailableRetryInterval);
508
669
  }
@@ -510,9 +671,11 @@ var nextObject = function(self, callback) {
510
671
  self.s.state = Cursor.OPEN;
511
672
  if(err) return handleCallback(callback, err);
512
673
  handleCallback(callback, null, doc);
513
- });
674
+ });
514
675
  }
515
676
 
677
+ define.classMethod('nextObject', {callback: true, promise:true});
678
+
516
679
  // Trampoline emptying the number of retrieved items
517
680
  // without incurring a nextTick operation
518
681
  var loop = function(self, callback) {
@@ -524,16 +687,10 @@ var loop = function(self, callback) {
524
687
  return loop;
525
688
  }
526
689
 
527
- /**
528
- * Get the next available document from the cursor, returns null if no more documents are available.
529
- * @method
530
- * @param {Cursor~resultCallback} callback The result callback.
531
- * @throws {MongoError}
532
- * @deprecated
533
- * @return {Promise} returns Promise if no callback passed
534
- */
535
690
  Cursor.prototype.next = Cursor.prototype.nextObject;
536
691
 
692
+ define.classMethod('next', {callback: true, promise:true});
693
+
537
694
  /**
538
695
  * Iterates over all the documents for this cursor. As with **{cursor.toArray}**,
539
696
  * not all of the elements will be iterated if this cursor had been previouly accessed.
@@ -556,12 +713,14 @@ Cursor.prototype.each = function(callback) {
556
713
  _each(this, callback);
557
714
  };
558
715
 
716
+ define.classMethod('each', {callback: true, promise:false});
717
+
559
718
  // Run the each loop
560
719
  var _each = function(self, callback) {
561
- if(!callback) throw new MongoError('callback is mandatory');
720
+ if(!callback) throw MongoError.create({message: 'callback is mandatory', driver:true});
562
721
  if(self.isNotified()) return;
563
722
  if(self.s.state == Cursor.CLOSED || self.isDead()) {
564
- return handleCallback(callback, new MongoError("Cursor is closed"), null);
723
+ return handleCallback(callback, MongoError.create({message: "Cursor is closed", driver:true}));
565
724
  }
566
725
 
567
726
  if(self.s.state == Cursor.INIT) self.s.state = Cursor.OPEN;
@@ -573,7 +732,7 @@ var _each = function(self, callback) {
573
732
  while(fn = loop(self, callback)) fn(self, callback);
574
733
  _each(self, callback);
575
734
  } else {
576
- self._next(function(err, item) {
735
+ self.next(function(err, item) {
577
736
  if(err) return handleCallback(callback, err);
578
737
  if(item == null) {
579
738
  self.s.state = Cursor.CLOSED;
@@ -619,6 +778,8 @@ Cursor.prototype.forEach = function(iterator, callback) {
619
778
  });
620
779
  }
621
780
 
781
+ define.classMethod('forEach', {callback: true, promise:false});
782
+
622
783
  /**
623
784
  * Set the ReadPreference for the cursor.
624
785
  * @method
@@ -627,7 +788,7 @@ Cursor.prototype.forEach = function(iterator, callback) {
627
788
  * @return {Cursor}
628
789
  */
629
790
  Cursor.prototype.setReadPreference = function(r) {
630
- if(this.s.state != Cursor.INIT) throw new MongoError('cannot change cursor readPreference after cursor has been accessed');
791
+ if(this.s.state != Cursor.INIT) throw MongoError.create({message: 'cannot change cursor readPreference after cursor has been accessed', driver:true});
631
792
  if(r instanceof ReadPreference) {
632
793
  this.s.options.readPreference = new CoreReadPreference(r.mode, r.tags);
633
794
  } else {
@@ -637,6 +798,8 @@ Cursor.prototype.setReadPreference = function(r) {
637
798
  return this;
638
799
  }
639
800
 
801
+ define.classMethod('setReadPreference', {callback: false, promise:false, returns: [Cursor]});
802
+
640
803
  /**
641
804
  * The callback format for results
642
805
  * @callback Cursor~toArrayResultCallback
@@ -650,13 +813,13 @@ Cursor.prototype.setReadPreference = function(r) {
650
813
  * results when this cursor had been previouly accessed. In that case,
651
814
  * cursor.rewind() can be used to reset the cursor.
652
815
  * @method
653
- * @param {Cursor~toArrayResultCallback} callback The result callback.
816
+ * @param {Cursor~toArrayResultCallback} [callback] The result callback.
654
817
  * @throws {MongoError}
655
818
  * @return {Promise} returns Promise if no callback passed
656
819
  */
657
820
  Cursor.prototype.toArray = function(callback) {
658
821
  var self = this;
659
- if(self.s.options.tailable) throw new MongoError('Tailable cursor cannot be converted to array');
822
+ if(self.s.options.tailable) throw MongoError.create({message: 'Tailable cursor cannot be converted to array', driver:true});
660
823
 
661
824
  // Execute using callback
662
825
  if(typeof callback == 'function') return toArray(self, callback);
@@ -665,7 +828,7 @@ Cursor.prototype.toArray = function(callback) {
665
828
  return new this.s.promiseLibrary(function(resolve, reject) {
666
829
  toArray(self, function(err, r) {
667
830
  if(err) return reject(err);
668
- resolve(r);
831
+ resolve(r);
669
832
  });
670
833
  });
671
834
  }
@@ -677,7 +840,6 @@ var toArray = function(self, callback) {
677
840
  self.rewind();
678
841
  self.s.state = Cursor.INIT;
679
842
 
680
-
681
843
  // Fetch all the documents
682
844
  var fetchDocs = function() {
683
845
  self._next(function(err, doc) {
@@ -689,6 +851,7 @@ var toArray = function(self, callback) {
689
851
 
690
852
  // Add doc to items
691
853
  items.push(doc)
854
+
692
855
  // Get all buffered objects
693
856
  if(self.bufferedCount() > 0) {
694
857
  var docs = self.readBufferedDocuments(self.bufferedCount())
@@ -706,9 +869,11 @@ var toArray = function(self, callback) {
706
869
  })
707
870
  }
708
871
 
709
- fetchDocs();
872
+ fetchDocs();
710
873
  }
711
874
 
875
+ define.classMethod('toArray', {callback: true, promise:true});
876
+
712
877
  /**
713
878
  * The callback format for results
714
879
  * @callback Cursor~countResultCallback
@@ -726,12 +891,12 @@ var toArray = function(self, callback) {
726
891
  * @param {number} [options.maxTimeMS=null] Number of miliseconds to wait before aborting the query.
727
892
  * @param {string} [options.hint=null] An index name hint for the query.
728
893
  * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
729
- * @param {Cursor~countResultCallback} callback The result callback.
894
+ * @param {Cursor~countResultCallback} [callback] The result callback.
730
895
  * @return {Promise} returns Promise if no callback passed
731
896
  */
732
897
  Cursor.prototype.count = function(applySkipLimit, opts, callback) {
733
898
  var self = this;
734
- if(self.s.cmd.query == null) throw new MongoError("count can only be used with find command");
899
+ if(self.s.cmd.query == null) throw MongoError.create({message: "count can only be used with find command", driver:true});
735
900
  if(typeof opts == 'function') callback = opts, opts = {};
736
901
  opts = opts || {};
737
902
 
@@ -742,7 +907,7 @@ Cursor.prototype.count = function(applySkipLimit, opts, callback) {
742
907
  return new this.s.promiseLibrary(function(resolve, reject) {
743
908
  count(self, applySkipLimit, opts, function(err, r) {
744
909
  if(err) return reject(err);
745
- resolve(r);
910
+ resolve(r);
746
911
  });
747
912
  });
748
913
  };
@@ -767,8 +932,8 @@ var count = function(self, applySkipLimit, opts, callback) {
767
932
 
768
933
  if(typeof opts.maxTimeMS == 'number') {
769
934
  command.maxTimeMS = opts.maxTimeMS;
770
- } else if(typeof self.s.maxTimeMS == 'number') {
771
- command.maxTimeMS = self.s.maxTimeMS;
935
+ } else if(self.s.cmd && typeof self.s.cmd.maxTimeMS == 'number') {
936
+ command.maxTimeMS = self.s.cmd.maxTimeMS;
772
937
  }
773
938
 
774
939
  // Get a server
@@ -795,14 +960,18 @@ var count = function(self, applySkipLimit, opts, callback) {
795
960
  if(result.documents.length == 1
796
961
  && (result.documents[0].errmsg
797
962
  || result.documents[0].err
798
- || result.documents[0]['$err'])) return callback(MongoError.create(result.documents[0]));
963
+ || result.documents[0]['$err'])) {
964
+ return handleCallback(callback, MongoError.create(result.documents[0]));
965
+ }
799
966
  handleCallback(callback, null, result.documents[0].n);
800
967
  });
801
968
 
802
969
  // Write the initial command out
803
- connection.write(query.toBin());
970
+ connection.write(query.toBin());
804
971
  }
805
972
 
973
+ define.classMethod('count', {callback: true, promise:true});
974
+
806
975
  /**
807
976
  * Close the cursor, sending a KillCursor command and emitting close.
808
977
  * @method
@@ -823,6 +992,8 @@ Cursor.prototype.close = function(callback) {
823
992
  });
824
993
  }
825
994
 
995
+ define.classMethod('close', {callback: true, promise:true});
996
+
826
997
  /**
827
998
  * Map all documents using the provided function
828
999
  * @method
@@ -834,6 +1005,8 @@ Cursor.prototype.map = function(transform) {
834
1005
  return this;
835
1006
  }
836
1007
 
1008
+ define.classMethod('map', {callback: false, promise:false, returns: [Cursor]});
1009
+
837
1010
  /**
838
1011
  * Is the cursor closed
839
1012
  * @method
@@ -843,12 +1016,16 @@ Cursor.prototype.isClosed = function() {
843
1016
  return this.isDead();
844
1017
  }
845
1018
 
1019
+ define.classMethod('isClosed', {callback: false, promise:false, returns: [Boolean]});
1020
+
846
1021
  Cursor.prototype.destroy = function(err) {
847
1022
  this.pause();
848
1023
  this.close();
849
1024
  if(err) this.emit('error', err);
850
1025
  }
851
1026
 
1027
+ define.classMethod('destroy', {callback: false, promise:false});
1028
+
852
1029
  /**
853
1030
  * Return a modified Readable stream including a possible transform method.
854
1031
  * @method
@@ -861,6 +1038,8 @@ Cursor.prototype.stream = function(options) {
861
1038
  return this;
862
1039
  }
863
1040
 
1041
+ define.classMethod('stream', {callback: false, promise:false, returns: [Cursor]});
1042
+
864
1043
  /**
865
1044
  * Execute the explain for the cursor
866
1045
  * @method
@@ -871,6 +1050,11 @@ Cursor.prototype.explain = function(callback) {
871
1050
  var self = this;
872
1051
  this.s.cmd.explain = true;
873
1052
 
1053
+ // Do we have a readConcern
1054
+ if(this.s.cmd.readConcern) {
1055
+ delete this.s.cmd['readConcern'];
1056
+ }
1057
+
874
1058
  // Execute using callback
875
1059
  if(typeof callback == 'function') return this._next(callback);
876
1060
 
@@ -878,11 +1062,13 @@ Cursor.prototype.explain = function(callback) {
878
1062
  return new this.s.promiseLibrary(function(resolve, reject) {
879
1063
  self._next(function(err, r) {
880
1064
  if(err) return reject(err);
881
- resolve(r);
1065
+ resolve(r);
882
1066
  });
883
1067
  });
884
1068
  }
885
1069
 
1070
+ define.classMethod('explain', {callback: true, promise:true});
1071
+
886
1072
  Cursor.prototype._read = function(n) {
887
1073
  var self = this;
888
1074
  if(self.s.state == Cursor.CLOSED || self.isDead()) {
@@ -898,7 +1084,8 @@ Cursor.prototype._read = function(n) {
898
1084
  }
899
1085
 
900
1086
  // Emit end event
901
- return self.emit('end');
1087
+ self.emit('end');
1088
+ return self.emit('finish');
902
1089
  }
903
1090
 
904
1091
  // If we provided a transformation method
@@ -916,6 +1103,17 @@ Cursor.prototype._read = function(n) {
916
1103
  });
917
1104
  }
918
1105
 
1106
+ Object.defineProperty(Cursor.prototype, 'readPreference', {
1107
+ enumerable:true,
1108
+ get: function() {
1109
+ if (!this || !this.s) {
1110
+ return null;
1111
+ }
1112
+
1113
+ return this.s.options.readPreference;
1114
+ }
1115
+ });
1116
+
919
1117
  Object.defineProperty(Cursor.prototype, 'namespace', {
920
1118
  enumerable: true,
921
1119
  get: function() {