mongodb 6.9.0-dev.20240917.sha.20396e1b → 6.9.0-dev.20240926.sha.3d3da407

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.
Files changed (42) hide show
  1. package/lib/beta.d.ts +247 -1
  2. package/lib/cmap/command_monitoring_events.js +10 -1
  3. package/lib/cmap/command_monitoring_events.js.map +1 -1
  4. package/lib/cmap/commands.js +7 -5
  5. package/lib/cmap/commands.js.map +1 -1
  6. package/lib/cmap/connection.js +6 -2
  7. package/lib/cmap/connection.js.map +1 -1
  8. package/lib/cmap/wire_protocol/responses.js +23 -1
  9. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  10. package/lib/cursor/client_bulk_write_cursor.js +50 -0
  11. package/lib/cursor/client_bulk_write_cursor.js.map +1 -0
  12. package/lib/error.js +27 -1
  13. package/lib/error.js.map +1 -1
  14. package/lib/index.js +4 -3
  15. package/lib/index.js.map +1 -1
  16. package/lib/mongo_client.js +10 -0
  17. package/lib/mongo_client.js.map +1 -1
  18. package/lib/operations/client_bulk_write/client_bulk_write.js +35 -0
  19. package/lib/operations/client_bulk_write/client_bulk_write.js.map +1 -0
  20. package/lib/operations/client_bulk_write/command_builder.js +20 -6
  21. package/lib/operations/client_bulk_write/command_builder.js.map +1 -1
  22. package/lib/operations/client_bulk_write/executor.js +71 -0
  23. package/lib/operations/client_bulk_write/executor.js.map +1 -0
  24. package/lib/operations/client_bulk_write/results_merger.js +79 -0
  25. package/lib/operations/client_bulk_write/results_merger.js.map +1 -0
  26. package/lib/write_concern.js.map +1 -1
  27. package/mongodb.d.ts +247 -1
  28. package/package.json +1 -1
  29. package/src/cmap/command_monitoring_events.ts +16 -2
  30. package/src/cmap/commands.ts +22 -12
  31. package/src/cmap/connection.ts +6 -2
  32. package/src/cmap/wire_protocol/responses.ts +26 -0
  33. package/src/cursor/client_bulk_write_cursor.ts +73 -0
  34. package/src/error.ts +27 -0
  35. package/src/index.ts +16 -0
  36. package/src/mongo_client.ts +19 -0
  37. package/src/operations/client_bulk_write/client_bulk_write.ts +45 -0
  38. package/src/operations/client_bulk_write/command_builder.ts +35 -6
  39. package/src/operations/client_bulk_write/common.ts +79 -0
  40. package/src/operations/client_bulk_write/executor.ts +99 -0
  41. package/src/operations/client_bulk_write/results_merger.ts +95 -0
  42. package/src/write_concern.ts +4 -1
package/src/index.ts CHANGED
@@ -44,6 +44,7 @@ export {
44
44
  MongoAWSError,
45
45
  MongoAzureError,
46
46
  MongoBatchReExecutionError,
47
+ MongoBulkWriteCursorError,
47
48
  MongoChangeStreamError,
48
49
  MongoCompatibilityError,
49
50
  MongoCursorExhaustedError,
@@ -473,6 +474,21 @@ export type {
473
474
  AggregateOptions,
474
475
  DB_AGGREGATE_COLLECTION
475
476
  } from './operations/aggregate';
477
+ export type {
478
+ AnyClientBulkWriteModel,
479
+ ClientBulkWriteOptions,
480
+ ClientBulkWriteResult,
481
+ ClientDeleteManyModel,
482
+ ClientDeleteOneModel,
483
+ ClientDeleteResult,
484
+ ClientInsertOneModel,
485
+ ClientInsertOneResult,
486
+ ClientReplaceOneModel,
487
+ ClientUpdateManyModel,
488
+ ClientUpdateOneModel,
489
+ ClientUpdateResult,
490
+ ClientWriteModel
491
+ } from './operations/client_bulk_write/common';
476
492
  export type {
477
493
  CollationOptions,
478
494
  CommandOperation,
@@ -30,6 +30,12 @@ import {
30
30
  SeverityLevel
31
31
  } from './mongo_logger';
32
32
  import { TypedEventEmitter } from './mongo_types';
33
+ import {
34
+ type AnyClientBulkWriteModel,
35
+ type ClientBulkWriteOptions,
36
+ type ClientBulkWriteResult
37
+ } from './operations/client_bulk_write/common';
38
+ import { ClientBulkWriteExecutor } from './operations/client_bulk_write/executor';
33
39
  import { executeOperation } from './operations/execute_operation';
34
40
  import { RunAdminCommandOperation } from './operations/run_command';
35
41
  import type { ReadConcern, ReadConcernLevel, ReadConcernLike } from './read_concern';
@@ -477,6 +483,19 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
477
483
  return this.s.bsonOptions;
478
484
  }
479
485
 
486
+ /**
487
+ * Executes a client bulk write operation, available on server 8.0+.
488
+ * @param models - The client bulk write models.
489
+ * @param options - The client bulk write options.
490
+ * @returns A ClientBulkWriteResult for acknowledged writes and ok: 1 for unacknowledged writes.
491
+ */
492
+ async bulkWrite(
493
+ models: AnyClientBulkWriteModel[],
494
+ options?: ClientBulkWriteOptions
495
+ ): Promise<ClientBulkWriteResult | { ok: 1 }> {
496
+ return await new ClientBulkWriteExecutor(this, models, options).execute();
497
+ }
498
+
480
499
  /**
481
500
  * Connect to MongoDB using a url
482
501
  *
@@ -0,0 +1,45 @@
1
+ import { type Document } from 'bson';
2
+
3
+ import { ClientBulkWriteCursorResponse } from '../../cmap/wire_protocol/responses';
4
+ import type { Server } from '../../sdam/server';
5
+ import type { ClientSession } from '../../sessions';
6
+ import { MongoDBNamespace } from '../../utils';
7
+ import { CommandOperation } from '../command';
8
+ import { Aspect, defineAspects } from '../operation';
9
+ import { type ClientBulkWriteOptions } from './common';
10
+
11
+ /**
12
+ * Executes a single client bulk write operation within a potential batch.
13
+ * @internal
14
+ */
15
+ export class ClientBulkWriteOperation extends CommandOperation<ClientBulkWriteCursorResponse> {
16
+ command: Document;
17
+ override options: ClientBulkWriteOptions;
18
+
19
+ override get commandName() {
20
+ return 'bulkWrite' as const;
21
+ }
22
+
23
+ constructor(command: Document, options: ClientBulkWriteOptions) {
24
+ super(undefined, options);
25
+ this.command = command;
26
+ this.options = options;
27
+ this.ns = new MongoDBNamespace('admin', '$cmd');
28
+ }
29
+
30
+ /**
31
+ * Execute the command. Superclass will handle write concern, etc.
32
+ * @param server - The server.
33
+ * @param session - The session.
34
+ * @returns The response.
35
+ */
36
+ override async execute(
37
+ server: Server,
38
+ session: ClientSession | undefined
39
+ ): Promise<ClientBulkWriteCursorResponse> {
40
+ return await super.executeCommand(server, session, this.command, ClientBulkWriteCursorResponse);
41
+ }
42
+ }
43
+
44
+ // Skipping the collation as it goes on the individual ops.
45
+ defineAspects(ClientBulkWriteOperation, [Aspect.WRITE_OPERATION, Aspect.SKIP_COLLATION]);
@@ -1,6 +1,8 @@
1
1
  import { type Document } from '../../bson';
2
2
  import { DocumentSequence } from '../../cmap/commands';
3
+ import { type PkFactory } from '../../mongo_client';
3
4
  import type { Filter, OptionalId, UpdateFilter, WithoutId } from '../../mongo_types';
5
+ import { DEFAULT_PK_FACTORY } from '../../utils';
4
6
  import { type CollationOptions } from '../command';
5
7
  import { type Hint } from '../operation';
6
8
  import type {
@@ -23,20 +25,27 @@ export interface ClientBulkWriteCommand {
23
25
  nsInfo: DocumentSequence;
24
26
  bypassDocumentValidation?: boolean;
25
27
  let?: Document;
28
+ comment?: any;
26
29
  }
27
30
 
28
31
  /** @internal */
29
32
  export class ClientBulkWriteCommandBuilder {
30
33
  models: AnyClientBulkWriteModel[];
31
34
  options: ClientBulkWriteOptions;
35
+ pkFactory: PkFactory;
32
36
 
33
37
  /**
34
38
  * Create the command builder.
35
39
  * @param models - The client write models.
36
40
  */
37
- constructor(models: AnyClientBulkWriteModel[], options: ClientBulkWriteOptions) {
41
+ constructor(
42
+ models: AnyClientBulkWriteModel[],
43
+ options: ClientBulkWriteOptions,
44
+ pkFactory?: PkFactory
45
+ ) {
38
46
  this.models = models;
39
47
  this.options = options;
48
+ this.pkFactory = pkFactory ?? DEFAULT_PK_FACTORY;
40
49
  }
41
50
 
42
51
  /**
@@ -62,10 +71,10 @@ export class ClientBulkWriteCommandBuilder {
62
71
  const ns = model.namespace;
63
72
  const index = namespaces.get(ns);
64
73
  if (index != null) {
65
- operations.push(buildOperation(model, index));
74
+ operations.push(buildOperation(model, index, this.pkFactory));
66
75
  } else {
67
76
  namespaces.set(ns, currentNamespaceIndex);
68
- operations.push(buildOperation(model, currentNamespaceIndex));
77
+ operations.push(buildOperation(model, currentNamespaceIndex, this.pkFactory));
69
78
  currentNamespaceIndex++;
70
79
  }
71
80
  }
@@ -88,6 +97,12 @@ export class ClientBulkWriteCommandBuilder {
88
97
  if (this.options.let) {
89
98
  command.let = this.options.let;
90
99
  }
100
+
101
+ // we check for undefined specifically here to allow falsy values
102
+ // eslint-disable-next-line no-restricted-syntax
103
+ if (this.options.comment !== undefined) {
104
+ command.comment = this.options.comment;
105
+ }
91
106
  return [command];
92
107
  }
93
108
  }
@@ -106,12 +121,14 @@ interface ClientInsertOperation {
106
121
  */
107
122
  export const buildInsertOneOperation = (
108
123
  model: ClientInsertOneModel,
109
- index: number
124
+ index: number,
125
+ pkFactory: PkFactory
110
126
  ): ClientInsertOperation => {
111
127
  const document: ClientInsertOperation = {
112
128
  insert: index,
113
129
  document: model.document
114
130
  };
131
+ document.document._id = model.document._id ?? pkFactory.createPk();
115
132
  return document;
116
133
  };
117
134
 
@@ -175,6 +192,7 @@ export interface ClientUpdateOperation {
175
192
  hint?: Hint;
176
193
  upsert?: boolean;
177
194
  arrayFilters?: Document[];
195
+ collation?: CollationOptions;
178
196
  }
179
197
 
180
198
  /**
@@ -226,6 +244,9 @@ function createUpdateOperation(
226
244
  if (model.arrayFilters) {
227
245
  document.arrayFilters = model.arrayFilters;
228
246
  }
247
+ if (model.collation) {
248
+ document.collation = model.collation;
249
+ }
229
250
  return document;
230
251
  }
231
252
 
@@ -237,6 +258,7 @@ export interface ClientReplaceOneOperation {
237
258
  updateMods: WithoutId<Document>;
238
259
  hint?: Hint;
239
260
  upsert?: boolean;
261
+ collation?: CollationOptions;
240
262
  }
241
263
 
242
264
  /**
@@ -261,14 +283,21 @@ export const buildReplaceOneOperation = (
261
283
  if (model.upsert) {
262
284
  document.upsert = model.upsert;
263
285
  }
286
+ if (model.collation) {
287
+ document.collation = model.collation;
288
+ }
264
289
  return document;
265
290
  };
266
291
 
267
292
  /** @internal */
268
- export function buildOperation(model: AnyClientBulkWriteModel, index: number): Document {
293
+ export function buildOperation(
294
+ model: AnyClientBulkWriteModel,
295
+ index: number,
296
+ pkFactory: PkFactory
297
+ ): Document {
269
298
  switch (model.name) {
270
299
  case 'insertOne':
271
- return buildInsertOneOperation(model, index);
300
+ return buildInsertOneOperation(model, index, pkFactory);
272
301
  case 'deleteOne':
273
302
  return buildDeleteOneOperation(model, index);
274
303
  case 'deleteMany':
@@ -144,3 +144,82 @@ export type AnyClientBulkWriteModel =
144
144
  | ClientUpdateManyModel
145
145
  | ClientDeleteOneModel
146
146
  | ClientDeleteManyModel;
147
+
148
+ /** @public */
149
+ export interface ClientBulkWriteResult {
150
+ /**
151
+ * The total number of documents inserted across all insert operations.
152
+ */
153
+ insertedCount: number;
154
+ /**
155
+ * The total number of documents upserted across all update operations.
156
+ */
157
+ upsertedCount: number;
158
+ /**
159
+ * The total number of documents matched across all update operations.
160
+ */
161
+ matchedCount: number;
162
+ /**
163
+ * The total number of documents modified across all update operations.
164
+ */
165
+ modifiedCount: number;
166
+ /**
167
+ * The total number of documents deleted across all delete operations.
168
+ */
169
+ deletedCount: number;
170
+ /**
171
+ * The results of each individual insert operation that was successfully performed.
172
+ */
173
+ insertResults?: Map<number, ClientInsertOneResult>;
174
+ /**
175
+ * The results of each individual update operation that was successfully performed.
176
+ */
177
+ updateResults?: Map<number, ClientUpdateResult>;
178
+ /**
179
+ * The results of each individual delete operation that was successfully performed.
180
+ */
181
+ deleteResults?: Map<number, ClientDeleteResult>;
182
+ }
183
+
184
+ /** @public */
185
+ export interface ClientInsertOneResult {
186
+ /**
187
+ * The _id of the inserted document.
188
+ */
189
+ insertedId: any;
190
+ }
191
+
192
+ /** @public */
193
+ export interface ClientUpdateResult {
194
+ /**
195
+ * The number of documents that matched the filter.
196
+ */
197
+ matchedCount: number;
198
+
199
+ /**
200
+ * The number of documents that were modified.
201
+ */
202
+ modifiedCount: number;
203
+
204
+ /**
205
+ * The _id field of the upserted document if an upsert occurred.
206
+ *
207
+ * It MUST be possible to discern between a BSON Null upserted ID value and this field being
208
+ * unset. If necessary, drivers MAY add a didUpsert boolean field to differentiate between
209
+ * these two cases.
210
+ */
211
+ upsertedId?: any;
212
+
213
+ /**
214
+ * Determines if the upsert did include an _id, which includes the case of the _id being null.
215
+ */
216
+ didUpsert: boolean;
217
+ }
218
+
219
+ /** @public */
220
+ export interface ClientDeleteResult {
221
+ /**
222
+ * The number of documents that were deleted.
223
+ */
224
+ deletedCount: number;
225
+ }
@@ -0,0 +1,99 @@
1
+ import { type Document } from 'bson';
2
+
3
+ import { ClientBulkWriteCursor } from '../../cursor/client_bulk_write_cursor';
4
+ import { type MongoClient } from '../../mongo_client';
5
+ import { WriteConcern } from '../../write_concern';
6
+ import { executeOperation } from '../execute_operation';
7
+ import { ClientBulkWriteOperation } from './client_bulk_write';
8
+ import { type ClientBulkWriteCommand, ClientBulkWriteCommandBuilder } from './command_builder';
9
+ import {
10
+ type AnyClientBulkWriteModel,
11
+ type ClientBulkWriteOptions,
12
+ type ClientBulkWriteResult
13
+ } from './common';
14
+ import { ClientBulkWriteResultsMerger } from './results_merger';
15
+
16
+ /**
17
+ * Responsible for executing a client bulk write.
18
+ * @internal
19
+ */
20
+ export class ClientBulkWriteExecutor {
21
+ client: MongoClient;
22
+ options: ClientBulkWriteOptions;
23
+ operations: AnyClientBulkWriteModel[];
24
+
25
+ /**
26
+ * Instantiate the executor.
27
+ * @param client - The mongo client.
28
+ * @param operations - The user supplied bulk write models.
29
+ * @param options - The bulk write options.
30
+ */
31
+ constructor(
32
+ client: MongoClient,
33
+ operations: AnyClientBulkWriteModel[],
34
+ options?: ClientBulkWriteOptions
35
+ ) {
36
+ this.client = client;
37
+ this.operations = operations;
38
+ this.options = { ...options };
39
+
40
+ // If no write concern was provided, we inherit one from the client.
41
+ if (!this.options.writeConcern) {
42
+ this.options.writeConcern = WriteConcern.fromOptions(this.client.options);
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Execute the client bulk write. Will split commands into batches and exhaust the cursors
48
+ * for each, then merge the results into one.
49
+ * @returns The result.
50
+ */
51
+ async execute(): Promise<ClientBulkWriteResult | { ok: 1 }> {
52
+ // The command builder will take the user provided models and potential split the batch
53
+ // into multiple commands due to size.
54
+ const pkFactory = this.client.s.options.pkFactory;
55
+ const commandBuilder = new ClientBulkWriteCommandBuilder(
56
+ this.operations,
57
+ this.options,
58
+ pkFactory
59
+ );
60
+ const commands = commandBuilder.buildCommands();
61
+ if (this.options.writeConcern?.w === 0) {
62
+ return await executeUnacknowledged(this.client, this.options, commands);
63
+ }
64
+ return await executeAcknowledged(this.client, this.options, commands);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Execute an acknowledged bulk write.
70
+ */
71
+ async function executeAcknowledged(
72
+ client: MongoClient,
73
+ options: ClientBulkWriteOptions,
74
+ commands: ClientBulkWriteCommand[]
75
+ ): Promise<ClientBulkWriteResult> {
76
+ const resultsMerger = new ClientBulkWriteResultsMerger(options);
77
+ // For each command will will create and exhaust a cursor for the results.
78
+ for (const command of commands) {
79
+ const cursor = new ClientBulkWriteCursor(client, command, options);
80
+ const docs = await cursor.toArray();
81
+ resultsMerger.merge(command.ops.documents, cursor.response, docs);
82
+ }
83
+ return resultsMerger.result;
84
+ }
85
+
86
+ /**
87
+ * Execute an unacknowledged bulk write.
88
+ */
89
+ async function executeUnacknowledged(
90
+ client: MongoClient,
91
+ options: ClientBulkWriteOptions,
92
+ commands: Document[]
93
+ ): Promise<{ ok: 1 }> {
94
+ for (const command of commands) {
95
+ const operation = new ClientBulkWriteOperation(command, options);
96
+ await executeOperation(client, operation);
97
+ }
98
+ return { ok: 1 };
99
+ }
@@ -0,0 +1,95 @@
1
+ import { type Document } from '../../bson';
2
+ import { type ClientBulkWriteCursorResponse } from '../../cmap/wire_protocol/responses';
3
+ import {
4
+ type ClientBulkWriteOptions,
5
+ type ClientBulkWriteResult,
6
+ type ClientDeleteResult,
7
+ type ClientInsertOneResult,
8
+ type ClientUpdateResult
9
+ } from './common';
10
+
11
+ /**
12
+ * Merges client bulk write cursor responses together into a single result.
13
+ * @internal
14
+ */
15
+ export class ClientBulkWriteResultsMerger {
16
+ result: ClientBulkWriteResult;
17
+ options: ClientBulkWriteOptions;
18
+
19
+ /**
20
+ * Instantiate the merger.
21
+ * @param options - The options.
22
+ */
23
+ constructor(options: ClientBulkWriteOptions) {
24
+ this.options = options;
25
+ this.result = {
26
+ insertedCount: 0,
27
+ upsertedCount: 0,
28
+ matchedCount: 0,
29
+ modifiedCount: 0,
30
+ deletedCount: 0,
31
+ insertResults: undefined,
32
+ updateResults: undefined,
33
+ deleteResults: undefined
34
+ };
35
+
36
+ if (options.verboseResults) {
37
+ this.result.insertResults = new Map<number, ClientInsertOneResult>();
38
+ this.result.updateResults = new Map<number, ClientUpdateResult>();
39
+ this.result.deleteResults = new Map<number, ClientDeleteResult>();
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Merge the results in the cursor to the existing result.
45
+ * @param response - The cursor response.
46
+ * @param documents - The documents in the cursor.
47
+ * @returns The current result.
48
+ */
49
+ merge(
50
+ operations: Document[],
51
+ response: ClientBulkWriteCursorResponse,
52
+ documents: Document[]
53
+ ): ClientBulkWriteResult {
54
+ // Update the counts from the cursor response.
55
+ this.result.insertedCount += response.insertedCount;
56
+ this.result.upsertedCount += response.upsertedCount;
57
+ this.result.matchedCount += response.matchedCount;
58
+ this.result.modifiedCount += response.modifiedCount;
59
+ this.result.deletedCount += response.deletedCount;
60
+
61
+ if (this.options.verboseResults) {
62
+ // Iterate all the documents in the cursor and update the result.
63
+ for (const document of documents) {
64
+ // Only add to maps if ok: 1
65
+ if (document.ok === 1) {
66
+ // Get the corresponding operation from the command.
67
+ const operation = operations[document.idx];
68
+ // Handle insert results.
69
+ if ('insert' in operation) {
70
+ this.result.insertResults?.set(document.idx, { insertedId: operation.document._id });
71
+ }
72
+ // Handle update results.
73
+ if ('update' in operation) {
74
+ const result: ClientUpdateResult = {
75
+ matchedCount: document.n,
76
+ modifiedCount: document.nModified ?? 0,
77
+ // Check if the bulk did actually upsert.
78
+ didUpsert: document.upserted != null
79
+ };
80
+ if (document.upserted) {
81
+ result.upsertedId = document.upserted._id;
82
+ }
83
+ this.result.updateResults?.set(document.idx, result);
84
+ }
85
+ // Handle delete results.
86
+ if ('delete' in operation) {
87
+ this.result.deleteResults?.set(document.idx, { deletedCount: document.n });
88
+ }
89
+ }
90
+ }
91
+ }
92
+
93
+ return this.result;
94
+ }
95
+ }
@@ -58,7 +58,10 @@ interface CommandWriteConcernOptions {
58
58
  * @see https://www.mongodb.com/docs/manual/reference/write-concern/
59
59
  */
60
60
  export class WriteConcern {
61
- /** Request acknowledgment that the write operation has propagated to a specified number of mongod instances or to mongod instances with specified tags. */
61
+ /**
62
+ * Request acknowledgment that the write operation has propagated to a specified number of mongod instances or to mongod instances with specified tags.
63
+ * If w is 0 and is set on a write operation, the server will not send a response.
64
+ */
62
65
  readonly w?: W;
63
66
  /** Request acknowledgment that the write operation has been written to the on-disk journal */
64
67
  readonly journal?: boolean;