mongodb 6.7.0 → 6.8.0
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/README.md +20 -1
- package/lib/bson.js.map +1 -1
- package/lib/client-side-encryption/auto_encrypter.js +8 -61
- package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
- package/lib/client-side-encryption/client_encryption.js +5 -5
- package/lib/client-side-encryption/client_encryption.js.map +1 -1
- package/lib/client-side-encryption/providers/index.js.map +1 -1
- package/lib/client-side-encryption/state_machine.js +15 -11
- package/lib/client-side-encryption/state_machine.js.map +1 -1
- package/lib/cmap/connection.js +22 -20
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/wire_protocol/on_demand/document.js +8 -5
- package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -1
- package/lib/cmap/wire_protocol/responses.js +116 -40
- package/lib/cmap/wire_protocol/responses.js.map +1 -1
- package/lib/collection.js +13 -2
- package/lib/collection.js.map +1 -1
- package/lib/constants.js +9 -1
- package/lib/constants.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +231 -285
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +11 -19
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/change_stream_cursor.js +12 -14
- package/lib/cursor/change_stream_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +64 -84
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/cursor/list_collections_cursor.js +0 -1
- package/lib/cursor/list_collections_cursor.js.map +1 -1
- package/lib/cursor/list_indexes_cursor.js +0 -1
- package/lib/cursor/list_indexes_cursor.js.map +1 -1
- package/lib/cursor/run_command_cursor.js +4 -6
- package/lib/cursor/run_command_cursor.js.map +1 -1
- package/lib/error.js +10 -23
- package/lib/error.js.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/operations/aggregate.js +2 -2
- package/lib/operations/aggregate.js.map +1 -1
- package/lib/operations/bulk_write.js +1 -2
- package/lib/operations/bulk_write.js.map +1 -1
- package/lib/operations/command.js +2 -3
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js +2 -1
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/get_more.js +1 -1
- package/lib/operations/get_more.js.map +1 -1
- package/lib/operations/indexes.js +2 -1
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/list_collections.js +2 -1
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/run_command.js +1 -1
- package/lib/operations/run_command.js.map +1 -1
- package/lib/operations/update.js +2 -1
- package/lib/operations/update.js.map +1 -1
- package/lib/sdam/server.js +7 -2
- package/lib/sdam/server.js.map +1 -1
- package/lib/sessions.js +1 -1
- package/lib/sessions.js.map +1 -1
- package/lib/utils.js +45 -1
- package/lib/utils.js.map +1 -1
- package/lib/write_concern.js +17 -1
- package/lib/write_concern.js.map +1 -1
- package/mongodb.d.ts +187 -150
- package/package.json +2 -2
- package/src/bson.ts +1 -0
- package/src/client-side-encryption/auto_encrypter.ts +9 -70
- package/src/client-side-encryption/client_encryption.ts +33 -19
- package/src/client-side-encryption/providers/index.ts +118 -92
- package/src/client-side-encryption/state_machine.ts +22 -18
- package/src/cmap/connection.ts +46 -50
- package/src/cmap/wire_protocol/on_demand/document.ts +13 -6
- package/src/cmap/wire_protocol/responses.ts +140 -45
- package/src/collection.ts +25 -5
- package/src/constants.ts +9 -0
- package/src/cursor/abstract_cursor.ts +280 -373
- package/src/cursor/aggregation_cursor.ts +24 -33
- package/src/cursor/change_stream_cursor.ts +31 -48
- package/src/cursor/find_cursor.ts +77 -92
- package/src/cursor/list_collections_cursor.ts +3 -4
- package/src/cursor/list_indexes_cursor.ts +3 -4
- package/src/cursor/run_command_cursor.ts +13 -19
- package/src/error.ts +20 -30
- package/src/index.ts +19 -10
- package/src/operations/aggregate.ts +12 -5
- package/src/operations/bulk_write.ts +1 -2
- package/src/operations/command.ts +17 -3
- package/src/operations/delete.ts +2 -2
- package/src/operations/execute_operation.ts +0 -13
- package/src/operations/find.ts +7 -3
- package/src/operations/find_and_modify.ts +1 -1
- package/src/operations/get_more.ts +6 -10
- package/src/operations/indexes.ts +7 -3
- package/src/operations/list_collections.ts +8 -3
- package/src/operations/run_command.ts +16 -6
- package/src/operations/update.ts +2 -1
- package/src/sdam/server.ts +7 -2
- package/src/sessions.ts +1 -1
- package/src/utils.ts +52 -2
- package/src/write_concern.ts +18 -0
- package/lib/operations/count_documents.js +0 -31
- package/lib/operations/count_documents.js.map +0 -1
- package/src/operations/count_documents.ts +0 -46
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.AbstractCursor = exports.CURSOR_FLAGS = void 0;
|
|
4
4
|
const stream_1 = require("stream");
|
|
5
5
|
const bson_1 = require("../bson");
|
|
6
|
-
const responses_1 = require("../cmap/wire_protocol/responses");
|
|
7
6
|
const error_1 = require("../error");
|
|
8
7
|
const mongo_types_1 = require("../mongo_types");
|
|
9
8
|
const execute_operation_1 = require("../operations/execute_operation");
|
|
@@ -13,30 +12,6 @@ const read_concern_1 = require("../read_concern");
|
|
|
13
12
|
const read_preference_1 = require("../read_preference");
|
|
14
13
|
const sessions_1 = require("../sessions");
|
|
15
14
|
const utils_1 = require("../utils");
|
|
16
|
-
/** @internal */
|
|
17
|
-
const kId = Symbol('id');
|
|
18
|
-
/** @internal */
|
|
19
|
-
const kDocuments = Symbol('documents');
|
|
20
|
-
/** @internal */
|
|
21
|
-
const kServer = Symbol('server');
|
|
22
|
-
/** @internal */
|
|
23
|
-
const kNamespace = Symbol('namespace');
|
|
24
|
-
/** @internal */
|
|
25
|
-
const kClient = Symbol('client');
|
|
26
|
-
/** @internal */
|
|
27
|
-
const kSession = Symbol('session');
|
|
28
|
-
/** @internal */
|
|
29
|
-
const kOptions = Symbol('options');
|
|
30
|
-
/** @internal */
|
|
31
|
-
const kTransform = Symbol('transform');
|
|
32
|
-
/** @internal */
|
|
33
|
-
const kInitialized = Symbol('initialized');
|
|
34
|
-
/** @internal */
|
|
35
|
-
const kClosed = Symbol('closed');
|
|
36
|
-
/** @internal */
|
|
37
|
-
const kKilled = Symbol('killed');
|
|
38
|
-
/** @internal */
|
|
39
|
-
const kInit = Symbol('kInit');
|
|
40
15
|
/** @public */
|
|
41
16
|
exports.CURSOR_FLAGS = [
|
|
42
17
|
'tailable',
|
|
@@ -51,102 +26,115 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
51
26
|
/** @internal */
|
|
52
27
|
constructor(client, namespace, options = {}) {
|
|
53
28
|
super();
|
|
29
|
+
/** @internal */
|
|
30
|
+
this.documents = null;
|
|
31
|
+
/** @internal */
|
|
32
|
+
this.hasEmittedClose = false;
|
|
54
33
|
if (!client.s.isMongoClient) {
|
|
55
34
|
throw new error_1.MongoRuntimeError('Cursor must be constructed with MongoClient');
|
|
56
35
|
}
|
|
57
|
-
this
|
|
58
|
-
this
|
|
59
|
-
this
|
|
60
|
-
this
|
|
61
|
-
this
|
|
62
|
-
this
|
|
63
|
-
this
|
|
64
|
-
this[kOptions] = {
|
|
36
|
+
this.cursorClient = client;
|
|
37
|
+
this.cursorNamespace = namespace;
|
|
38
|
+
this.cursorId = null;
|
|
39
|
+
this.initialized = false;
|
|
40
|
+
this.isClosed = false;
|
|
41
|
+
this.isKilled = false;
|
|
42
|
+
this.cursorOptions = {
|
|
65
43
|
readPreference: options.readPreference && options.readPreference instanceof read_preference_1.ReadPreference
|
|
66
44
|
? options.readPreference
|
|
67
45
|
: read_preference_1.ReadPreference.primary,
|
|
68
46
|
...(0, bson_1.pluckBSONSerializeOptions)(options)
|
|
69
47
|
};
|
|
70
|
-
this
|
|
48
|
+
this.cursorOptions.timeoutMS = options.timeoutMS;
|
|
71
49
|
const readConcern = read_concern_1.ReadConcern.fromOptions(options);
|
|
72
50
|
if (readConcern) {
|
|
73
|
-
this
|
|
51
|
+
this.cursorOptions.readConcern = readConcern;
|
|
74
52
|
}
|
|
75
53
|
if (typeof options.batchSize === 'number') {
|
|
76
|
-
this
|
|
54
|
+
this.cursorOptions.batchSize = options.batchSize;
|
|
77
55
|
}
|
|
78
56
|
// we check for undefined specifically here to allow falsy values
|
|
79
57
|
// eslint-disable-next-line no-restricted-syntax
|
|
80
58
|
if (options.comment !== undefined) {
|
|
81
|
-
this
|
|
59
|
+
this.cursorOptions.comment = options.comment;
|
|
82
60
|
}
|
|
83
61
|
if (typeof options.maxTimeMS === 'number') {
|
|
84
|
-
this
|
|
62
|
+
this.cursorOptions.maxTimeMS = options.maxTimeMS;
|
|
85
63
|
}
|
|
86
64
|
if (typeof options.maxAwaitTimeMS === 'number') {
|
|
87
|
-
this
|
|
65
|
+
this.cursorOptions.maxAwaitTimeMS = options.maxAwaitTimeMS;
|
|
88
66
|
}
|
|
89
67
|
if (options.session instanceof sessions_1.ClientSession) {
|
|
90
|
-
this
|
|
68
|
+
this.cursorSession = options.session;
|
|
91
69
|
}
|
|
92
70
|
else {
|
|
93
|
-
this
|
|
71
|
+
this.cursorSession = this.cursorClient.startSession({ owner: this, explicit: false });
|
|
94
72
|
}
|
|
95
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* The cursor has no id until it receives a response from the initial cursor creating command.
|
|
76
|
+
*
|
|
77
|
+
* It is non-zero for as long as the database has an open cursor.
|
|
78
|
+
*
|
|
79
|
+
* The initiating command may receive a zero id if the entire result is in the `firstBatch`.
|
|
80
|
+
*/
|
|
96
81
|
get id() {
|
|
97
|
-
return this
|
|
82
|
+
return this.cursorId ?? undefined;
|
|
98
83
|
}
|
|
99
84
|
/** @internal */
|
|
100
85
|
get isDead() {
|
|
101
|
-
return (this
|
|
86
|
+
return (this.cursorId?.isZero() ?? false) || this.isClosed || this.isKilled;
|
|
102
87
|
}
|
|
103
88
|
/** @internal */
|
|
104
89
|
get client() {
|
|
105
|
-
return this
|
|
90
|
+
return this.cursorClient;
|
|
106
91
|
}
|
|
107
92
|
/** @internal */
|
|
108
93
|
get server() {
|
|
109
|
-
return this
|
|
94
|
+
return this.selectedServer;
|
|
110
95
|
}
|
|
111
96
|
get namespace() {
|
|
112
|
-
return this
|
|
97
|
+
return this.cursorNamespace;
|
|
113
98
|
}
|
|
114
99
|
get readPreference() {
|
|
115
|
-
return this
|
|
100
|
+
return this.cursorOptions.readPreference;
|
|
116
101
|
}
|
|
117
102
|
get readConcern() {
|
|
118
|
-
return this
|
|
103
|
+
return this.cursorOptions.readConcern;
|
|
119
104
|
}
|
|
120
105
|
/** @internal */
|
|
121
106
|
get session() {
|
|
122
|
-
return this
|
|
107
|
+
return this.cursorSession;
|
|
123
108
|
}
|
|
124
109
|
set session(clientSession) {
|
|
125
|
-
this
|
|
126
|
-
}
|
|
127
|
-
/** @internal */
|
|
128
|
-
get cursorOptions() {
|
|
129
|
-
return this[kOptions];
|
|
110
|
+
this.cursorSession = clientSession;
|
|
130
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* The cursor is closed and all remaining locally buffered documents have been iterated.
|
|
114
|
+
*/
|
|
131
115
|
get closed() {
|
|
132
|
-
return this
|
|
116
|
+
return this.isClosed && (this.documents?.length ?? 0) === 0;
|
|
133
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* A `killCursors` command was attempted on this cursor.
|
|
120
|
+
* This is performed if the cursor id is non zero.
|
|
121
|
+
*/
|
|
134
122
|
get killed() {
|
|
135
|
-
return this
|
|
123
|
+
return this.isKilled;
|
|
136
124
|
}
|
|
137
125
|
get loadBalanced() {
|
|
138
|
-
return !!this
|
|
126
|
+
return !!this.cursorClient.topology?.loadBalanced;
|
|
139
127
|
}
|
|
140
128
|
/** Returns current buffered documents length */
|
|
141
129
|
bufferedCount() {
|
|
142
|
-
return this
|
|
130
|
+
return this.documents?.length ?? 0;
|
|
143
131
|
}
|
|
144
132
|
/** Returns current buffered documents */
|
|
145
133
|
readBufferedDocuments(number) {
|
|
146
134
|
const bufferedDocs = [];
|
|
147
|
-
const documentsToRead = Math.min(number ?? this
|
|
135
|
+
const documentsToRead = Math.min(number ?? this.documents?.length ?? 0, this.documents?.length ?? 0);
|
|
148
136
|
for (let count = 0; count < documentsToRead; count++) {
|
|
149
|
-
const document = this
|
|
137
|
+
const document = this.documents?.shift(this.cursorOptions);
|
|
150
138
|
if (document != null) {
|
|
151
139
|
bufferedDocs.push(document);
|
|
152
140
|
}
|
|
@@ -154,39 +142,32 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
154
142
|
return bufferedDocs;
|
|
155
143
|
}
|
|
156
144
|
async *[Symbol.asyncIterator]() {
|
|
157
|
-
if (this.
|
|
145
|
+
if (this.isClosed) {
|
|
158
146
|
return;
|
|
159
147
|
}
|
|
160
148
|
try {
|
|
161
149
|
while (true) {
|
|
150
|
+
if (this.isKilled) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (this.closed) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (this.cursorId != null && this.isDead && (this.documents?.length ?? 0) === 0) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
162
159
|
const document = await this.next();
|
|
163
|
-
// Intentional strict null check, because users can map cursors to falsey values.
|
|
164
|
-
// We allow mapping to all values except for null.
|
|
165
160
|
// eslint-disable-next-line no-restricted-syntax
|
|
166
161
|
if (document === null) {
|
|
167
|
-
|
|
168
|
-
const message = 'Cursor returned a `null` document, but the cursor is not exhausted. Mapping documents to `null` is not supported in the cursor transform.';
|
|
169
|
-
try {
|
|
170
|
-
await cleanupCursor(this, { needsToEmitClosed: true });
|
|
171
|
-
}
|
|
172
|
-
catch (error) {
|
|
173
|
-
(0, utils_1.squashError)(error);
|
|
174
|
-
}
|
|
175
|
-
throw new error_1.MongoAPIError(message);
|
|
176
|
-
}
|
|
177
|
-
break;
|
|
162
|
+
return;
|
|
178
163
|
}
|
|
179
164
|
yield document;
|
|
180
|
-
if (this[kId] === bson_1.Long.ZERO) {
|
|
181
|
-
// Cursor exhausted
|
|
182
|
-
break;
|
|
183
|
-
}
|
|
184
165
|
}
|
|
185
166
|
}
|
|
186
167
|
finally {
|
|
187
168
|
// Only close the cursor if it has not already been closed. This finally clause handles
|
|
188
169
|
// the case when a user would break out of a for await of loop early.
|
|
189
|
-
if (!this.
|
|
170
|
+
if (!this.isClosed) {
|
|
190
171
|
try {
|
|
191
172
|
await this.close();
|
|
192
173
|
}
|
|
@@ -221,29 +202,54 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
221
202
|
return new ReadableCursorStream(this);
|
|
222
203
|
}
|
|
223
204
|
async hasNext() {
|
|
224
|
-
if (this
|
|
205
|
+
if (this.cursorId === bson_1.Long.ZERO) {
|
|
225
206
|
return false;
|
|
226
207
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
208
|
+
do {
|
|
209
|
+
if ((this.documents?.length ?? 0) !== 0) {
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
await this.fetchBatch();
|
|
213
|
+
} while (!this.isDead || (this.documents?.length ?? 0) !== 0);
|
|
214
|
+
return false;
|
|
231
215
|
}
|
|
232
216
|
/** Get the next available document from the cursor, returns null if no more documents are available. */
|
|
233
217
|
async next() {
|
|
234
|
-
if (this
|
|
218
|
+
if (this.cursorId === bson_1.Long.ZERO) {
|
|
235
219
|
throw new error_1.MongoCursorExhaustedError();
|
|
236
220
|
}
|
|
237
|
-
|
|
221
|
+
do {
|
|
222
|
+
const doc = this.documents?.shift(this.cursorOptions);
|
|
223
|
+
if (doc != null) {
|
|
224
|
+
if (this.transform != null)
|
|
225
|
+
return await this.transformDocument(doc);
|
|
226
|
+
return doc;
|
|
227
|
+
}
|
|
228
|
+
await this.fetchBatch();
|
|
229
|
+
} while (!this.isDead || (this.documents?.length ?? 0) !== 0);
|
|
230
|
+
return null;
|
|
238
231
|
}
|
|
239
232
|
/**
|
|
240
233
|
* Try to get the next available document from the cursor or `null` if an empty batch is returned
|
|
241
234
|
*/
|
|
242
235
|
async tryNext() {
|
|
243
|
-
if (this
|
|
236
|
+
if (this.cursorId === bson_1.Long.ZERO) {
|
|
244
237
|
throw new error_1.MongoCursorExhaustedError();
|
|
245
238
|
}
|
|
246
|
-
|
|
239
|
+
let doc = this.documents?.shift(this.cursorOptions);
|
|
240
|
+
if (doc != null) {
|
|
241
|
+
if (this.transform != null)
|
|
242
|
+
return await this.transformDocument(doc);
|
|
243
|
+
return doc;
|
|
244
|
+
}
|
|
245
|
+
await this.fetchBatch();
|
|
246
|
+
doc = this.documents?.shift(this.cursorOptions);
|
|
247
|
+
if (doc != null) {
|
|
248
|
+
if (this.transform != null)
|
|
249
|
+
return await this.transformDocument(doc);
|
|
250
|
+
return doc;
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
247
253
|
}
|
|
248
254
|
/**
|
|
249
255
|
* Iterates over all the documents for this cursor using the iterator, callback pattern.
|
|
@@ -265,9 +271,7 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
265
271
|
}
|
|
266
272
|
}
|
|
267
273
|
async close() {
|
|
268
|
-
|
|
269
|
-
this[kClosed] = true;
|
|
270
|
-
await cleanupCursor(this, { needsToEmitClosed });
|
|
274
|
+
await this.cleanup();
|
|
271
275
|
}
|
|
272
276
|
/**
|
|
273
277
|
* Returns an array of documents. The caller is responsible for making sure that there
|
|
@@ -289,14 +293,14 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
289
293
|
* @param value - The flag boolean value.
|
|
290
294
|
*/
|
|
291
295
|
addCursorFlag(flag, value) {
|
|
292
|
-
|
|
296
|
+
this.throwIfInitialized();
|
|
293
297
|
if (!exports.CURSOR_FLAGS.includes(flag)) {
|
|
294
298
|
throw new error_1.MongoInvalidArgumentError(`Flag ${flag} is not one of ${exports.CURSOR_FLAGS}`);
|
|
295
299
|
}
|
|
296
300
|
if (typeof value !== 'boolean') {
|
|
297
301
|
throw new error_1.MongoInvalidArgumentError(`Flag ${flag} must be a boolean value`);
|
|
298
302
|
}
|
|
299
|
-
this[
|
|
303
|
+
this.cursorOptions[flag] = value;
|
|
300
304
|
return this;
|
|
301
305
|
}
|
|
302
306
|
/**
|
|
@@ -342,15 +346,15 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
342
346
|
* @param transform - The mapping transformation method.
|
|
343
347
|
*/
|
|
344
348
|
map(transform) {
|
|
345
|
-
|
|
346
|
-
const oldTransform = this
|
|
349
|
+
this.throwIfInitialized();
|
|
350
|
+
const oldTransform = this.transform;
|
|
347
351
|
if (oldTransform) {
|
|
348
|
-
this
|
|
352
|
+
this.transform = doc => {
|
|
349
353
|
return transform(oldTransform(doc));
|
|
350
354
|
};
|
|
351
355
|
}
|
|
352
356
|
else {
|
|
353
|
-
this
|
|
357
|
+
this.transform = transform;
|
|
354
358
|
}
|
|
355
359
|
return this;
|
|
356
360
|
}
|
|
@@ -360,12 +364,12 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
360
364
|
* @param readPreference - The new read preference for the cursor.
|
|
361
365
|
*/
|
|
362
366
|
withReadPreference(readPreference) {
|
|
363
|
-
|
|
367
|
+
this.throwIfInitialized();
|
|
364
368
|
if (readPreference instanceof read_preference_1.ReadPreference) {
|
|
365
|
-
this
|
|
369
|
+
this.cursorOptions.readPreference = readPreference;
|
|
366
370
|
}
|
|
367
371
|
else if (typeof readPreference === 'string') {
|
|
368
|
-
this
|
|
372
|
+
this.cursorOptions.readPreference = read_preference_1.ReadPreference.fromString(readPreference);
|
|
369
373
|
}
|
|
370
374
|
else {
|
|
371
375
|
throw new error_1.MongoInvalidArgumentError(`Invalid read preference: ${readPreference}`);
|
|
@@ -378,10 +382,10 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
378
382
|
* @param readPreference - The new read preference for the cursor.
|
|
379
383
|
*/
|
|
380
384
|
withReadConcern(readConcern) {
|
|
381
|
-
|
|
385
|
+
this.throwIfInitialized();
|
|
382
386
|
const resolvedReadConcern = read_concern_1.ReadConcern.fromOptions({ readConcern });
|
|
383
387
|
if (resolvedReadConcern) {
|
|
384
|
-
this
|
|
388
|
+
this.cursorOptions.readConcern = resolvedReadConcern;
|
|
385
389
|
}
|
|
386
390
|
return this;
|
|
387
391
|
}
|
|
@@ -391,11 +395,11 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
391
395
|
* @param value - Number of milliseconds to wait before aborting the query.
|
|
392
396
|
*/
|
|
393
397
|
maxTimeMS(value) {
|
|
394
|
-
|
|
398
|
+
this.throwIfInitialized();
|
|
395
399
|
if (typeof value !== 'number') {
|
|
396
400
|
throw new error_1.MongoInvalidArgumentError('Argument for maxTimeMS must be a number');
|
|
397
401
|
}
|
|
398
|
-
this
|
|
402
|
+
this.cursorOptions.maxTimeMS = value;
|
|
399
403
|
return this;
|
|
400
404
|
}
|
|
401
405
|
/**
|
|
@@ -404,14 +408,14 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
404
408
|
* @param value - The number of documents to return per batch. See {@link https://www.mongodb.com/docs/manual/reference/command/find/|find command documentation}.
|
|
405
409
|
*/
|
|
406
410
|
batchSize(value) {
|
|
407
|
-
|
|
408
|
-
if (this
|
|
411
|
+
this.throwIfInitialized();
|
|
412
|
+
if (this.cursorOptions.tailable) {
|
|
409
413
|
throw new error_1.MongoTailableCursorError('Tailable cursor does not support batchSize');
|
|
410
414
|
}
|
|
411
415
|
if (typeof value !== 'number') {
|
|
412
416
|
throw new error_1.MongoInvalidArgumentError('Operation "batchSize" requires an integer');
|
|
413
417
|
}
|
|
414
|
-
this
|
|
418
|
+
this.cursorOptions.batchSize = value;
|
|
415
419
|
return this;
|
|
416
420
|
}
|
|
417
421
|
/**
|
|
@@ -420,15 +424,15 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
420
424
|
* if the resultant data has already been retrieved by this cursor.
|
|
421
425
|
*/
|
|
422
426
|
rewind() {
|
|
423
|
-
if (!this
|
|
427
|
+
if (!this.initialized) {
|
|
424
428
|
return;
|
|
425
429
|
}
|
|
426
|
-
this
|
|
427
|
-
this
|
|
428
|
-
this
|
|
429
|
-
this
|
|
430
|
-
this
|
|
431
|
-
const session = this
|
|
430
|
+
this.cursorId = null;
|
|
431
|
+
this.documents?.clear();
|
|
432
|
+
this.isClosed = false;
|
|
433
|
+
this.isKilled = false;
|
|
434
|
+
this.initialized = false;
|
|
435
|
+
const session = this.cursorSession;
|
|
432
436
|
if (session) {
|
|
433
437
|
// We only want to end this session if we created it, and it hasn't ended yet
|
|
434
438
|
if (session.explicit === false) {
|
|
@@ -436,20 +440,24 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
436
440
|
// eslint-disable-next-line github/no-then
|
|
437
441
|
session.endSession().then(undefined, utils_1.squashError);
|
|
438
442
|
}
|
|
439
|
-
this
|
|
443
|
+
this.cursorSession = this.cursorClient.startSession({ owner: this, explicit: false });
|
|
440
444
|
}
|
|
441
445
|
}
|
|
442
446
|
}
|
|
443
447
|
/** @internal */
|
|
444
|
-
async getMore(batchSize
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
448
|
+
async getMore(batchSize) {
|
|
449
|
+
if (this.cursorId == null) {
|
|
450
|
+
throw new error_1.MongoRuntimeError('Unexpected null cursor id. A cursor creating command should have set this');
|
|
451
|
+
}
|
|
452
|
+
if (this.selectedServer == null) {
|
|
453
|
+
throw new error_1.MongoRuntimeError('Unexpected null selectedServer. A cursor creating command should have set this');
|
|
454
|
+
}
|
|
455
|
+
const getMoreOperation = new get_more_1.GetMoreOperation(this.cursorNamespace, this.cursorId, this.selectedServer, {
|
|
456
|
+
...this.cursorOptions,
|
|
457
|
+
session: this.cursorSession,
|
|
458
|
+
batchSize
|
|
451
459
|
});
|
|
452
|
-
return await (0, execute_operation_1.executeOperation)(this
|
|
460
|
+
return await (0, execute_operation_1.executeOperation)(this.cursorClient, getMoreOperation);
|
|
453
461
|
}
|
|
454
462
|
/**
|
|
455
463
|
* @internal
|
|
@@ -458,118 +466,56 @@ class AbstractCursor extends mongo_types_1.TypedEventEmitter {
|
|
|
458
466
|
* operation. We cannot refactor to use the abstract _initialize method without
|
|
459
467
|
* a significant refactor.
|
|
460
468
|
*/
|
|
461
|
-
async
|
|
469
|
+
async cursorInit() {
|
|
462
470
|
try {
|
|
463
|
-
const state = await this._initialize(this
|
|
471
|
+
const state = await this._initialize(this.cursorSession);
|
|
464
472
|
const response = state.response;
|
|
465
|
-
this
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
this[kDocuments] = response;
|
|
471
|
-
}
|
|
472
|
-
else if (response.cursor) {
|
|
473
|
-
// TODO(NODE-2674): Preserve int64 sent from MongoDB
|
|
474
|
-
this[kId] =
|
|
475
|
-
typeof response.cursor.id === 'number'
|
|
476
|
-
? bson_1.Long.fromNumber(response.cursor.id)
|
|
477
|
-
: typeof response.cursor.id === 'bigint'
|
|
478
|
-
? bson_1.Long.fromBigInt(response.cursor.id)
|
|
479
|
-
: response.cursor.id;
|
|
480
|
-
if (response.cursor.ns) {
|
|
481
|
-
this[kNamespace] = (0, utils_1.ns)(response.cursor.ns);
|
|
482
|
-
}
|
|
483
|
-
this[kDocuments].pushMany(response.cursor.firstBatch);
|
|
484
|
-
}
|
|
485
|
-
// When server responses return without a cursor document, we close this cursor
|
|
486
|
-
// and return the raw server response. This is often the case for explain commands
|
|
487
|
-
// for example
|
|
488
|
-
if (this[kId] == null) {
|
|
489
|
-
this[kId] = bson_1.Long.ZERO;
|
|
490
|
-
// TODO(NODE-3286): ExecutionResult needs to accept a generic parameter
|
|
491
|
-
this[kDocuments].push(state.response);
|
|
492
|
-
}
|
|
493
|
-
// the cursor is now initialized, even if it is dead
|
|
494
|
-
this[kInitialized] = true;
|
|
473
|
+
this.selectedServer = state.server;
|
|
474
|
+
this.cursorId = response.id;
|
|
475
|
+
this.cursorNamespace = response.ns ?? this.namespace;
|
|
476
|
+
this.documents = response;
|
|
477
|
+
this.initialized = true; // the cursor is now initialized, even if it is dead
|
|
495
478
|
}
|
|
496
479
|
catch (error) {
|
|
497
480
|
// the cursor is now initialized, even if an error occurred
|
|
498
|
-
this
|
|
499
|
-
await
|
|
481
|
+
this.initialized = true;
|
|
482
|
+
await this.cleanup(error);
|
|
500
483
|
throw error;
|
|
501
484
|
}
|
|
502
485
|
if (this.isDead) {
|
|
503
|
-
await
|
|
486
|
+
await this.cleanup();
|
|
504
487
|
}
|
|
505
488
|
return;
|
|
506
489
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
async function next(cursor, { blocking, transform, shift }) {
|
|
512
|
-
if (cursor.closed) {
|
|
513
|
-
if (!shift)
|
|
514
|
-
return false;
|
|
515
|
-
return null;
|
|
516
|
-
}
|
|
517
|
-
do {
|
|
518
|
-
if (cursor[kId] == null) {
|
|
519
|
-
// All cursors must operate within a session, one must be made implicitly if not explicitly provided
|
|
520
|
-
await cursor[kInit]();
|
|
521
|
-
}
|
|
522
|
-
if (cursor[kDocuments].length !== 0) {
|
|
523
|
-
if (!shift)
|
|
524
|
-
return true;
|
|
525
|
-
const doc = cursor[kDocuments].shift(cursor[kOptions]);
|
|
526
|
-
if (doc != null && transform && cursor[kTransform]) {
|
|
527
|
-
try {
|
|
528
|
-
return cursor[kTransform](doc);
|
|
529
|
-
}
|
|
530
|
-
catch (error) {
|
|
531
|
-
try {
|
|
532
|
-
await cleanupCursor(cursor, { error, needsToEmitClosed: true });
|
|
533
|
-
}
|
|
534
|
-
catch (error) {
|
|
535
|
-
// `cleanupCursor` should never throw, squash and throw the original error
|
|
536
|
-
(0, utils_1.squashError)(error);
|
|
537
|
-
}
|
|
538
|
-
throw error;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
return doc;
|
|
490
|
+
/** @internal Attempt to obtain more documents */
|
|
491
|
+
async fetchBatch() {
|
|
492
|
+
if (this.isClosed) {
|
|
493
|
+
return;
|
|
542
494
|
}
|
|
543
|
-
if (
|
|
495
|
+
if (this.isDead) {
|
|
544
496
|
// if the cursor is dead, we clean it up
|
|
545
497
|
// cleanupCursor should never throw, but if it does it indicates a bug in the driver
|
|
546
498
|
// and we should surface the error
|
|
547
|
-
await
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
499
|
+
await this.cleanup();
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
if (this.cursorId == null) {
|
|
503
|
+
await this.cursorInit();
|
|
504
|
+
// If the cursor died or returned documents, return
|
|
505
|
+
if ((this.documents?.length ?? 0) !== 0 || this.isDead)
|
|
506
|
+
return;
|
|
507
|
+
// Otherwise, run a getMore
|
|
551
508
|
}
|
|
552
509
|
// otherwise need to call getMore
|
|
553
|
-
const batchSize =
|
|
510
|
+
const batchSize = this.cursorOptions.batchSize || 1000;
|
|
554
511
|
try {
|
|
555
|
-
const response = await
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
cursor[kDocuments] = response;
|
|
559
|
-
}
|
|
560
|
-
else if (response) {
|
|
561
|
-
const cursorId = typeof response.cursor.id === 'number'
|
|
562
|
-
? bson_1.Long.fromNumber(response.cursor.id)
|
|
563
|
-
: typeof response.cursor.id === 'bigint'
|
|
564
|
-
? bson_1.Long.fromBigInt(response.cursor.id)
|
|
565
|
-
: response.cursor.id;
|
|
566
|
-
cursor[kDocuments].pushMany(response.cursor.nextBatch);
|
|
567
|
-
cursor[kId] = cursorId;
|
|
568
|
-
}
|
|
512
|
+
const response = await this.getMore(batchSize);
|
|
513
|
+
this.cursorId = response.id;
|
|
514
|
+
this.documents = response;
|
|
569
515
|
}
|
|
570
516
|
catch (error) {
|
|
571
517
|
try {
|
|
572
|
-
await
|
|
518
|
+
await this.cleanup(error);
|
|
573
519
|
}
|
|
574
520
|
catch (error) {
|
|
575
521
|
// `cleanupCursor` should never throw, squash and throw the original error
|
|
@@ -577,7 +523,7 @@ async function next(cursor, { blocking, transform, shift }) {
|
|
|
577
523
|
}
|
|
578
524
|
throw error;
|
|
579
525
|
}
|
|
580
|
-
if (
|
|
526
|
+
if (this.isDead) {
|
|
581
527
|
// If we successfully received a response from a cursor BUT the cursor indicates that it is exhausted,
|
|
582
528
|
// we intentionally clean up the cursor to release its session back into the pool before the cursor
|
|
583
529
|
// is iterated. This prevents a cursor that is exhausted on the server from holding
|
|
@@ -585,89 +531,85 @@ async function next(cursor, { blocking, transform, shift }) {
|
|
|
585
531
|
//
|
|
586
532
|
// cleanupCursorAsync should never throw, but if it does it indicates a bug in the driver
|
|
587
533
|
// and we should surface the error
|
|
588
|
-
await
|
|
534
|
+
await this.cleanup();
|
|
589
535
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
536
|
+
}
|
|
537
|
+
/** @internal */
|
|
538
|
+
async cleanup(error) {
|
|
539
|
+
this.isClosed = true;
|
|
540
|
+
const session = this.cursorSession;
|
|
541
|
+
try {
|
|
542
|
+
if (!this.isKilled &&
|
|
543
|
+
this.cursorId &&
|
|
544
|
+
!this.cursorId.isZero() &&
|
|
545
|
+
this.cursorNamespace &&
|
|
546
|
+
this.selectedServer &&
|
|
547
|
+
!session.hasEnded) {
|
|
548
|
+
this.isKilled = true;
|
|
549
|
+
const cursorId = this.cursorId;
|
|
550
|
+
this.cursorId = bson_1.Long.ZERO;
|
|
551
|
+
await (0, execute_operation_1.executeOperation)(this.cursorClient, new kill_cursors_1.KillCursorsOperation(cursorId, this.cursorNamespace, this.selectedServer, {
|
|
552
|
+
session
|
|
553
|
+
}));
|
|
554
|
+
}
|
|
594
555
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
return false;
|
|
598
|
-
return null;
|
|
599
|
-
}
|
|
600
|
-
async function cleanupCursor(cursor, options) {
|
|
601
|
-
const cursorId = cursor[kId];
|
|
602
|
-
const cursorNs = cursor[kNamespace];
|
|
603
|
-
const server = cursor[kServer];
|
|
604
|
-
const session = cursor[kSession];
|
|
605
|
-
const error = options?.error;
|
|
606
|
-
// Cursors only emit closed events once the client-side cursor has been exhausted fully or there
|
|
607
|
-
// was an error. Notably, when the server returns a cursor id of 0 and a non-empty batch, we
|
|
608
|
-
// cleanup the cursor but don't emit a `close` event.
|
|
609
|
-
const needsToEmitClosed = options?.needsToEmitClosed ?? cursor[kDocuments].length === 0;
|
|
610
|
-
if (error) {
|
|
611
|
-
if (cursor.loadBalanced && error instanceof error_1.MongoNetworkError) {
|
|
612
|
-
return await completeCleanup();
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
if (cursorId == null || server == null || cursorId.isZero() || cursorNs == null) {
|
|
616
|
-
if (needsToEmitClosed) {
|
|
617
|
-
cursor[kClosed] = true;
|
|
618
|
-
cursor[kId] = bson_1.Long.ZERO;
|
|
619
|
-
cursor.emit(AbstractCursor.CLOSE);
|
|
556
|
+
catch (error) {
|
|
557
|
+
(0, utils_1.squashError)(error);
|
|
620
558
|
}
|
|
621
|
-
|
|
622
|
-
if (session
|
|
559
|
+
finally {
|
|
560
|
+
if (session?.owner === this) {
|
|
623
561
|
await session.endSession({ error });
|
|
624
|
-
return;
|
|
625
562
|
}
|
|
626
|
-
if (!session
|
|
563
|
+
if (!session?.inTransaction()) {
|
|
627
564
|
(0, sessions_1.maybeClearPinnedConnection)(session, { error });
|
|
628
565
|
}
|
|
566
|
+
this.emitClose();
|
|
629
567
|
}
|
|
630
|
-
return;
|
|
631
568
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
finally {
|
|
639
|
-
cursor.emit(AbstractCursor.CLOSE);
|
|
640
|
-
}
|
|
641
|
-
return;
|
|
642
|
-
}
|
|
643
|
-
if (!session.inTransaction()) {
|
|
644
|
-
(0, sessions_1.maybeClearPinnedConnection)(session, { error });
|
|
569
|
+
/** @internal */
|
|
570
|
+
emitClose() {
|
|
571
|
+
try {
|
|
572
|
+
if (!this.hasEmittedClose && ((this.documents?.length ?? 0) === 0 || this.isClosed)) {
|
|
573
|
+
// @ts-expect-error: CursorEvents is generic so Parameters<CursorEvents["close"]> may not be assignable to `[]`. Not sure how to require extenders do not add parameters.
|
|
574
|
+
this.emit('close');
|
|
645
575
|
}
|
|
646
576
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
cursor[kKilled] = true;
|
|
651
|
-
if (session.hasEnded) {
|
|
652
|
-
return await completeCleanup();
|
|
653
|
-
}
|
|
654
|
-
try {
|
|
655
|
-
await (0, execute_operation_1.executeOperation)(cursor[kClient], new kill_cursors_1.KillCursorsOperation(cursorId, cursorNs, server, { session }));
|
|
656
|
-
}
|
|
657
|
-
catch (error) {
|
|
658
|
-
(0, utils_1.squashError)(error);
|
|
577
|
+
finally {
|
|
578
|
+
this.hasEmittedClose = true;
|
|
579
|
+
}
|
|
659
580
|
}
|
|
660
|
-
|
|
661
|
-
|
|
581
|
+
/** @internal */
|
|
582
|
+
async transformDocument(document) {
|
|
583
|
+
if (this.transform == null)
|
|
584
|
+
return document;
|
|
585
|
+
try {
|
|
586
|
+
const transformedDocument = this.transform(document);
|
|
587
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
588
|
+
if (transformedDocument === null) {
|
|
589
|
+
const TRANSFORM_TO_NULL_ERROR = 'Cursor returned a `null` document, but the cursor is not exhausted. Mapping documents to `null` is not supported in the cursor transform.';
|
|
590
|
+
throw new error_1.MongoAPIError(TRANSFORM_TO_NULL_ERROR);
|
|
591
|
+
}
|
|
592
|
+
return transformedDocument;
|
|
593
|
+
}
|
|
594
|
+
catch (transformError) {
|
|
595
|
+
try {
|
|
596
|
+
await this.close();
|
|
597
|
+
}
|
|
598
|
+
catch (closeError) {
|
|
599
|
+
(0, utils_1.squashError)(closeError);
|
|
600
|
+
}
|
|
601
|
+
throw transformError;
|
|
602
|
+
}
|
|
662
603
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
throw new error_1.MongoCursorInUseError();
|
|
604
|
+
/** @internal */
|
|
605
|
+
throwIfInitialized() {
|
|
606
|
+
if (this.initialized)
|
|
607
|
+
throw new error_1.MongoCursorInUseError();
|
|
668
608
|
}
|
|
669
609
|
}
|
|
670
|
-
|
|
610
|
+
/** @event */
|
|
611
|
+
AbstractCursor.CLOSE = 'close';
|
|
612
|
+
exports.AbstractCursor = AbstractCursor;
|
|
671
613
|
class ReadableCursorStream extends stream_1.Readable {
|
|
672
614
|
constructor(cursor) {
|
|
673
615
|
super({
|
|
@@ -690,8 +632,12 @@ class ReadableCursorStream extends stream_1.Readable {
|
|
|
690
632
|
this._cursor.close().then(() => callback(error), closeError => callback(closeError));
|
|
691
633
|
}
|
|
692
634
|
_readNext() {
|
|
635
|
+
if (this._cursor.id === bson_1.Long.ZERO) {
|
|
636
|
+
this.push(null);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
693
639
|
// eslint-disable-next-line github/no-then
|
|
694
|
-
|
|
640
|
+
this._cursor.next().then(result => {
|
|
695
641
|
if (result == null) {
|
|
696
642
|
this.push(null);
|
|
697
643
|
}
|