Skip to content

Commit 0f88582

Browse files
mbroadstdaprahamian
authored andcommitted
refactor(cursor): modernize and deduplicate cursor classes
Since we merged core into native, there is no longer great need to maintain a fully functional "core" cursor type, it has become a private implementation detail. This changeset renames that class to `CoreCursor`, converts it to an ES6 class, and then builds the three public cursors on top of it. Here is a summary of changes: - All cursor classes are now ES6 classes - The `Cursor` from "core" has been renamed `CoreCursor` - a `CursorState` has been exported from the `CoreCursor` module so that all cursors may share a common state definition - `CoreCursor` extends `Readable` stream. Each of its subclasses inherited this, so this is where it naturally fits in the hierarchy. This wasn't possible before because the cursor exported by `mongodb-core` did not want to concern itself with higher concepts like streams. We are no longer shackled by this idea. - `CoreCursor` only exposes a public `_next` method because of the way the other cursors have traditionally subclassed it. - We still have `cursorState` and `s`, but they are only in _two_ locations now (`CoreCursor` has a `cursorState`, and `Cursor` has a `s). Eventually I think even these should be merged, but it did not seem important to distinguish this now. - All cursors had a _lot_ of duplicated properties. As much as possible these have been deduplicated, and include the following stored properties: bson, options, topology, topologyOptions, streamOptions, namespace, ns, cmd, promiseLibrary - generally sessions are stored and referred to by the core cursor's `cursorState`. We could move all functionality there eventually.
1 parent feec4ba commit 0f88582

31 files changed

+1719
-1862
lines changed

lib/aggregation_cursor.js

Lines changed: 177 additions & 224 deletions
Large diffs are not rendered by default.

lib/command_cursor.js

Lines changed: 85 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
'use strict';
22

3-
const inherits = require('util').inherits;
43
const ReadPreference = require('./core').ReadPreference;
54
const MongoError = require('./core').MongoError;
6-
const Readable = require('stream').Readable;
7-
const CoreCursor = require('./cursor');
8-
const SUPPORTS = require('./utils').SUPPORTS;
9-
const MongoDBNamespace = require('./utils').MongoDBNamespace;
5+
const Cursor = require('./cursor');
6+
const CursorState = require('./core/cursor').CursorState;
107

118
/**
129
* @fileOverview The **CommandCursor** class is an internal class that embodies a
@@ -55,62 +52,92 @@ const MongoDBNamespace = require('./utils').MongoDBNamespace;
5552
* @fires CommandCursor#readable
5653
* @return {CommandCursor} an CommandCursor instance.
5754
*/
58-
var CommandCursor = function(topology, ns, cmd, options) {
59-
CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
60-
var state = CommandCursor.INIT;
61-
var streamOptions = {};
62-
const bson = topology.s.bson;
63-
const topologyOptions = topology.s.options;
64-
65-
if (typeof ns !== 'string') {
66-
this.operation = ns;
67-
ns = this.operation.ns.toString();
68-
options = this.operation.options;
69-
cmd = {};
55+
class CommandCursor extends Cursor {
56+
constructor(topology, ns, cmd, options) {
57+
super(topology, ns, cmd, options);
7058
}
7159

72-
// MaxTimeMS
73-
var maxTimeMS = null;
74-
75-
// Get the promiseLibrary
76-
var promiseLibrary = options.promiseLibrary || Promise;
77-
78-
// Set up
79-
Readable.call(this, { objectMode: true });
80-
81-
// Internal state
82-
this.s = {
83-
// MaxTimeMS
84-
maxTimeMS: maxTimeMS,
85-
// State
86-
state: state,
87-
// Stream options
88-
streamOptions: streamOptions,
89-
// BSON
90-
bson: bson,
91-
// Namespace
92-
namespace: MongoDBNamespace.fromString(ns),
93-
// Command
94-
cmd: cmd,
95-
// Options
96-
options: options,
97-
// Topology
98-
topology: topology,
99-
// Topology Options
100-
topologyOptions: topologyOptions,
101-
// Promise library
102-
promiseLibrary: promiseLibrary,
103-
// Optional ClientSession
104-
session: options.session
105-
};
106-
};
107-
108-
Object.defineProperty(CommandCursor.prototype, 'namespace', {
109-
enumerable: true,
110-
get: function() {
111-
return this.s.namespace.toString();
60+
/**
61+
* Set the ReadPreference for the cursor.
62+
* @method
63+
* @param {(string|ReadPreference)} readPreference The new read preference for the cursor.
64+
* @throws {MongoError}
65+
* @return {Cursor}
66+
*/
67+
setReadPreference(readPreference) {
68+
if (this.s.state === CursorState.CLOSED || this.isDead()) {
69+
throw MongoError.create({ message: 'Cursor is closed', driver: true });
70+
}
71+
72+
if (this.s.state !== CursorState.INIT) {
73+
throw MongoError.create({
74+
message: 'cannot change cursor readPreference after cursor has been accessed',
75+
driver: true
76+
});
77+
}
78+
79+
if (readPreference instanceof ReadPreference) {
80+
this.options.readPreference = readPreference;
81+
} else if (typeof readPreference === 'string') {
82+
this.options.readPreference = new ReadPreference(readPreference);
83+
} else {
84+
throw new TypeError('Invalid read preference: ' + readPreference);
85+
}
86+
87+
return this;
11288
}
113-
});
89+
90+
/**
91+
* Set the batch size for the cursor.
92+
* @method
93+
* @param {number} value The batchSize for the cursor.
94+
* @throws {MongoError}
95+
* @return {CommandCursor}
96+
*/
97+
batchSize(value) {
98+
if (this.s.state === CursorState.CLOSED || this.isDead()) {
99+
throw MongoError.create({ message: 'Cursor is closed', driver: true });
100+
}
101+
102+
if (typeof value !== 'number') {
103+
throw MongoError.create({ message: 'batchSize requires an integer', driver: true });
104+
}
105+
106+
if (this.cmd.cursor) {
107+
this.cmd.cursor.batchSize = value;
108+
}
109+
110+
this.setCursorBatchSize(value);
111+
return this;
112+
}
113+
114+
/**
115+
* Add a maxTimeMS stage to the aggregation pipeline
116+
* @method
117+
* @param {number} value The state maxTimeMS value.
118+
* @return {CommandCursor}
119+
*/
120+
maxTimeMS(value) {
121+
if (this.topology.lastIsMaster().minWireVersion > 2) {
122+
this.cmd.maxTimeMS = value;
123+
}
124+
125+
return this;
126+
}
127+
128+
/**
129+
* Return the cursor logger
130+
* @method
131+
* @return {Logger} return the cursor logger
132+
* @ignore
133+
*/
134+
getLogger() {
135+
return this.logger;
136+
}
137+
}
138+
139+
// aliases
140+
CommandCursor.prototype.get = CommandCursor.prototype.toArray;
114141

115142
/**
116143
* CommandCursor stream data event, fired for each document in the cursor.
@@ -140,124 +167,6 @@ Object.defineProperty(CommandCursor.prototype, 'namespace', {
140167
* @type {null}
141168
*/
142169

143-
// Inherit from Readable
144-
inherits(CommandCursor, Readable);
145-
146-
// Set the methods to inherit from prototype
147-
var methodsToInherit = [
148-
'_next',
149-
'next',
150-
'hasNext',
151-
'each',
152-
'forEach',
153-
'toArray',
154-
'rewind',
155-
'bufferedCount',
156-
'readBufferedDocuments',
157-
'close',
158-
'isClosed',
159-
'kill',
160-
'setCursorBatchSize',
161-
'_find',
162-
'_initializeCursor',
163-
'_getMore',
164-
'_killcursor',
165-
'isDead',
166-
'explain',
167-
'isNotified',
168-
'isKilled',
169-
'_endSession'
170-
];
171-
172-
// Only inherit the types we need
173-
for (var i = 0; i < methodsToInherit.length; i++) {
174-
CommandCursor.prototype[methodsToInherit[i]] = CoreCursor.prototype[methodsToInherit[i]];
175-
}
176-
177-
if (SUPPORTS.ASYNC_ITERATOR) {
178-
CommandCursor.prototype[Symbol.asyncIterator] = require('./async/async_iterator').asyncIterator;
179-
}
180-
181-
/**
182-
* Set the ReadPreference for the cursor.
183-
* @method
184-
* @param {(string|ReadPreference)} readPreference The new read preference for the cursor.
185-
* @throws {MongoError}
186-
* @return {Cursor}
187-
*/
188-
CommandCursor.prototype.setReadPreference = function(readPreference) {
189-
if (this.s.state === CommandCursor.CLOSED || this.isDead()) {
190-
throw MongoError.create({ message: 'Cursor is closed', driver: true });
191-
}
192-
193-
if (this.s.state !== CommandCursor.INIT) {
194-
throw MongoError.create({
195-
message: 'cannot change cursor readPreference after cursor has been accessed',
196-
driver: true
197-
});
198-
}
199-
200-
if (readPreference instanceof ReadPreference) {
201-
this.s.options.readPreference = readPreference;
202-
} else if (typeof readPreference === 'string') {
203-
this.s.options.readPreference = new ReadPreference(readPreference);
204-
} else {
205-
throw new TypeError('Invalid read preference: ' + readPreference);
206-
}
207-
208-
return this;
209-
};
210-
211-
/**
212-
* Set the batch size for the cursor.
213-
* @method
214-
* @param {number} value The batchSize for the cursor.
215-
* @throws {MongoError}
216-
* @return {CommandCursor}
217-
*/
218-
CommandCursor.prototype.batchSize = function(value) {
219-
if (this.s.state === CommandCursor.CLOSED || this.isDead()) {
220-
throw MongoError.create({ message: 'Cursor is closed', driver: true });
221-
}
222-
223-
if (typeof value !== 'number') {
224-
throw MongoError.create({ message: 'batchSize requires an integer', driver: true });
225-
}
226-
227-
if (this.cmd.cursor) {
228-
this.cmd.cursor.batchSize = value;
229-
}
230-
231-
this.setCursorBatchSize(value);
232-
return this;
233-
};
234-
235-
/**
236-
* Add a maxTimeMS stage to the aggregation pipeline
237-
* @method
238-
* @param {number} value The state maxTimeMS value.
239-
* @return {CommandCursor}
240-
*/
241-
CommandCursor.prototype.maxTimeMS = function(value) {
242-
if (this.s.topology.lastIsMaster().minWireVersion > 2) {
243-
this.cmd.maxTimeMS = value;
244-
}
245-
246-
return this;
247-
};
248-
249-
/**
250-
* Return the cursor logger
251-
* @method
252-
* @return {Logger} return the cursor logger
253-
* @ignore
254-
*/
255-
CommandCursor.prototype.getLogger = function() {
256-
return this.logger;
257-
};
258-
259-
CommandCursor.prototype.get = CommandCursor.prototype.toArray;
260-
261170
/**
262171
* Get the next available document from the cursor, returns null if no more documents are available.
263172
* @function CommandCursor.prototype.next
@@ -357,8 +266,4 @@ CommandCursor.prototype.get = CommandCursor.prototype.toArray;
357266
* @return {null}
358267
*/
359268

360-
CommandCursor.INIT = 0;
361-
CommandCursor.OPEN = 1;
362-
CommandCursor.CLOSED = 2;
363-
364269
module.exports = CommandCursor;

0 commit comments

Comments
 (0)