mongodb 6.7.0-dev.20240608.sha.0655c730 → 6.7.0-dev.20240614.sha.3ed6a2ad
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/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/constants.js +9 -1
- package/lib/constants.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +24 -60
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +2 -3
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/change_stream_cursor.js +6 -8
- package/lib/cursor/change_stream_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +5 -17
- 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 +6 -21
- 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/count_documents.js +1 -7
- package/lib/operations/count_documents.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/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 +120 -85
- package/package.json +1 -1
- package/src/bson.ts +1 -0
- package/src/client-side-encryption/auto_encrypter.ts +9 -70
- package/src/client-side-encryption/client_encryption.ts +6 -6
- package/src/client-side-encryption/providers/index.ts +120 -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/constants.ts +9 -0
- package/src/cursor/abstract_cursor.ts +51 -71
- package/src/cursor/aggregation_cursor.ts +13 -12
- package/src/cursor/change_stream_cursor.ts +20 -34
- package/src/cursor/find_cursor.ts +17 -25
- 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 +16 -28
- package/src/index.ts +12 -8
- 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/count_documents.ts +7 -11
- 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/utils.ts +52 -2
- package/src/write_concern.ts +18 -0
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type BSONElement,
|
|
2
3
|
type BSONSerializeOptions,
|
|
3
4
|
BSONType,
|
|
4
5
|
type Document,
|
|
5
6
|
Long,
|
|
6
7
|
parseToElementsToArray,
|
|
8
|
+
pluckBSONSerializeOptions,
|
|
7
9
|
type Timestamp
|
|
8
10
|
} from '../../bson';
|
|
9
11
|
import { MongoUnexpectedServerResponseError } from '../../error';
|
|
10
12
|
import { type ClusterTime } from '../../sdam/common';
|
|
11
|
-
import {
|
|
12
|
-
import { OnDemandDocument } from './on_demand/document';
|
|
13
|
+
import { decorateDecryptionResult, ns } from '../../utils';
|
|
14
|
+
import { type JSTypeOf, OnDemandDocument } from './on_demand/document';
|
|
13
15
|
|
|
14
16
|
// eslint-disable-next-line no-restricted-syntax
|
|
15
17
|
const enum BSONElementOffset {
|
|
@@ -30,8 +32,7 @@ const enum BSONElementOffset {
|
|
|
30
32
|
*
|
|
31
33
|
* @param bytes - BSON document returned from the server
|
|
32
34
|
*/
|
|
33
|
-
export function isErrorResponse(bson: Uint8Array): boolean {
|
|
34
|
-
const elements = parseToElementsToArray(bson, 0);
|
|
35
|
+
export function isErrorResponse(bson: Uint8Array, elements: BSONElement[]): boolean {
|
|
35
36
|
for (let eIdx = 0; eIdx < elements.length; eIdx++) {
|
|
36
37
|
const element = elements[eIdx];
|
|
37
38
|
|
|
@@ -60,26 +61,49 @@ export function isErrorResponse(bson: Uint8Array): boolean {
|
|
|
60
61
|
/** @internal */
|
|
61
62
|
export type MongoDBResponseConstructor = {
|
|
62
63
|
new (bson: Uint8Array, offset?: number, isArray?: boolean): MongoDBResponse;
|
|
64
|
+
make(bson: Uint8Array): MongoDBResponse;
|
|
63
65
|
};
|
|
64
66
|
|
|
65
67
|
/** @internal */
|
|
66
68
|
export class MongoDBResponse extends OnDemandDocument {
|
|
69
|
+
// Wrap error thrown from BSON
|
|
70
|
+
public override get<const T extends keyof JSTypeOf>(
|
|
71
|
+
name: string | number,
|
|
72
|
+
as: T,
|
|
73
|
+
required?: false | undefined
|
|
74
|
+
): JSTypeOf[T] | null;
|
|
75
|
+
public override get<const T extends keyof JSTypeOf>(
|
|
76
|
+
name: string | number,
|
|
77
|
+
as: T,
|
|
78
|
+
required: true
|
|
79
|
+
): JSTypeOf[T];
|
|
80
|
+
public override get<const T extends keyof JSTypeOf>(
|
|
81
|
+
name: string | number,
|
|
82
|
+
as: T,
|
|
83
|
+
required?: boolean | undefined
|
|
84
|
+
): JSTypeOf[T] | null {
|
|
85
|
+
try {
|
|
86
|
+
return super.get(name, as, required);
|
|
87
|
+
} catch (cause) {
|
|
88
|
+
throw new MongoUnexpectedServerResponseError(cause.message, { cause });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
67
92
|
static is(value: unknown): value is MongoDBResponse {
|
|
68
93
|
return value instanceof MongoDBResponse;
|
|
69
94
|
}
|
|
70
95
|
|
|
96
|
+
static make(bson: Uint8Array) {
|
|
97
|
+
const elements = parseToElementsToArray(bson, 0);
|
|
98
|
+
const isError = isErrorResponse(bson, elements);
|
|
99
|
+
return isError
|
|
100
|
+
? new MongoDBResponse(bson, 0, false, elements)
|
|
101
|
+
: new this(bson, 0, false, elements);
|
|
102
|
+
}
|
|
103
|
+
|
|
71
104
|
// {ok:1}
|
|
72
105
|
static empty = new MongoDBResponse(new Uint8Array([13, 0, 0, 0, 16, 111, 107, 0, 1, 0, 0, 0, 0]));
|
|
73
106
|
|
|
74
|
-
/** Indicates this document is a server error */
|
|
75
|
-
public get isError() {
|
|
76
|
-
let isError = this.ok === 0;
|
|
77
|
-
isError ||= this.has('errmsg');
|
|
78
|
-
isError ||= this.has('code');
|
|
79
|
-
isError ||= this.has('$err'); // The '$err' field is used in OP_REPLY responses
|
|
80
|
-
return isError;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
107
|
/**
|
|
84
108
|
* Drivers can safely assume that the `recoveryToken` field is always a BSON document but drivers MUST NOT modify the
|
|
85
109
|
* contents of the document.
|
|
@@ -110,6 +134,7 @@ export class MongoDBResponse extends OnDemandDocument {
|
|
|
110
134
|
return this.get('operationTime', BSONType.timestamp);
|
|
111
135
|
}
|
|
112
136
|
|
|
137
|
+
/** Normalizes whatever BSON value is "ok" to a JS number 1 or 0. */
|
|
113
138
|
public get ok(): 0 | 1 {
|
|
114
139
|
return this.getNumber('ok') ? 1 : 0;
|
|
115
140
|
}
|
|
@@ -144,13 +169,7 @@ export class MongoDBResponse extends OnDemandDocument {
|
|
|
144
169
|
|
|
145
170
|
public override toObject(options?: BSONSerializeOptions): Record<string, any> {
|
|
146
171
|
const exactBSONOptions = {
|
|
147
|
-
|
|
148
|
-
promoteLongs: options?.promoteLongs,
|
|
149
|
-
promoteValues: options?.promoteValues,
|
|
150
|
-
promoteBuffers: options?.promoteBuffers,
|
|
151
|
-
bsonRegExp: options?.bsonRegExp,
|
|
152
|
-
raw: options?.raw ?? false,
|
|
153
|
-
fieldsAsRaw: options?.fieldsAsRaw ?? {},
|
|
172
|
+
...pluckBSONSerializeOptions(options ?? {}),
|
|
154
173
|
validation: this.parseBsonSerializationOptions(options)
|
|
155
174
|
};
|
|
156
175
|
return super.toObject(exactBSONOptions);
|
|
@@ -169,69 +188,145 @@ export class MongoDBResponse extends OnDemandDocument {
|
|
|
169
188
|
|
|
170
189
|
/** @internal */
|
|
171
190
|
export class CursorResponse extends MongoDBResponse {
|
|
191
|
+
/**
|
|
192
|
+
* Devtools need to know which keys were encrypted before the driver automatically decrypted them.
|
|
193
|
+
* If decorating is enabled (`Symbol.for('@@mdb.decorateDecryptionResult')`), this field will be set,
|
|
194
|
+
* storing the original encrypted response from the server, so that we can build an object that has
|
|
195
|
+
* the list of BSON keys that were encrypted stored at a well known symbol: `Symbol.for('@@mdb.decryptedKeys')`.
|
|
196
|
+
*/
|
|
197
|
+
encryptedResponse?: MongoDBResponse;
|
|
172
198
|
/**
|
|
173
199
|
* This supports a feature of the FindCursor.
|
|
174
200
|
* It is an optimization to avoid an extra getMore when the limit has been reached
|
|
175
201
|
*/
|
|
176
|
-
static emptyGetMore = {
|
|
202
|
+
static emptyGetMore: CursorResponse = {
|
|
203
|
+
id: new Long(0),
|
|
204
|
+
length: 0,
|
|
205
|
+
shift: () => null
|
|
206
|
+
} as unknown as CursorResponse;
|
|
177
207
|
|
|
178
208
|
static override is(value: unknown): value is CursorResponse {
|
|
179
209
|
return value instanceof CursorResponse || value === CursorResponse.emptyGetMore;
|
|
180
210
|
}
|
|
181
211
|
|
|
182
|
-
|
|
183
|
-
public ns: MongoDBNamespace | null = null;
|
|
184
|
-
public batchSize = 0;
|
|
185
|
-
|
|
186
|
-
private batch: OnDemandDocument;
|
|
212
|
+
private _batch: OnDemandDocument | null = null;
|
|
187
213
|
private iterated = 0;
|
|
188
214
|
|
|
189
|
-
|
|
190
|
-
|
|
215
|
+
get cursor() {
|
|
216
|
+
return this.get('cursor', BSONType.object, true);
|
|
217
|
+
}
|
|
191
218
|
|
|
192
|
-
|
|
219
|
+
public get id(): Long {
|
|
220
|
+
try {
|
|
221
|
+
return Long.fromBigInt(this.cursor.get('id', BSONType.long, true));
|
|
222
|
+
} catch (cause) {
|
|
223
|
+
throw new MongoUnexpectedServerResponseError(cause.message, { cause });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
193
226
|
|
|
194
|
-
|
|
195
|
-
|
|
227
|
+
public get ns() {
|
|
228
|
+
const namespace = this.cursor.get('ns', BSONType.string);
|
|
229
|
+
if (namespace != null) return ns(namespace);
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
public get length() {
|
|
234
|
+
return Math.max(this.batchSize - this.iterated, 0);
|
|
235
|
+
}
|
|
196
236
|
|
|
197
|
-
|
|
198
|
-
|
|
237
|
+
private _encryptedBatch: OnDemandDocument | null = null;
|
|
238
|
+
get encryptedBatch() {
|
|
239
|
+
if (this.encryptedResponse == null) return null;
|
|
240
|
+
if (this._encryptedBatch != null) return this._encryptedBatch;
|
|
199
241
|
|
|
200
|
-
|
|
201
|
-
|
|
242
|
+
const cursor = this.encryptedResponse?.get('cursor', BSONType.object);
|
|
243
|
+
if (cursor?.has('firstBatch'))
|
|
244
|
+
this._encryptedBatch = cursor.get('firstBatch', BSONType.array, true);
|
|
245
|
+
else if (cursor?.has('nextBatch'))
|
|
246
|
+
this._encryptedBatch = cursor.get('nextBatch', BSONType.array, true);
|
|
202
247
|
else throw new MongoUnexpectedServerResponseError('Cursor document did not contain a batch');
|
|
203
248
|
|
|
204
|
-
|
|
249
|
+
return this._encryptedBatch;
|
|
205
250
|
}
|
|
206
251
|
|
|
207
|
-
get
|
|
208
|
-
|
|
252
|
+
private get batch() {
|
|
253
|
+
if (this._batch != null) return this._batch;
|
|
254
|
+
const cursor = this.cursor;
|
|
255
|
+
if (cursor.has('firstBatch')) this._batch = cursor.get('firstBatch', BSONType.array, true);
|
|
256
|
+
else if (cursor.has('nextBatch')) this._batch = cursor.get('nextBatch', BSONType.array, true);
|
|
257
|
+
else throw new MongoUnexpectedServerResponseError('Cursor document did not contain a batch');
|
|
258
|
+
return this._batch;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
public get batchSize() {
|
|
262
|
+
return this.batch?.size();
|
|
209
263
|
}
|
|
210
264
|
|
|
211
|
-
|
|
265
|
+
public get postBatchResumeToken() {
|
|
266
|
+
return (
|
|
267
|
+
this.cursor.get('postBatchResumeToken', BSONType.object)?.toObject({
|
|
268
|
+
promoteValues: false,
|
|
269
|
+
promoteLongs: false,
|
|
270
|
+
promoteBuffers: false
|
|
271
|
+
}) ?? null
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
public shift(options?: BSONSerializeOptions): any {
|
|
212
276
|
if (this.iterated >= this.batchSize) {
|
|
213
277
|
return null;
|
|
214
278
|
}
|
|
215
279
|
|
|
216
280
|
const result = this.batch.get(this.iterated, BSONType.object, true) ?? null;
|
|
281
|
+
const encryptedResult = this.encryptedBatch?.get(this.iterated, BSONType.object, true) ?? null;
|
|
282
|
+
|
|
217
283
|
this.iterated += 1;
|
|
218
284
|
|
|
219
285
|
if (options?.raw) {
|
|
220
286
|
return result.toBytes();
|
|
221
287
|
} else {
|
|
222
|
-
|
|
288
|
+
const object = result.toObject(options);
|
|
289
|
+
if (encryptedResult) {
|
|
290
|
+
decorateDecryptionResult(object, encryptedResult.toObject(options), true);
|
|
291
|
+
}
|
|
292
|
+
return object;
|
|
223
293
|
}
|
|
224
294
|
}
|
|
225
295
|
|
|
226
|
-
clear() {
|
|
296
|
+
public clear() {
|
|
227
297
|
this.iterated = this.batchSize;
|
|
228
298
|
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Explain responses have nothing to do with cursor responses
|
|
303
|
+
* This class serves to temporarily avoid refactoring how cursors handle
|
|
304
|
+
* explain responses which is to detect that the response is not cursor-like and return the explain
|
|
305
|
+
* result as the "first and only" document in the "batch" and end the "cursor"
|
|
306
|
+
*/
|
|
307
|
+
export class ExplainedCursorResponse extends CursorResponse {
|
|
308
|
+
isExplain = true;
|
|
309
|
+
|
|
310
|
+
override get id(): Long {
|
|
311
|
+
return Long.fromBigInt(0n);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
override get batchSize() {
|
|
315
|
+
return 0;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
override get ns() {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
229
321
|
|
|
230
|
-
|
|
231
|
-
|
|
322
|
+
_length = 1;
|
|
323
|
+
override get length(): number {
|
|
324
|
+
return this._length;
|
|
232
325
|
}
|
|
233
326
|
|
|
234
|
-
|
|
235
|
-
|
|
327
|
+
override shift(options?: BSONSerializeOptions | undefined) {
|
|
328
|
+
if (this._length === 0) return null;
|
|
329
|
+
this._length -= 1;
|
|
330
|
+
return this.toObject(options);
|
|
236
331
|
}
|
|
237
332
|
}
|
package/src/constants.ts
CHANGED
|
@@ -165,3 +165,12 @@ export const LEGACY_HELLO_COMMAND = 'ismaster';
|
|
|
165
165
|
* The legacy hello command that was deprecated in MongoDB 5.0.
|
|
166
166
|
*/
|
|
167
167
|
export const LEGACY_HELLO_COMMAND_CAMEL_CASE = 'isMaster';
|
|
168
|
+
|
|
169
|
+
// Typescript errors if we index objects with `Symbol.for(...)`, so
|
|
170
|
+
// to avoid TS errors we pull them out into variables. Then we can type
|
|
171
|
+
// the objects (and class) that we expect to see them on and prevent TS
|
|
172
|
+
// errors.
|
|
173
|
+
/** @internal */
|
|
174
|
+
export const kDecorateResult = Symbol.for('@@mdb.decorateDecryptionResult');
|
|
175
|
+
/** @internal */
|
|
176
|
+
export const kDecoratedKeys = Symbol.for('@@mdb.decryptedKeys');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Readable, Transform } from 'stream';
|
|
2
2
|
|
|
3
3
|
import { type BSONSerializeOptions, type Document, Long, pluckBSONSerializeOptions } from '../bson';
|
|
4
|
-
import { CursorResponse } from '../cmap/wire_protocol/responses';
|
|
4
|
+
import { type CursorResponse } from '../cmap/wire_protocol/responses';
|
|
5
5
|
import {
|
|
6
6
|
MongoAPIError,
|
|
7
7
|
MongoCursorExhaustedError,
|
|
@@ -11,15 +11,33 @@ import {
|
|
|
11
11
|
MongoTailableCursorError
|
|
12
12
|
} from '../error';
|
|
13
13
|
import type { MongoClient } from '../mongo_client';
|
|
14
|
-
import {
|
|
15
|
-
import { executeOperation
|
|
14
|
+
import { TypedEventEmitter } from '../mongo_types';
|
|
15
|
+
import { executeOperation } from '../operations/execute_operation';
|
|
16
16
|
import { GetMoreOperation } from '../operations/get_more';
|
|
17
17
|
import { KillCursorsOperation } from '../operations/kill_cursors';
|
|
18
18
|
import { ReadConcern, type ReadConcernLike } from '../read_concern';
|
|
19
19
|
import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
|
|
20
20
|
import type { Server } from '../sdam/server';
|
|
21
21
|
import { ClientSession, maybeClearPinnedConnection } from '../sessions';
|
|
22
|
-
import {
|
|
22
|
+
import { type MongoDBNamespace, squashError } from '../utils';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @internal
|
|
26
|
+
* TODO(NODE-2882): A cursor's getMore commands must be run on the same server it was started on
|
|
27
|
+
* and the same session must be used for the lifetime of the cursor. This object serves to get the
|
|
28
|
+
* server and session (along with the response) out of executeOperation back to the AbstractCursor.
|
|
29
|
+
*
|
|
30
|
+
* There may be a better design for communicating these values back to the cursor, currently an operation
|
|
31
|
+
* MUST store the selected server on itself so it can be read after executeOperation has returned.
|
|
32
|
+
*/
|
|
33
|
+
export interface InitialCursorResponse {
|
|
34
|
+
/** The server selected for the operation */
|
|
35
|
+
server: Server;
|
|
36
|
+
/** The session used for this operation, may be implicitly created */
|
|
37
|
+
session?: ClientSession;
|
|
38
|
+
/** The raw server response for the operation */
|
|
39
|
+
response: CursorResponse;
|
|
40
|
+
}
|
|
23
41
|
|
|
24
42
|
/** @public */
|
|
25
43
|
export const CURSOR_FLAGS = [
|
|
@@ -118,13 +136,7 @@ export abstract class AbstractCursor<
|
|
|
118
136
|
/** @internal */
|
|
119
137
|
private cursorNamespace: MongoDBNamespace;
|
|
120
138
|
/** @internal */
|
|
121
|
-
private documents:
|
|
122
|
-
length: number;
|
|
123
|
-
shift(bsonOptions?: any): TSchema | null;
|
|
124
|
-
clear(): void;
|
|
125
|
-
pushMany(many: Iterable<TSchema>): void;
|
|
126
|
-
push(item: TSchema): void;
|
|
127
|
-
};
|
|
139
|
+
private documents: CursorResponse | null = null;
|
|
128
140
|
/** @internal */
|
|
129
141
|
private cursorClient: MongoClient;
|
|
130
142
|
/** @internal */
|
|
@@ -155,7 +167,6 @@ export abstract class AbstractCursor<
|
|
|
155
167
|
this.cursorClient = client;
|
|
156
168
|
this.cursorNamespace = namespace;
|
|
157
169
|
this.cursorId = null;
|
|
158
|
-
this.documents = new List();
|
|
159
170
|
this.initialized = false;
|
|
160
171
|
this.isClosed = false;
|
|
161
172
|
this.isKilled = false;
|
|
@@ -252,16 +263,19 @@ export abstract class AbstractCursor<
|
|
|
252
263
|
|
|
253
264
|
/** Returns current buffered documents length */
|
|
254
265
|
bufferedCount(): number {
|
|
255
|
-
return this.documents
|
|
266
|
+
return this.documents?.length ?? 0;
|
|
256
267
|
}
|
|
257
268
|
|
|
258
269
|
/** Returns current buffered documents */
|
|
259
270
|
readBufferedDocuments(number?: number): TSchema[] {
|
|
260
271
|
const bufferedDocs: TSchema[] = [];
|
|
261
|
-
const documentsToRead = Math.min(
|
|
272
|
+
const documentsToRead = Math.min(
|
|
273
|
+
number ?? this.documents?.length ?? 0,
|
|
274
|
+
this.documents?.length ?? 0
|
|
275
|
+
);
|
|
262
276
|
|
|
263
277
|
for (let count = 0; count < documentsToRead; count++) {
|
|
264
|
-
const document = this.documents
|
|
278
|
+
const document = this.documents?.shift(this.cursorOptions);
|
|
265
279
|
if (document != null) {
|
|
266
280
|
bufferedDocs.push(document);
|
|
267
281
|
}
|
|
@@ -269,7 +283,6 @@ export abstract class AbstractCursor<
|
|
|
269
283
|
|
|
270
284
|
return bufferedDocs;
|
|
271
285
|
}
|
|
272
|
-
|
|
273
286
|
async *[Symbol.asyncIterator](): AsyncGenerator<TSchema, void, void> {
|
|
274
287
|
if (this.isClosed) {
|
|
275
288
|
return;
|
|
@@ -281,11 +294,11 @@ export abstract class AbstractCursor<
|
|
|
281
294
|
return;
|
|
282
295
|
}
|
|
283
296
|
|
|
284
|
-
if (this.
|
|
297
|
+
if (this.closed && (this.documents?.length ?? 0) === 0) {
|
|
285
298
|
return;
|
|
286
299
|
}
|
|
287
300
|
|
|
288
|
-
if (this.cursorId != null && this.isDead && this.documents
|
|
301
|
+
if (this.cursorId != null && this.isDead && (this.documents?.length ?? 0) === 0) {
|
|
289
302
|
return;
|
|
290
303
|
}
|
|
291
304
|
|
|
@@ -347,11 +360,11 @@ export abstract class AbstractCursor<
|
|
|
347
360
|
}
|
|
348
361
|
|
|
349
362
|
do {
|
|
350
|
-
if (this.documents
|
|
363
|
+
if ((this.documents?.length ?? 0) !== 0) {
|
|
351
364
|
return true;
|
|
352
365
|
}
|
|
353
366
|
await this.fetchBatch();
|
|
354
|
-
} while (!this.isDead || this.documents
|
|
367
|
+
} while (!this.isDead || (this.documents?.length ?? 0) !== 0);
|
|
355
368
|
|
|
356
369
|
return false;
|
|
357
370
|
}
|
|
@@ -363,13 +376,13 @@ export abstract class AbstractCursor<
|
|
|
363
376
|
}
|
|
364
377
|
|
|
365
378
|
do {
|
|
366
|
-
const doc = this.documents
|
|
379
|
+
const doc = this.documents?.shift(this.cursorOptions);
|
|
367
380
|
if (doc != null) {
|
|
368
381
|
if (this.transform != null) return await this.transformDocument(doc);
|
|
369
382
|
return doc;
|
|
370
383
|
}
|
|
371
384
|
await this.fetchBatch();
|
|
372
|
-
} while (!this.isDead || this.documents
|
|
385
|
+
} while (!this.isDead || (this.documents?.length ?? 0) !== 0);
|
|
373
386
|
|
|
374
387
|
return null;
|
|
375
388
|
}
|
|
@@ -382,7 +395,7 @@ export abstract class AbstractCursor<
|
|
|
382
395
|
throw new MongoCursorExhaustedError();
|
|
383
396
|
}
|
|
384
397
|
|
|
385
|
-
let doc = this.documents
|
|
398
|
+
let doc = this.documents?.shift(this.cursorOptions);
|
|
386
399
|
if (doc != null) {
|
|
387
400
|
if (this.transform != null) return await this.transformDocument(doc);
|
|
388
401
|
return doc;
|
|
@@ -390,7 +403,7 @@ export abstract class AbstractCursor<
|
|
|
390
403
|
|
|
391
404
|
await this.fetchBatch();
|
|
392
405
|
|
|
393
|
-
doc = this.documents
|
|
406
|
+
doc = this.documents?.shift(this.cursorOptions);
|
|
394
407
|
if (doc != null) {
|
|
395
408
|
if (this.transform != null) return await this.transformDocument(doc);
|
|
396
409
|
return doc;
|
|
@@ -591,7 +604,7 @@ export abstract class AbstractCursor<
|
|
|
591
604
|
}
|
|
592
605
|
|
|
593
606
|
this.cursorId = null;
|
|
594
|
-
this.documents
|
|
607
|
+
this.documents?.clear();
|
|
595
608
|
this.isClosed = false;
|
|
596
609
|
this.isKilled = false;
|
|
597
610
|
this.initialized = false;
|
|
@@ -615,10 +628,12 @@ export abstract class AbstractCursor<
|
|
|
615
628
|
abstract clone(): AbstractCursor<TSchema>;
|
|
616
629
|
|
|
617
630
|
/** @internal */
|
|
618
|
-
protected abstract _initialize(
|
|
631
|
+
protected abstract _initialize(
|
|
632
|
+
session: ClientSession | undefined
|
|
633
|
+
): Promise<InitialCursorResponse>;
|
|
619
634
|
|
|
620
635
|
/** @internal */
|
|
621
|
-
async getMore(batchSize: number
|
|
636
|
+
async getMore(batchSize: number): Promise<CursorResponse> {
|
|
622
637
|
if (this.cursorId == null) {
|
|
623
638
|
throw new MongoRuntimeError(
|
|
624
639
|
'Unexpected null cursor id. A cursor creating command should have set this'
|
|
@@ -636,8 +651,7 @@ export abstract class AbstractCursor<
|
|
|
636
651
|
{
|
|
637
652
|
...this.cursorOptions,
|
|
638
653
|
session: this.cursorSession,
|
|
639
|
-
batchSize
|
|
640
|
-
useCursorResponse
|
|
654
|
+
batchSize
|
|
641
655
|
}
|
|
642
656
|
);
|
|
643
657
|
|
|
@@ -656,27 +670,10 @@ export abstract class AbstractCursor<
|
|
|
656
670
|
const state = await this._initialize(this.cursorSession);
|
|
657
671
|
const response = state.response;
|
|
658
672
|
this.selectedServer = state.server;
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
} else if (response.cursor) {
|
|
664
|
-
// TODO(NODE-2674): Preserve int64 sent from MongoDB
|
|
665
|
-
this.cursorId = getCursorId(response);
|
|
666
|
-
if (response.cursor.ns) this.cursorNamespace = ns(response.cursor.ns);
|
|
667
|
-
this.documents.pushMany(response.cursor.firstBatch);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
if (this.cursorId == null) {
|
|
671
|
-
// When server responses return without a cursor document, we close this cursor
|
|
672
|
-
// and return the raw server response. This is the case for explain commands
|
|
673
|
-
this.cursorId = Long.ZERO;
|
|
674
|
-
// TODO(NODE-3286): ExecutionResult needs to accept a generic parameter
|
|
675
|
-
this.documents.push(state.response as TODO_NODE_3286);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
// the cursor is now initialized, even if it is dead
|
|
679
|
-
this.initialized = true;
|
|
673
|
+
this.cursorId = response.id;
|
|
674
|
+
this.cursorNamespace = response.ns ?? this.namespace;
|
|
675
|
+
this.documents = response;
|
|
676
|
+
this.initialized = true; // the cursor is now initialized, even if it is dead
|
|
680
677
|
} catch (error) {
|
|
681
678
|
// the cursor is now initialized, even if an error occurred
|
|
682
679
|
this.initialized = true;
|
|
@@ -708,7 +705,7 @@ export abstract class AbstractCursor<
|
|
|
708
705
|
if (this.cursorId == null) {
|
|
709
706
|
await this.cursorInit();
|
|
710
707
|
// If the cursor died or returned documents, return
|
|
711
|
-
if (this.documents
|
|
708
|
+
if ((this.documents?.length ?? 0) !== 0 || this.isDead) return;
|
|
712
709
|
// Otherwise, run a getMore
|
|
713
710
|
}
|
|
714
711
|
|
|
@@ -717,16 +714,8 @@ export abstract class AbstractCursor<
|
|
|
717
714
|
|
|
718
715
|
try {
|
|
719
716
|
const response = await this.getMore(batchSize);
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (CursorResponse.is(response)) {
|
|
723
|
-
this.cursorId = response.id;
|
|
724
|
-
this.documents = response;
|
|
725
|
-
} else if (response?.cursor) {
|
|
726
|
-
const cursorId = getCursorId(response);
|
|
727
|
-
this.documents.pushMany(response.cursor.nextBatch);
|
|
728
|
-
this.cursorId = cursorId;
|
|
729
|
-
}
|
|
717
|
+
this.cursorId = response.id;
|
|
718
|
+
this.documents = response;
|
|
730
719
|
} catch (error) {
|
|
731
720
|
try {
|
|
732
721
|
await this.cleanup(error);
|
|
@@ -789,7 +778,7 @@ export abstract class AbstractCursor<
|
|
|
789
778
|
/** @internal */
|
|
790
779
|
private emitClose() {
|
|
791
780
|
try {
|
|
792
|
-
if (!this.hasEmittedClose && (this.documents
|
|
781
|
+
if (!this.hasEmittedClose && ((this.documents?.length ?? 0) === 0 || this.isClosed)) {
|
|
793
782
|
// @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.
|
|
794
783
|
this.emit('close');
|
|
795
784
|
}
|
|
@@ -827,15 +816,6 @@ export abstract class AbstractCursor<
|
|
|
827
816
|
}
|
|
828
817
|
}
|
|
829
818
|
|
|
830
|
-
/** A temporary helper to box up the many possible type issue of cursor ids */
|
|
831
|
-
function getCursorId(response: Document) {
|
|
832
|
-
return typeof response.cursor.id === 'number'
|
|
833
|
-
? Long.fromNumber(response.cursor.id)
|
|
834
|
-
: typeof response.cursor.id === 'bigint'
|
|
835
|
-
? Long.fromBigInt(response.cursor.id)
|
|
836
|
-
: response.cursor.id;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
819
|
class ReadableCursorStream extends Readable {
|
|
840
820
|
private _cursor: AbstractCursor;
|
|
841
821
|
private _readInProgress = false;
|
|
@@ -2,12 +2,12 @@ import type { Document } from '../bson';
|
|
|
2
2
|
import type { ExplainVerbosityLike } from '../explain';
|
|
3
3
|
import type { MongoClient } from '../mongo_client';
|
|
4
4
|
import { AggregateOperation, type AggregateOptions } from '../operations/aggregate';
|
|
5
|
-
import { executeOperation
|
|
5
|
+
import { executeOperation } from '../operations/execute_operation';
|
|
6
6
|
import type { ClientSession } from '../sessions';
|
|
7
7
|
import type { Sort } from '../sort';
|
|
8
8
|
import type { MongoDBNamespace } from '../utils';
|
|
9
9
|
import { mergeOptions } from '../utils';
|
|
10
|
-
import type { AbstractCursorOptions } from './abstract_cursor';
|
|
10
|
+
import type { AbstractCursorOptions, InitialCursorResponse } from './abstract_cursor';
|
|
11
11
|
import { AbstractCursor } from './abstract_cursor';
|
|
12
12
|
|
|
13
13
|
/** @public */
|
|
@@ -51,7 +51,7 @@ export class AggregationCursor<TSchema = any> extends AbstractCursor<TSchema> {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/** @internal */
|
|
54
|
-
async _initialize(session: ClientSession): Promise<
|
|
54
|
+
async _initialize(session: ClientSession): Promise<InitialCursorResponse> {
|
|
55
55
|
const aggregateOperation = new AggregateOperation(this.namespace, this.pipeline, {
|
|
56
56
|
...this.aggregateOptions,
|
|
57
57
|
...this.cursorOptions,
|
|
@@ -60,20 +60,21 @@ export class AggregationCursor<TSchema = any> extends AbstractCursor<TSchema> {
|
|
|
60
60
|
|
|
61
61
|
const response = await executeOperation(this.client, aggregateOperation);
|
|
62
62
|
|
|
63
|
-
// TODO: NODE-2882
|
|
64
63
|
return { server: aggregateOperation.server, session, response };
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
/** Execute the explain for the cursor */
|
|
68
67
|
async explain(verbosity?: ExplainVerbosityLike): Promise<Document> {
|
|
69
|
-
return
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
return (
|
|
69
|
+
await executeOperation(
|
|
70
|
+
this.client,
|
|
71
|
+
new AggregateOperation(this.namespace, this.pipeline, {
|
|
72
|
+
...this.aggregateOptions, // NOTE: order matters here, we may need to refine this
|
|
73
|
+
...this.cursorOptions,
|
|
74
|
+
explain: verbosity ?? true
|
|
75
|
+
})
|
|
76
|
+
)
|
|
77
|
+
).shift(this.aggregateOptions);
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
/** Add a stage to the aggregation pipeline
|