mongodb 3.3.0-beta1 → 3.3.0-beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,9 @@ const MongoNetworkError = require('./error').MongoNetworkError;
7
7
  const mongoErrorContextSymbol = require('./error').mongoErrorContextSymbol;
8
8
  const f = require('util').format;
9
9
  const collationNotSupported = require('./utils').collationNotSupported;
10
- const wireProtocol = require('./wireprotocol');
10
+ const ReadPreference = require('./topologies/read_preference');
11
+ const isUnifiedTopology = require('./utils').isUnifiedTopology;
12
+
11
13
  const BSON = retrieveBSON();
12
14
  const Long = BSON.Long;
13
15
 
@@ -29,7 +31,7 @@ const Long = BSON.Long;
29
31
  /**
30
32
  * Creates a new Cursor, not to be used directly
31
33
  * @class
32
- * @param {object} bson An instance of the BSON parser
34
+ * @param {object} topology The server topology instance.
33
35
  * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
34
36
  * @param {{object}|Long} cmd The selector (can be a command or a cursorId)
35
37
  * @param {object} [options=null] Optional settings.
@@ -38,14 +40,12 @@ const Long = BSON.Long;
38
40
  * @param {object} [options.transforms=null] Transform methods for the cursor results
39
41
  * @param {function} [options.transforms.query] Transform the value returned from the initial query
40
42
  * @param {function} [options.transforms.doc] Transform each document returned from Cursor.prototype.next
41
- * @param {object} topology The server topology instance.
42
- * @param {object} topologyOptions The server topology options.
43
43
  * @return {Cursor} A cursor instance
44
44
  * @property {number} cursorBatchSize The current cursorBatchSize for the cursor
45
45
  * @property {number} cursorLimit The current cursorLimit for the cursor
46
46
  * @property {number} cursorSkip The current cursorSkip for the cursor
47
47
  */
48
- var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
48
+ var Cursor = function(topology, ns, cmd, options) {
49
49
  options = options || {};
50
50
 
51
51
  // Cursor pool
@@ -57,7 +57,7 @@ var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
57
57
  this.disconnectHandler = options.disconnectHandler;
58
58
 
59
59
  // Set local values
60
- this.bson = bson;
60
+ this.bson = topology.s.bson;
61
61
  this.ns = ns;
62
62
  this.cmd = cmd;
63
63
  this.options = options;
@@ -87,6 +87,7 @@ var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
87
87
  }
88
88
 
89
89
  // Add promoteLong to cursor state
90
+ const topologyOptions = topology.s.options;
90
91
  if (typeof topologyOptions.promoteLongs === 'boolean') {
91
92
  this.cursorState.promoteLongs = topologyOptions.promoteLongs;
92
93
  } else if (typeof options.promoteLongs === 'boolean') {
@@ -183,7 +184,7 @@ var handleCallback = function(callback, err, result) {
183
184
  };
184
185
 
185
186
  // Internal methods
186
- Cursor.prototype._getmore = function(callback) {
187
+ Cursor.prototype._getMore = function(callback) {
187
188
  if (this.logger.isDebug())
188
189
  this.logger.debug(f('schedule getMore call for query [%s]', JSON.stringify(this.query)));
189
190
 
@@ -196,7 +197,7 @@ Cursor.prototype._getmore = function(callback) {
196
197
  batchSize = this.cursorState.limit - this.cursorState.currentLimit;
197
198
  }
198
199
 
199
- wireProtocol.getMore(this.server, this.ns, this.cursorState, batchSize, this.options, callback);
200
+ this.server.getMore(this.ns, this.cursorState, batchSize, this.options, callback);
200
201
  };
201
202
 
202
203
  /**
@@ -305,7 +306,7 @@ Cursor.prototype.kill = function(callback) {
305
306
  return;
306
307
  }
307
308
 
308
- wireProtocol.killCursors(this.server, this.ns, this.cursorState, callback);
309
+ this.server.killCursors(this.ns, this.cursorState, callback);
309
310
  };
310
311
 
311
312
  /**
@@ -406,6 +407,7 @@ var _setCursorNotifiedImpl = function(self, callback) {
406
407
  if (self._endSession) {
407
408
  return self._endSession(undefined, () => callback());
408
409
  }
410
+
409
411
  return callback();
410
412
  };
411
413
 
@@ -426,7 +428,37 @@ var nextFunction = function(self, callback) {
426
428
 
427
429
  // We have just started the cursor
428
430
  if (!self.cursorState.init) {
429
- return initializeCursor(self, callback);
431
+ // Topology is not connected, save the call in the provided store to be
432
+ // Executed at some point when the handler deems it's reconnected
433
+ if (!self.topology.isConnected(self.options)) {
434
+ // Only need this for single server, because repl sets and mongos
435
+ // will always continue trying to reconnect
436
+ if (self.topology._type === 'server' && !self.topology.s.options.reconnect) {
437
+ // Reconnect is disabled, so we'll never reconnect
438
+ return callback(new MongoError('no connection available'));
439
+ }
440
+
441
+ if (self.disconnectHandler != null) {
442
+ if (self.topology.isDestroyed()) {
443
+ // Topology was destroyed, so don't try to wait for it to reconnect
444
+ return callback(new MongoError('Topology was destroyed'));
445
+ }
446
+
447
+ self.disconnectHandler.addObjectAndMethod('cursor', self, 'next', [callback], callback);
448
+ return;
449
+ }
450
+ }
451
+
452
+ self._initializeCursor((err, result) => {
453
+ if (err || result === null) {
454
+ callback(err, result);
455
+ return;
456
+ }
457
+
458
+ nextFunction(self, callback);
459
+ });
460
+
461
+ return;
430
462
  }
431
463
 
432
464
  if (self.cursorState.limit > 0 && self.cursorState.currentLimit >= self.cursorState.limit) {
@@ -449,11 +481,11 @@ var nextFunction = function(self, callback) {
449
481
  );
450
482
 
451
483
  // Check if connection is dead and return if not possible to
452
- // execute a getmore on this connection
484
+ // execute a getMore on this connection
453
485
  if (isConnectionDead(self, callback)) return;
454
486
 
455
487
  // Execute the next get more
456
- self._getmore(function(err, doc, connection) {
488
+ self._getMore(function(err, doc, connection) {
457
489
  if (err) {
458
490
  if (err instanceof MongoError) {
459
491
  err[mongoErrorContextSymbol].isGetMore = true;
@@ -552,31 +584,21 @@ var nextFunction = function(self, callback) {
552
584
  }
553
585
  };
554
586
 
555
- function initializeCursor(cursor, callback) {
556
- // Topology is not connected, save the call in the provided store to be
557
- // Executed at some point when the handler deems it's reconnected
558
- if (!cursor.topology.isConnected(cursor.options)) {
559
- // Only need this for single server, because repl sets and mongos
560
- // will always continue trying to reconnect
561
- if (cursor.topology._type === 'server' && !cursor.topology.s.options.reconnect) {
562
- // Reconnect is disabled, so we'll never reconnect
563
- return callback(new MongoError('no connection available'));
564
- }
587
+ Cursor.prototype._initializeCursor = function(callback) {
588
+ const cursor = this;
565
589
 
566
- if (cursor.disconnectHandler != null) {
567
- if (cursor.topology.isDestroyed()) {
568
- // Topology was destroyed, so don't try to wait for it to reconnect
569
- return callback(new MongoError('Topology was destroyed'));
590
+ // NOTE: this goes away once cursors use `executeOperation`
591
+ if (isUnifiedTopology(cursor.topology) && cursor.topology.shouldCheckForSessionSupport()) {
592
+ cursor.topology.selectServer(ReadPreference.primaryPreferred, err => {
593
+ if (err) {
594
+ callback(err);
595
+ return;
570
596
  }
571
597
 
572
- return cursor.disconnectHandler.addObjectAndMethod(
573
- 'cursor',
574
- cursor,
575
- 'next',
576
- [callback],
577
- callback
578
- );
579
- }
598
+ cursor.next(callback);
599
+ });
600
+
601
+ return;
580
602
  }
581
603
 
582
604
  // Very explicitly choose what is passed to selectServer
@@ -604,7 +626,7 @@ function initializeCursor(cursor, callback) {
604
626
  return callback(new MongoError(`server ${cursor.server.name} does not support collation`));
605
627
  }
606
628
 
607
- function done() {
629
+ function done(err, result) {
608
630
  if (
609
631
  cursor.cursorState.cursorId &&
610
632
  cursor.cursorState.cursorId.isZero() &&
@@ -623,7 +645,7 @@ function initializeCursor(cursor, callback) {
623
645
  return setCursorNotified(cursor, callback);
624
646
  }
625
647
 
626
- nextFunction(cursor, callback);
648
+ callback(err, result);
627
649
  }
628
650
 
629
651
  // NOTE: this is a special internal method for cloning a cursor, consider removing
@@ -632,11 +654,13 @@ function initializeCursor(cursor, callback) {
632
654
  }
633
655
 
634
656
  const queryCallback = (err, r) => {
635
- if (err) return callback(err);
657
+ if (err) {
658
+ return done(err);
659
+ }
636
660
 
637
661
  const result = r.message;
638
662
  if (result.queryFailure) {
639
- return callback(new MongoError(result.documents[0]), null);
663
+ return done(new MongoError(result.documents[0]), null);
640
664
  }
641
665
 
642
666
  // Check if we have a command cursor
@@ -651,7 +675,7 @@ function initializeCursor(cursor, callback) {
651
675
  ) {
652
676
  // We have an error document, return the error
653
677
  if (result.documents[0]['$err'] || result.documents[0]['errmsg']) {
654
- return callback(new MongoError(result.documents[0]), null);
678
+ return done(new MongoError(result.documents[0]), null);
655
679
  }
656
680
 
657
681
  // We have a cursor document
@@ -665,19 +689,20 @@ function initializeCursor(cursor, callback) {
665
689
  cursor.cursorState.cursorId = typeof id === 'number' ? Long.fromNumber(id) : id;
666
690
  cursor.cursorState.lastCursorId = cursor.cursorState.cursorId;
667
691
  cursor.cursorState.operationTime = result.documents[0].operationTime;
692
+
668
693
  // If we have a firstBatch set it
669
694
  if (Array.isArray(result.documents[0].cursor.firstBatch)) {
670
695
  cursor.cursorState.documents = result.documents[0].cursor.firstBatch; //.reverse();
671
696
  }
672
697
 
673
698
  // Return after processing command cursor
674
- return done(result);
699
+ return done(null, result);
675
700
  }
676
701
 
677
702
  if (Array.isArray(result.documents[0].result)) {
678
703
  cursor.cursorState.documents = result.documents[0].result;
679
704
  cursor.cursorState.cursorId = Long.ZERO;
680
- return done(result);
705
+ return done(null, result);
681
706
  }
682
707
  }
683
708
 
@@ -695,8 +720,7 @@ function initializeCursor(cursor, callback) {
695
720
  cursor.cursorState.documents = cursor.cursorState.transforms.query(result);
696
721
  }
697
722
 
698
- // Return callback
699
- done(result);
723
+ done(null, result);
700
724
  };
701
725
 
702
726
  if (cursor.logger.isDebug()) {
@@ -708,27 +732,13 @@ function initializeCursor(cursor, callback) {
708
732
  }
709
733
 
710
734
  if (cursor.cmd.find != null) {
711
- wireProtocol.query(
712
- cursor.server,
713
- cursor.ns,
714
- cursor.cmd,
715
- cursor.cursorState,
716
- cursor.options,
717
- queryCallback
718
- );
719
-
735
+ server.query(cursor.ns, cursor.cmd, cursor.cursorState, cursor.options, queryCallback);
720
736
  return;
721
737
  }
722
738
 
723
- cursor.query = wireProtocol.command(
724
- cursor.server,
725
- cursor.ns,
726
- cursor.cmd,
727
- cursor.options,
728
- queryCallback
729
- );
739
+ server.command(cursor.ns, cursor.cmd, cursor.options, queryCallback);
730
740
  });
731
- }
741
+ };
732
742
 
733
743
  /**
734
744
  * Retrieve the next document from the cursor
package/lib/core/error.js CHANGED
@@ -152,6 +152,28 @@ function isRetryableError(error) {
152
152
  );
153
153
  }
154
154
 
155
+ const SDAM_UNRECOVERABLE_ERROR_CODES = new Set([
156
+ 91, // ShutdownInProgress
157
+ 189, // PrimarySteppedDown
158
+ 10107, // NotMaster
159
+ 11600, // InterruptedAtShutdown
160
+ 11602, // InterruptedDueToReplStateChange
161
+ 13435, // NotMasterNoSlaveOk
162
+ 13436 // NotMasterOrSecondary
163
+ ]);
164
+ /**
165
+ * Determines whether an error is a "node is recovering" error or a "not master" error for SDAM retryability.
166
+ * See https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#not-master-and-node-is-recovering
167
+ * @param {MongoError|Error} error
168
+ */
169
+ function isSDAMUnrecoverableError(error) {
170
+ return (
171
+ SDAM_UNRECOVERABLE_ERROR_CODES.has(error.code) ||
172
+ (error.message &&
173
+ (error.message.match(/not master/) || error.message.match(/node is recovering/)))
174
+ );
175
+ }
176
+
155
177
  module.exports = {
156
178
  MongoError,
157
179
  MongoNetworkError,
@@ -159,5 +181,6 @@ module.exports = {
159
181
  MongoTimeoutError,
160
182
  MongoWriteConcernError,
161
183
  mongoErrorContextSymbol,
162
- isRetryableError
184
+ isRetryableError,
185
+ isSDAMUnrecoverableError
163
186
  };
@@ -14,6 +14,7 @@ const MongoParseError = require('../error').MongoParseError;
14
14
  const MongoNetworkError = require('../error').MongoNetworkError;
15
15
  const collationNotSupported = require('../utils').collationNotSupported;
16
16
  const debugOptions = require('../connection/utils').debugOptions;
17
+ const isSDAMUnrecoverableError = require('../error').isSDAMUnrecoverableError;
17
18
 
18
19
  // Used for filtering out fields for logging
19
20
  const DEBUG_FIELDS = [
@@ -95,6 +96,13 @@ class Server extends EventEmitter {
95
96
  return this.s.description.address;
96
97
  }
97
98
 
99
+ get autoEncrypter() {
100
+ if (this.s.options && this.s.options.autoEncrypter) {
101
+ return this.s.options.autoEncrypter;
102
+ }
103
+ return null;
104
+ }
105
+
98
106
  /**
99
107
  * Initiate server connect
100
108
  */
@@ -154,13 +162,16 @@ class Server extends EventEmitter {
154
162
  if (typeof options === 'function') (callback = options), (options = {});
155
163
  options = Object.assign({}, { force: false }, options);
156
164
 
157
- if (!this.s.pool) {
165
+ const done = err => {
166
+ this.emit('closed');
158
167
  this.s.state = STATE_DISCONNECTED;
159
168
  if (typeof callback === 'function') {
160
- callback(null, null);
169
+ callback(err, null);
161
170
  }
171
+ };
162
172
 
163
- return;
173
+ if (!this.s.pool) {
174
+ return done();
164
175
  }
165
176
 
166
177
  ['close', 'error', 'timeout', 'parseError', 'connect'].forEach(event => {
@@ -171,10 +182,7 @@ class Server extends EventEmitter {
171
182
  clearTimeout(this.s.monitorId);
172
183
  }
173
184
 
174
- this.s.pool.destroy(options.force, err => {
175
- this.s.state = STATE_DISCONNECTED;
176
- callback(err);
177
- });
185
+ this.s.pool.destroy(options.force, done);
178
186
  }
179
187
 
180
188
  /**
@@ -231,7 +239,68 @@ class Server extends EventEmitter {
231
239
  return;
232
240
  }
233
241
 
234
- wireProtocol.command(this, ns, cmd, options, callback);
242
+ wireProtocol.command(this, ns, cmd, options, (err, result) => {
243
+ if (err && isSDAMUnrecoverableError(err)) {
244
+ this.emit('error', err);
245
+ }
246
+
247
+ callback(err, result);
248
+ });
249
+ }
250
+
251
+ /**
252
+ * Execute a query against the server
253
+ *
254
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
255
+ * @param {object} cmd The command document for the query
256
+ * @param {object} options Optional settings
257
+ * @param {function} callback
258
+ */
259
+ query(ns, cmd, cursorState, options, callback) {
260
+ wireProtocol.query(this, ns, cmd, cursorState, options, (err, result) => {
261
+ if (err && isSDAMUnrecoverableError(err)) {
262
+ this.emit('error', err);
263
+ }
264
+
265
+ callback(err, result);
266
+ });
267
+ }
268
+
269
+ /**
270
+ * Execute a `getMore` against the server
271
+ *
272
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
273
+ * @param {object} cursorState State data associated with the cursor calling this method
274
+ * @param {object} options Optional settings
275
+ * @param {function} callback
276
+ */
277
+ getMore(ns, cursorState, batchSize, options, callback) {
278
+ wireProtocol.getMore(this, ns, cursorState, batchSize, options, (err, result) => {
279
+ if (err && isSDAMUnrecoverableError(err)) {
280
+ this.emit('error', err);
281
+ }
282
+
283
+ callback(err, result);
284
+ });
285
+ }
286
+
287
+ /**
288
+ * Execute a `killCursors` command against the server
289
+ *
290
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
291
+ * @param {object} cursorState State data associated with the cursor calling this method
292
+ * @param {function} callback
293
+ */
294
+ killCursors(ns, cursorState, callback) {
295
+ wireProtocol.killCursors(this, ns, cursorState, (err, result) => {
296
+ if (err && isSDAMUnrecoverableError(err)) {
297
+ this.emit('error', err);
298
+ }
299
+
300
+ if (typeof callback === 'function') {
301
+ callback(err, result);
302
+ }
303
+ });
235
304
  }
236
305
 
237
306
  /**
@@ -336,7 +405,13 @@ function executeWriteOperation(args, options, callback) {
336
405
  return;
337
406
  }
338
407
 
339
- return wireProtocol[op](server, ns, ops, options, callback);
408
+ return wireProtocol[op](server, ns, ops, options, (err, result) => {
409
+ if (err && isSDAMUnrecoverableError(err)) {
410
+ server.emit('error', err);
411
+ }
412
+
413
+ callback(err, result);
414
+ });
340
415
  }
341
416
 
342
417
  function connectEventHandler(server) {
@@ -19,6 +19,13 @@ const WRITABLE_SERVER_TYPES = new Set([
19
19
  ServerType.Mongos
20
20
  ]);
21
21
 
22
+ const DATA_BEARING_SERVER_TYPES = new Set([
23
+ ServerType.RSPrimary,
24
+ ServerType.RSSecondary,
25
+ ServerType.Mongos,
26
+ ServerType.Standalone
27
+ ]);
28
+
22
29
  const ISMASTER_FIELDS = [
23
30
  'minWireVersion',
24
31
  'maxWireVersion',
@@ -99,6 +106,13 @@ class ServerDescription {
99
106
  return this.type === ServerType.RSSecondary || this.isWritable;
100
107
  }
101
108
 
109
+ /**
110
+ * @return {Boolean} Is this server data bearing
111
+ */
112
+ get isDataBearing() {
113
+ return DATA_BEARING_SERVER_TYPES.has(this.type);
114
+ }
115
+
102
116
  /**
103
117
  * @return {Boolean} Is this server available for writes
104
118
  */
@@ -19,6 +19,7 @@ const BSON = require('../connection/utils').retrieveBSON();
19
19
  const createCompressionInfo = require('../topologies/shared').createCompressionInfo;
20
20
  const isRetryableError = require('../error').isRetryableError;
21
21
  const MongoParseError = require('../error').MongoParseError;
22
+ const isSDAMUnrecoverableError = require('../error').isSDAMUnrecoverableError;
22
23
  const ClientSession = require('../sessions').ClientSession;
23
24
  const createClientInfo = require('../topologies/shared').createClientInfo;
24
25
  const MongoError = require('../error').MongoError;
@@ -390,6 +391,17 @@ class Topology extends EventEmitter {
390
391
  }
391
392
 
392
393
  // Sessions related methods
394
+
395
+ /**
396
+ * @return Whether the topology should initiate selection to determine session support
397
+ */
398
+ shouldCheckForSessionSupport() {
399
+ return (
400
+ (this.description.type === TopologyType.Single && !this.description.hasKnownServers) ||
401
+ !this.description.hasDataBearingServers
402
+ );
403
+ }
404
+
393
405
  /**
394
406
  * @return Whether sessions are supported on the current topology
395
407
  */
@@ -632,7 +644,7 @@ class Topology extends EventEmitter {
632
644
  const CursorClass = options.cursorFactory || this.s.Cursor;
633
645
  translateReadPreference(options);
634
646
 
635
- return new CursorClass(this.s.bson, ns, cmd, options, topology, this.s.options);
647
+ return new CursorClass(topology, ns, cmd, options);
636
648
  }
637
649
 
638
650
  get clientInfo() {
@@ -919,7 +931,7 @@ function serverErrorEventHandler(server, topology) {
919
931
  new monitoring.ServerClosedEvent(topology.s.id, server.description.address)
920
932
  );
921
933
 
922
- if (err instanceof MongoParseError) {
934
+ if (err instanceof MongoParseError || isSDAMUnrecoverableError(err)) {
923
935
  resetServerState(server, err, { clearPool: true });
924
936
  return;
925
937
  }
@@ -997,10 +1009,11 @@ function resetServerState(server, error, options) {
997
1009
  'descriptionReceived',
998
1010
  new ServerDescription(server.description.address, null, { error })
999
1011
  );
1012
+ server.monitor();
1000
1013
  }
1001
1014
 
1002
- if (options.clearPool && server.pool) {
1003
- server.pool.reset(() => resetState());
1015
+ if (options.clearPool && server.s.pool) {
1016
+ server.s.pool.reset(() => resetState());
1004
1017
  return;
1005
1018
  }
1006
1019
 
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
  const ServerType = require('./server_description').ServerType;
3
3
  const ServerDescription = require('./server_description').ServerDescription;
4
- const ReadPreference = require('../topologies/read_preference');
5
4
  const WIRE_CONSTANTS = require('../wireprotocol/constants');
6
5
 
7
6
  // contstants related to compatability checks
@@ -258,24 +257,17 @@ class TopologyDescription {
258
257
  }
259
258
 
260
259
  /**
261
- * Determines if the topology has a readable server available. See the table in the
262
- * following section for behaviour rules.
263
- *
264
- * @param {ReadPreference} [readPreference] An optional read preference for determining if a readable server is present
265
- * @return {Boolean} Whether there is a readable server in this topology
260
+ * Determines if the topology description has any known servers
266
261
  */
267
- hasReadableServer(/* readPreference */) {
268
- // To be implemented when server selection is implemented
262
+ get hasKnownServers() {
263
+ return Array.from(this.servers.values()).some(sd => sd.type !== ServerDescription.Unknown);
269
264
  }
270
265
 
271
266
  /**
272
- * Determines if the topology has a writable server available. See the table in the
273
- * following section for behaviour rules.
274
- *
275
- * @return {Boolean} Whether there is a writable server in this topology
267
+ * Determines if this topology description has a data-bearing server available.
276
268
  */
277
- hasWritableServer() {
278
- return this.hasReadableServer(ReadPreference.primary);
269
+ get hasDataBearingServers() {
270
+ return Array.from(this.servers.values()).some(sd => sd.isDataBearing);
279
271
  }
280
272
 
281
273
  /**
@@ -1110,7 +1110,7 @@ Mongos.prototype.cursor = function(ns, cmd, options) {
1110
1110
  var FinalCursor = options.cursorFactory || this.s.Cursor;
1111
1111
 
1112
1112
  // Return the cursor
1113
- return new FinalCursor(this.s.bson, ns, cmd, options, topology, this.s.options);
1113
+ return new FinalCursor(topology, ns, cmd, options);
1114
1114
  };
1115
1115
 
1116
1116
  /**
@@ -1364,7 +1364,7 @@ ReplSet.prototype.cursor = function(ns, cmd, options) {
1364
1364
  var FinalCursor = options.cursorFactory || this.s.Cursor;
1365
1365
 
1366
1366
  // Return the cursor
1367
- return new FinalCursor(this.s.bson, ns, cmd, options, topology, this.s.options);
1367
+ return new FinalCursor(topology, ns, cmd, options);
1368
1368
  };
1369
1369
 
1370
1370
  /**
@@ -628,6 +628,41 @@ Server.prototype.command = function(ns, cmd, options, callback) {
628
628
  wireProtocol.command(self, ns, cmd, options, callback);
629
629
  };
630
630
 
631
+ /**
632
+ * Execute a query against the server
633
+ *
634
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
635
+ * @param {object} cmd The command document for the query
636
+ * @param {object} options Optional settings
637
+ * @param {function} callback
638
+ */
639
+ Server.prototype.query = function(ns, cmd, cursorState, options, callback) {
640
+ wireProtocol.query(this, ns, cmd, cursorState, options, callback);
641
+ };
642
+
643
+ /**
644
+ * Execute a `getMore` against the server
645
+ *
646
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
647
+ * @param {object} cursorState State data associated with the cursor calling this method
648
+ * @param {object} options Optional settings
649
+ * @param {function} callback
650
+ */
651
+ Server.prototype.getMore = function(ns, cursorState, batchSize, options, callback) {
652
+ wireProtocol.getMore(this, ns, cursorState, batchSize, options, callback);
653
+ };
654
+
655
+ /**
656
+ * Execute a `killCursors` command against the server
657
+ *
658
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
659
+ * @param {object} cursorState State data associated with the cursor calling this method
660
+ * @param {function} callback
661
+ */
662
+ Server.prototype.killCursors = function(ns, cursorState, callback) {
663
+ wireProtocol.killCursors(this, ns, cursorState, callback);
664
+ };
665
+
631
666
  /**
632
667
  * Insert one or more documents
633
668
  * @method
@@ -752,7 +787,7 @@ Server.prototype.cursor = function(ns, cmd, options) {
752
787
  var FinalCursor = options.cursorFactory || this.s.Cursor;
753
788
 
754
789
  // Return the cursor
755
- return new FinalCursor(this.s.bson, ns, cmd, options, topology, this.s.options);
790
+ return new FinalCursor(topology, ns, cmd, options);
756
791
  };
757
792
 
758
793
  /**