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