mongodb 6.9.0 → 6.10.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.
Files changed (127) hide show
  1. package/lib/beta.d.ts +407 -12
  2. package/lib/bson.js +1 -0
  3. package/lib/bson.js.map +1 -1
  4. package/lib/bulk/common.js +60 -71
  5. package/lib/bulk/common.js.map +1 -1
  6. package/lib/bulk/unordered.js +3 -3
  7. package/lib/bulk/unordered.js.map +1 -1
  8. package/lib/change_stream.js +3 -2
  9. package/lib/change_stream.js.map +1 -1
  10. package/lib/cmap/auth/mongo_credentials.js +2 -7
  11. package/lib/cmap/auth/mongo_credentials.js.map +1 -1
  12. package/lib/cmap/auth/mongodb_oidc/callback_workflow.js.map +1 -1
  13. package/lib/cmap/auth/mongodb_oidc/command_builders.js +1 -1
  14. package/lib/cmap/auth/mongodb_oidc/command_builders.js.map +1 -1
  15. package/lib/cmap/auth/mongodb_oidc/human_callback_workflow.js +1 -1
  16. package/lib/cmap/auth/mongodb_oidc/human_callback_workflow.js.map +1 -1
  17. package/lib/cmap/auth/mongodb_oidc/machine_workflow.js.map +1 -1
  18. package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
  19. package/lib/cmap/command_monitoring_events.js +10 -1
  20. package/lib/cmap/command_monitoring_events.js.map +1 -1
  21. package/lib/cmap/commands.js +50 -18
  22. package/lib/cmap/commands.js.map +1 -1
  23. package/lib/cmap/connection.js +9 -2
  24. package/lib/cmap/connection.js.map +1 -1
  25. package/lib/cmap/wire_protocol/constants.js +2 -2
  26. package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -1
  27. package/lib/cmap/wire_protocol/responses.js +26 -1
  28. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  29. package/lib/connection_string.js +1 -7
  30. package/lib/connection_string.js.map +1 -1
  31. package/lib/cursor/aggregation_cursor.js.map +1 -1
  32. package/lib/cursor/client_bulk_write_cursor.js +52 -0
  33. package/lib/cursor/client_bulk_write_cursor.js.map +1 -0
  34. package/lib/cursor/find_cursor.js.map +1 -1
  35. package/lib/db.js +1 -1
  36. package/lib/error.js +76 -3
  37. package/lib/error.js.map +1 -1
  38. package/lib/explain.js +6 -6
  39. package/lib/explain.js.map +1 -1
  40. package/lib/index.js +6 -3
  41. package/lib/index.js.map +1 -1
  42. package/lib/mongo_client.js +14 -0
  43. package/lib/mongo_client.js.map +1 -1
  44. package/lib/mongo_client_auth_providers.js +6 -2
  45. package/lib/mongo_client_auth_providers.js.map +1 -1
  46. package/lib/mongo_types.js.map +1 -1
  47. package/lib/operations/aggregate.js.map +1 -1
  48. package/lib/operations/client_bulk_write/client_bulk_write.js +83 -0
  49. package/lib/operations/client_bulk_write/client_bulk_write.js.map +1 -0
  50. package/lib/operations/client_bulk_write/command_builder.js +154 -19
  51. package/lib/operations/client_bulk_write/command_builder.js.map +1 -1
  52. package/lib/operations/client_bulk_write/executor.js +109 -0
  53. package/lib/operations/client_bulk_write/executor.js.map +1 -0
  54. package/lib/operations/client_bulk_write/results_merger.js +204 -0
  55. package/lib/operations/client_bulk_write/results_merger.js.map +1 -0
  56. package/lib/operations/execute_operation.js +7 -0
  57. package/lib/operations/execute_operation.js.map +1 -1
  58. package/lib/operations/find.js.map +1 -1
  59. package/lib/operations/operation.js +5 -1
  60. package/lib/operations/operation.js.map +1 -1
  61. package/lib/operations/search_indexes/create.js.map +1 -1
  62. package/lib/operations/search_indexes/drop.js.map +1 -1
  63. package/lib/operations/search_indexes/update.js.map +1 -1
  64. package/lib/sdam/server.js +2 -1
  65. package/lib/sdam/server.js.map +1 -1
  66. package/lib/sdam/server_description.js +3 -0
  67. package/lib/sdam/server_description.js.map +1 -1
  68. package/lib/sdam/srv_polling.js +5 -1
  69. package/lib/sdam/srv_polling.js.map +1 -1
  70. package/lib/sdam/topology_description.js.map +1 -1
  71. package/lib/sessions.js +9 -2
  72. package/lib/sessions.js.map +1 -1
  73. package/lib/utils.js +25 -9
  74. package/lib/utils.js.map +1 -1
  75. package/lib/write_concern.js.map +1 -1
  76. package/mongodb.d.ts +407 -12
  77. package/package.json +1 -1
  78. package/src/beta.ts +1 -1
  79. package/src/bson.ts +3 -0
  80. package/src/bulk/common.ts +80 -120
  81. package/src/bulk/unordered.ts +3 -4
  82. package/src/change_stream.ts +5 -2
  83. package/src/cmap/auth/mongo_credentials.ts +2 -8
  84. package/src/cmap/auth/mongodb_oidc/callback_workflow.ts +1 -1
  85. package/src/cmap/auth/mongodb_oidc/command_builders.ts +1 -2
  86. package/src/cmap/auth/mongodb_oidc/human_callback_workflow.ts +1 -2
  87. package/src/cmap/auth/mongodb_oidc/machine_workflow.ts +1 -1
  88. package/src/cmap/auth/mongodb_oidc.ts +1 -2
  89. package/src/cmap/command_monitoring_events.ts +16 -2
  90. package/src/cmap/commands.ts +71 -25
  91. package/src/cmap/connection.ts +17 -4
  92. package/src/cmap/wire_protocol/constants.ts +2 -2
  93. package/src/cmap/wire_protocol/on_demand/document.ts +1 -2
  94. package/src/cmap/wire_protocol/responses.ts +31 -2
  95. package/src/connection_string.ts +2 -9
  96. package/src/cursor/aggregation_cursor.ts +2 -2
  97. package/src/cursor/client_bulk_write_cursor.ts +79 -0
  98. package/src/cursor/find_cursor.ts +2 -2
  99. package/src/db.ts +1 -1
  100. package/src/error.ts +98 -2
  101. package/src/explain.ts +47 -11
  102. package/src/index.ts +26 -1
  103. package/src/mongo_client.ts +29 -1
  104. package/src/mongo_client_auth_providers.ts +8 -2
  105. package/src/mongo_types.ts +2 -1
  106. package/src/operations/aggregate.ts +9 -1
  107. package/src/operations/client_bulk_write/client_bulk_write.ts +107 -0
  108. package/src/operations/client_bulk_write/command_builder.ts +216 -30
  109. package/src/operations/client_bulk_write/common.ts +148 -23
  110. package/src/operations/client_bulk_write/executor.ts +137 -0
  111. package/src/operations/client_bulk_write/results_merger.ts +260 -0
  112. package/src/operations/execute_operation.ts +8 -0
  113. package/src/operations/find.ts +8 -1
  114. package/src/operations/operation.ts +6 -1
  115. package/src/operations/search_indexes/create.ts +1 -2
  116. package/src/operations/search_indexes/drop.ts +1 -2
  117. package/src/operations/search_indexes/update.ts +1 -2
  118. package/src/sdam/server.ts +2 -1
  119. package/src/sdam/server_description.ts +9 -0
  120. package/src/sdam/srv_polling.ts +5 -2
  121. package/src/sdam/topology_description.ts +0 -1
  122. package/src/sessions.ts +16 -2
  123. package/src/utils.ts +40 -10
  124. package/src/write_concern.ts +4 -1
  125. package/lib/cmap/auth/mongocr.js +0 -35
  126. package/lib/cmap/auth/mongocr.js.map +0 -1
  127. package/src/cmap/auth/mongocr.ts +0 -38
@@ -27,25 +27,32 @@ export interface ClientBulkWriteOptions extends CommandOperationOptions {
27
27
 
28
28
  /** @public */
29
29
  export interface ClientWriteModel {
30
- /** The namespace for the write. */
30
+ /**
31
+ * The namespace for the write.
32
+ *
33
+ * A namespace is a combination of the database name and the name of the collection: `<database-name>.<collection>`.
34
+ * All documents belong to a namespace.
35
+ *
36
+ * @see https://www.mongodb.com/docs/manual/reference/limits/#std-label-faq-dev-namespace
37
+ */
31
38
  namespace: string;
32
39
  }
33
40
 
34
41
  /** @public */
35
- export interface ClientInsertOneModel extends ClientWriteModel {
42
+ export interface ClientInsertOneModel<TSchema> extends ClientWriteModel {
36
43
  name: 'insertOne';
37
44
  /** The document to insert. */
38
- document: OptionalId<Document>;
45
+ document: OptionalId<TSchema>;
39
46
  }
40
47
 
41
48
  /** @public */
42
- export interface ClientDeleteOneModel extends ClientWriteModel {
49
+ export interface ClientDeleteOneModel<TSchema> extends ClientWriteModel {
43
50
  name: 'deleteOne';
44
51
  /**
45
52
  * The filter used to determine if a document should be deleted.
46
53
  * For a deleteOne operation, the first match is removed.
47
54
  */
48
- filter: Filter<Document>;
55
+ filter: Filter<TSchema>;
49
56
  /** Specifies a collation. */
50
57
  collation?: CollationOptions;
51
58
  /** The index to use. If specified, then the query system will only consider plans using the hinted index. */
@@ -53,13 +60,13 @@ export interface ClientDeleteOneModel extends ClientWriteModel {
53
60
  }
54
61
 
55
62
  /** @public */
56
- export interface ClientDeleteManyModel extends ClientWriteModel {
63
+ export interface ClientDeleteManyModel<TSchema> extends ClientWriteModel {
57
64
  name: 'deleteMany';
58
65
  /**
59
66
  * The filter used to determine if a document should be deleted.
60
67
  * For a deleteMany operation, all matches are removed.
61
68
  */
62
- filter: Filter<Document>;
69
+ filter: Filter<TSchema>;
63
70
  /** Specifies a collation. */
64
71
  collation?: CollationOptions;
65
72
  /** The index to use. If specified, then the query system will only consider plans using the hinted index. */
@@ -67,15 +74,15 @@ export interface ClientDeleteManyModel extends ClientWriteModel {
67
74
  }
68
75
 
69
76
  /** @public */
70
- export interface ClientReplaceOneModel extends ClientWriteModel {
77
+ export interface ClientReplaceOneModel<TSchema> extends ClientWriteModel {
71
78
  name: 'replaceOne';
72
79
  /**
73
80
  * The filter used to determine if a document should be replaced.
74
81
  * For a replaceOne operation, the first match is replaced.
75
82
  */
76
- filter: Filter<Document>;
83
+ filter: Filter<TSchema>;
77
84
  /** The document with which to replace the matched document. */
78
- replacement: WithoutId<Document>;
85
+ replacement: WithoutId<TSchema>;
79
86
  /** Specifies a collation. */
80
87
  collation?: CollationOptions;
81
88
  /** The index to use. If specified, then the query system will only consider plans using the hinted index. */
@@ -85,19 +92,19 @@ export interface ClientReplaceOneModel extends ClientWriteModel {
85
92
  }
86
93
 
87
94
  /** @public */
88
- export interface ClientUpdateOneModel extends ClientWriteModel {
95
+ export interface ClientUpdateOneModel<TSchema> extends ClientWriteModel {
89
96
  name: 'updateOne';
90
97
  /**
91
98
  * The filter used to determine if a document should be updated.
92
99
  * For an updateOne operation, the first match is updated.
93
100
  */
94
- filter: Filter<Document>;
101
+ filter: Filter<TSchema>;
95
102
  /**
96
103
  * The modifications to apply. The value can be either:
97
104
  * UpdateFilter<Document> - A document that contains update operator expressions,
98
105
  * Document[] - an aggregation pipeline.
99
106
  */
100
- update: UpdateFilter<Document> | Document[];
107
+ update: UpdateFilter<TSchema> | Document[];
101
108
  /** A set of filters specifying to which array elements an update should apply. */
102
109
  arrayFilters?: Document[];
103
110
  /** Specifies a collation. */
@@ -109,19 +116,19 @@ export interface ClientUpdateOneModel extends ClientWriteModel {
109
116
  }
110
117
 
111
118
  /** @public */
112
- export interface ClientUpdateManyModel extends ClientWriteModel {
119
+ export interface ClientUpdateManyModel<TSchema> extends ClientWriteModel {
113
120
  name: 'updateMany';
114
121
  /**
115
122
  * The filter used to determine if a document should be updated.
116
123
  * For an updateMany operation, all matches are updated.
117
124
  */
118
- filter: Filter<Document>;
125
+ filter: Filter<TSchema>;
119
126
  /**
120
127
  * The modifications to apply. The value can be either:
121
128
  * UpdateFilter<Document> - A document that contains update operator expressions,
122
129
  * Document[] - an aggregation pipeline.
123
130
  */
124
- update: UpdateFilter<Document> | Document[];
131
+ update: UpdateFilter<TSchema> | Document[];
125
132
  /** A set of filters specifying to which array elements an update should apply. */
126
133
  arrayFilters?: Document[];
127
134
  /** Specifies a collation. */
@@ -137,10 +144,128 @@ export interface ClientUpdateManyModel extends ClientWriteModel {
137
144
  * to MongoClient#bulkWrite.
138
145
  * @public
139
146
  */
140
- export type AnyClientBulkWriteModel =
141
- | ClientInsertOneModel
142
- | ClientReplaceOneModel
143
- | ClientUpdateOneModel
144
- | ClientUpdateManyModel
145
- | ClientDeleteOneModel
146
- | ClientDeleteManyModel;
147
+ export type AnyClientBulkWriteModel<TSchema extends Document> =
148
+ | ClientInsertOneModel<TSchema>
149
+ | ClientReplaceOneModel<TSchema>
150
+ | ClientUpdateOneModel<TSchema>
151
+ | ClientUpdateManyModel<TSchema>
152
+ | ClientDeleteOneModel<TSchema>
153
+ | ClientDeleteManyModel<TSchema>;
154
+
155
+ /**
156
+ * A mapping of namespace strings to collections schemas.
157
+ * @public
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * type MongoDBSchemas = {
162
+ * 'db.books': Book;
163
+ * 'db.authors': Author;
164
+ * }
165
+ *
166
+ * const model: ClientBulkWriteModel<MongoDBSchemas> = {
167
+ * namespace: 'db.books'
168
+ * name: 'insertOne',
169
+ * document: { title: 'Practical MongoDB Aggregations', authorName: 3 } // error `authorName` cannot be number
170
+ * };
171
+ * ```
172
+ *
173
+ * The type of the `namespace` field narrows other parts of the BulkWriteModel to use the correct schema for type assertions.
174
+ *
175
+ */
176
+ export type ClientBulkWriteModel<
177
+ SchemaMap extends Record<string, Document> = Record<string, Document>
178
+ > = {
179
+ [Namespace in keyof SchemaMap]: AnyClientBulkWriteModel<SchemaMap[Namespace]> & {
180
+ namespace: Namespace;
181
+ };
182
+ }[keyof SchemaMap];
183
+
184
+ /** @public */
185
+ export interface ClientBulkWriteResult {
186
+ /**
187
+ * Whether the bulk write was acknowledged.
188
+ */
189
+ readonly acknowledged: boolean;
190
+ /**
191
+ * The total number of documents inserted across all insert operations.
192
+ */
193
+ readonly insertedCount: number;
194
+ /**
195
+ * The total number of documents upserted across all update operations.
196
+ */
197
+ readonly upsertedCount: number;
198
+ /**
199
+ * The total number of documents matched across all update operations.
200
+ */
201
+ readonly matchedCount: number;
202
+ /**
203
+ * The total number of documents modified across all update operations.
204
+ */
205
+ readonly modifiedCount: number;
206
+ /**
207
+ * The total number of documents deleted across all delete operations.
208
+ */
209
+ readonly deletedCount: number;
210
+ /**
211
+ * The results of each individual insert operation that was successfully performed.
212
+ */
213
+ readonly insertResults?: ReadonlyMap<number, ClientInsertOneResult>;
214
+ /**
215
+ * The results of each individual update operation that was successfully performed.
216
+ */
217
+ readonly updateResults?: ReadonlyMap<number, ClientUpdateResult>;
218
+ /**
219
+ * The results of each individual delete operation that was successfully performed.
220
+ */
221
+ readonly deleteResults?: ReadonlyMap<number, ClientDeleteResult>;
222
+ }
223
+
224
+ /** @public */
225
+ export interface ClientBulkWriteError {
226
+ code: number;
227
+ message: string;
228
+ }
229
+
230
+ /** @public */
231
+ export interface ClientInsertOneResult {
232
+ /**
233
+ * The _id of the inserted document.
234
+ */
235
+ insertedId: any;
236
+ }
237
+
238
+ /** @public */
239
+ export interface ClientUpdateResult {
240
+ /**
241
+ * The number of documents that matched the filter.
242
+ */
243
+ matchedCount: number;
244
+
245
+ /**
246
+ * The number of documents that were modified.
247
+ */
248
+ modifiedCount: number;
249
+
250
+ /**
251
+ * The _id field of the upserted document if an upsert occurred.
252
+ *
253
+ * It MUST be possible to discern between a BSON Null upserted ID value and this field being
254
+ * unset. If necessary, drivers MAY add a didUpsert boolean field to differentiate between
255
+ * these two cases.
256
+ */
257
+ upsertedId?: any;
258
+
259
+ /**
260
+ * Determines if the upsert did include an _id, which includes the case of the _id being null.
261
+ */
262
+ didUpsert: boolean;
263
+ }
264
+
265
+ /** @public */
266
+ export interface ClientDeleteResult {
267
+ /**
268
+ * The number of documents that were deleted.
269
+ */
270
+ deletedCount: number;
271
+ }
@@ -0,0 +1,137 @@
1
+ import { type Document } from '../../bson';
2
+ import { ClientBulkWriteCursor } from '../../cursor/client_bulk_write_cursor';
3
+ import {
4
+ MongoClientBulkWriteError,
5
+ MongoClientBulkWriteExecutionError,
6
+ MongoInvalidArgumentError,
7
+ MongoServerError
8
+ } from '../../error';
9
+ import { type MongoClient } from '../../mongo_client';
10
+ import { WriteConcern } from '../../write_concern';
11
+ import { executeOperation } from '../execute_operation';
12
+ import { ClientBulkWriteOperation } from './client_bulk_write';
13
+ import { ClientBulkWriteCommandBuilder } from './command_builder';
14
+ import {
15
+ type AnyClientBulkWriteModel,
16
+ type ClientBulkWriteOptions,
17
+ type ClientBulkWriteResult
18
+ } from './common';
19
+ import { ClientBulkWriteResultsMerger } from './results_merger';
20
+
21
+ /**
22
+ * Responsible for executing a client bulk write.
23
+ * @internal
24
+ */
25
+ export class ClientBulkWriteExecutor {
26
+ private readonly client: MongoClient;
27
+ private readonly options: ClientBulkWriteOptions;
28
+ private readonly operations: ReadonlyArray<AnyClientBulkWriteModel<Document>>;
29
+
30
+ /**
31
+ * Instantiate the executor.
32
+ * @param client - The mongo client.
33
+ * @param operations - The user supplied bulk write models.
34
+ * @param options - The bulk write options.
35
+ */
36
+ constructor(
37
+ client: MongoClient,
38
+ operations: ReadonlyArray<AnyClientBulkWriteModel<Document>>,
39
+ options?: ClientBulkWriteOptions
40
+ ) {
41
+ if (operations.length === 0) {
42
+ throw new MongoClientBulkWriteExecutionError('No client bulk write models were provided.');
43
+ }
44
+
45
+ this.client = client;
46
+ this.operations = operations;
47
+ this.options = {
48
+ ordered: true,
49
+ bypassDocumentValidation: false,
50
+ verboseResults: false,
51
+ ...options
52
+ };
53
+
54
+ // If no write concern was provided, we inherit one from the client.
55
+ if (!this.options.writeConcern) {
56
+ this.options.writeConcern = WriteConcern.fromOptions(this.client.options);
57
+ }
58
+
59
+ if (this.options.writeConcern?.w === 0) {
60
+ if (this.options.verboseResults) {
61
+ throw new MongoInvalidArgumentError(
62
+ 'Cannot request unacknowledged write concern and verbose results'
63
+ );
64
+ }
65
+
66
+ if (this.options.ordered) {
67
+ throw new MongoInvalidArgumentError(
68
+ 'Cannot request unacknowledged write concern and ordered writes'
69
+ );
70
+ }
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Execute the client bulk write. Will split commands into batches and exhaust the cursors
76
+ * for each, then merge the results into one.
77
+ * @returns The result.
78
+ */
79
+ async execute(): Promise<ClientBulkWriteResult> {
80
+ // The command builder will take the user provided models and potential split the batch
81
+ // into multiple commands due to size.
82
+ const pkFactory = this.client.s.options.pkFactory;
83
+ const commandBuilder = new ClientBulkWriteCommandBuilder(
84
+ this.operations,
85
+ this.options,
86
+ pkFactory
87
+ );
88
+ // Unacknowledged writes need to execute all batches and return { ok: 1}
89
+ if (this.options.writeConcern?.w === 0) {
90
+ while (commandBuilder.hasNextBatch()) {
91
+ const operation = new ClientBulkWriteOperation(commandBuilder, this.options);
92
+ await executeOperation(this.client, operation);
93
+ }
94
+ return ClientBulkWriteResultsMerger.unacknowledged();
95
+ } else {
96
+ const resultsMerger = new ClientBulkWriteResultsMerger(this.options);
97
+ // For each command will will create and exhaust a cursor for the results.
98
+ while (commandBuilder.hasNextBatch()) {
99
+ const cursor = new ClientBulkWriteCursor(this.client, commandBuilder, this.options);
100
+ try {
101
+ await resultsMerger.merge(cursor);
102
+ } catch (error) {
103
+ // Write concern errors are recorded in the writeConcernErrors field on MongoClientBulkWriteError.
104
+ // When a write concern error is encountered, it should not terminate execution of the bulk write
105
+ // for either ordered or unordered bulk writes. However, drivers MUST throw an exception at the end
106
+ // of execution if any write concern errors were observed.
107
+ if (error instanceof MongoServerError && !(error instanceof MongoClientBulkWriteError)) {
108
+ // Server side errors need to be wrapped inside a MongoClientBulkWriteError, where the root
109
+ // cause is the error property and a partial result is to be included.
110
+ const bulkWriteError = new MongoClientBulkWriteError({
111
+ message: 'Mongo client bulk write encountered an error during execution'
112
+ });
113
+ bulkWriteError.cause = error;
114
+ bulkWriteError.partialResult = resultsMerger.bulkWriteResult;
115
+ throw bulkWriteError;
116
+ } else {
117
+ // Client side errors are just thrown.
118
+ throw error;
119
+ }
120
+ }
121
+ }
122
+
123
+ // If we have write concern errors or unordered write errors at the end we throw.
124
+ if (resultsMerger.writeConcernErrors.length > 0 || resultsMerger.writeErrors.size > 0) {
125
+ const error = new MongoClientBulkWriteError({
126
+ message: 'Mongo client bulk write encountered errors during execution.'
127
+ });
128
+ error.writeConcernErrors = resultsMerger.writeConcernErrors;
129
+ error.writeErrors = resultsMerger.writeErrors;
130
+ error.partialResult = resultsMerger.bulkWriteResult;
131
+ throw error;
132
+ }
133
+
134
+ return resultsMerger.bulkWriteResult;
135
+ }
136
+ }
137
+ }
@@ -0,0 +1,260 @@
1
+ import { MongoWriteConcernError } from '../..';
2
+ import { type Document } from '../../bson';
3
+ import { type ClientBulkWriteCursor } from '../../cursor/client_bulk_write_cursor';
4
+ import { MongoClientBulkWriteError } from '../../error';
5
+ import {
6
+ type ClientBulkWriteError,
7
+ type ClientBulkWriteOptions,
8
+ type ClientBulkWriteResult,
9
+ type ClientDeleteResult,
10
+ type ClientInsertOneResult,
11
+ type ClientUpdateResult
12
+ } from './common';
13
+
14
+ /**
15
+ * Unacknowledged bulk writes are always the same.
16
+ */
17
+ const UNACKNOWLEDGED = {
18
+ acknowledged: false,
19
+ insertedCount: 0,
20
+ upsertedCount: 0,
21
+ matchedCount: 0,
22
+ modifiedCount: 0,
23
+ deletedCount: 0,
24
+ insertResults: undefined,
25
+ updateResults: undefined,
26
+ deleteResults: undefined
27
+ };
28
+
29
+ interface ClientBulkWriteResultAccumulation {
30
+ /**
31
+ * Whether the bulk write was acknowledged.
32
+ */
33
+ acknowledged: boolean;
34
+ /**
35
+ * The total number of documents inserted across all insert operations.
36
+ */
37
+ insertedCount: number;
38
+ /**
39
+ * The total number of documents upserted across all update operations.
40
+ */
41
+ upsertedCount: number;
42
+ /**
43
+ * The total number of documents matched across all update operations.
44
+ */
45
+ matchedCount: number;
46
+ /**
47
+ * The total number of documents modified across all update operations.
48
+ */
49
+ modifiedCount: number;
50
+ /**
51
+ * The total number of documents deleted across all delete operations.
52
+ */
53
+ deletedCount: number;
54
+ /**
55
+ * The results of each individual insert operation that was successfully performed.
56
+ */
57
+ insertResults?: Map<number, ClientInsertOneResult>;
58
+ /**
59
+ * The results of each individual update operation that was successfully performed.
60
+ */
61
+ updateResults?: Map<number, ClientUpdateResult>;
62
+ /**
63
+ * The results of each individual delete operation that was successfully performed.
64
+ */
65
+ deleteResults?: Map<number, ClientDeleteResult>;
66
+ }
67
+
68
+ /**
69
+ * Merges client bulk write cursor responses together into a single result.
70
+ * @internal
71
+ */
72
+ export class ClientBulkWriteResultsMerger {
73
+ private result: ClientBulkWriteResultAccumulation;
74
+ private options: ClientBulkWriteOptions;
75
+ private currentBatchOffset: number;
76
+ writeConcernErrors: Document[];
77
+ writeErrors: Map<number, ClientBulkWriteError>;
78
+
79
+ /**
80
+ * @returns The standard unacknowledged bulk write result.
81
+ */
82
+ static unacknowledged(): ClientBulkWriteResult {
83
+ return UNACKNOWLEDGED;
84
+ }
85
+
86
+ /**
87
+ * Instantiate the merger.
88
+ * @param options - The options.
89
+ */
90
+ constructor(options: ClientBulkWriteOptions) {
91
+ this.options = options;
92
+ this.currentBatchOffset = 0;
93
+ this.writeConcernErrors = [];
94
+ this.writeErrors = new Map();
95
+ this.result = {
96
+ acknowledged: true,
97
+ insertedCount: 0,
98
+ upsertedCount: 0,
99
+ matchedCount: 0,
100
+ modifiedCount: 0,
101
+ deletedCount: 0,
102
+ insertResults: undefined,
103
+ updateResults: undefined,
104
+ deleteResults: undefined
105
+ };
106
+
107
+ if (options.verboseResults) {
108
+ this.result.insertResults = new Map<number, ClientInsertOneResult>();
109
+ this.result.updateResults = new Map<number, ClientUpdateResult>();
110
+ this.result.deleteResults = new Map<number, ClientDeleteResult>();
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Get the bulk write result object.
116
+ */
117
+ get bulkWriteResult(): ClientBulkWriteResult {
118
+ return {
119
+ acknowledged: this.result.acknowledged,
120
+ insertedCount: this.result.insertedCount,
121
+ upsertedCount: this.result.upsertedCount,
122
+ matchedCount: this.result.matchedCount,
123
+ modifiedCount: this.result.modifiedCount,
124
+ deletedCount: this.result.deletedCount,
125
+ insertResults: this.result.insertResults,
126
+ updateResults: this.result.updateResults,
127
+ deleteResults: this.result.deleteResults
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Merge the results in the cursor to the existing result.
133
+ * @param currentBatchOffset - The offset index to the original models.
134
+ * @param response - The cursor response.
135
+ * @param documents - The documents in the cursor.
136
+ * @returns The current result.
137
+ */
138
+ async merge(cursor: ClientBulkWriteCursor): Promise<ClientBulkWriteResult> {
139
+ let writeConcernErrorResult;
140
+ try {
141
+ for await (const document of cursor) {
142
+ // Only add to maps if ok: 1
143
+ if (document.ok === 1) {
144
+ if (this.options.verboseResults) {
145
+ this.processDocument(cursor, document);
146
+ }
147
+ } else {
148
+ // If an individual write error is encountered during an ordered bulk write, drivers MUST
149
+ // record the error in writeErrors and immediately throw the exception. Otherwise, drivers
150
+ // MUST continue to iterate the results cursor and execute any further bulkWrite batches.
151
+ if (this.options.ordered) {
152
+ const error = new MongoClientBulkWriteError({
153
+ message: 'Mongo client ordered bulk write encountered a write error.'
154
+ });
155
+ error.writeErrors.set(document.idx + this.currentBatchOffset, {
156
+ code: document.code,
157
+ message: document.errmsg
158
+ });
159
+ error.partialResult = this.result;
160
+ throw error;
161
+ } else {
162
+ this.writeErrors.set(document.idx + this.currentBatchOffset, {
163
+ code: document.code,
164
+ message: document.errmsg
165
+ });
166
+ }
167
+ }
168
+ }
169
+ } catch (error) {
170
+ if (error instanceof MongoWriteConcernError) {
171
+ const result = error.result;
172
+ writeConcernErrorResult = {
173
+ insertedCount: result.nInserted,
174
+ upsertedCount: result.nUpserted,
175
+ matchedCount: result.nMatched,
176
+ modifiedCount: result.nModified,
177
+ deletedCount: result.nDeleted,
178
+ writeConcernError: result.writeConcernError
179
+ };
180
+ if (this.options.verboseResults && result.cursor.firstBatch) {
181
+ for (const document of result.cursor.firstBatch) {
182
+ if (document.ok === 1) {
183
+ this.processDocument(cursor, document);
184
+ }
185
+ }
186
+ }
187
+ } else {
188
+ throw error;
189
+ }
190
+ } finally {
191
+ // Update the counts from the cursor response.
192
+ if (cursor.response) {
193
+ const response = cursor.response;
194
+ this.incrementCounts(response);
195
+ }
196
+
197
+ // Increment the batch offset.
198
+ this.currentBatchOffset += cursor.operations.length;
199
+ }
200
+
201
+ // If we have write concern errors ensure they are added.
202
+ if (writeConcernErrorResult) {
203
+ const writeConcernError = writeConcernErrorResult.writeConcernError as Document;
204
+ this.incrementCounts(writeConcernErrorResult);
205
+ this.writeConcernErrors.push({
206
+ code: writeConcernError.code,
207
+ message: writeConcernError.errmsg
208
+ });
209
+ }
210
+
211
+ return this.result;
212
+ }
213
+
214
+ /**
215
+ * Process an individual document in the results.
216
+ * @param cursor - The cursor.
217
+ * @param document - The document to process.
218
+ */
219
+ private processDocument(cursor: ClientBulkWriteCursor, document: Document) {
220
+ // Get the corresponding operation from the command.
221
+ const operation = cursor.operations[document.idx];
222
+ // Handle insert results.
223
+ if ('insert' in operation) {
224
+ this.result.insertResults?.set(document.idx + this.currentBatchOffset, {
225
+ insertedId: operation.document._id
226
+ });
227
+ }
228
+ // Handle update results.
229
+ if ('update' in operation) {
230
+ const result: ClientUpdateResult = {
231
+ matchedCount: document.n,
232
+ modifiedCount: document.nModified ?? 0,
233
+ // Check if the bulk did actually upsert.
234
+ didUpsert: document.upserted != null
235
+ };
236
+ if (document.upserted) {
237
+ result.upsertedId = document.upserted._id;
238
+ }
239
+ this.result.updateResults?.set(document.idx + this.currentBatchOffset, result);
240
+ }
241
+ // Handle delete results.
242
+ if ('delete' in operation) {
243
+ this.result.deleteResults?.set(document.idx + this.currentBatchOffset, {
244
+ deletedCount: document.n
245
+ });
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Increment the result counts.
251
+ * @param document - The document with the results.
252
+ */
253
+ private incrementCounts(document: Document) {
254
+ this.result.insertedCount += document.insertedCount;
255
+ this.result.upsertedCount += document.upsertedCount;
256
+ this.result.matchedCount += document.matchedCount;
257
+ this.result.modifiedCount += document.modifiedCount;
258
+ this.result.deletedCount += document.deletedCount;
259
+ }
260
+ }
@@ -230,6 +230,10 @@ async function tryOperation<
230
230
  });
231
231
  }
232
232
 
233
+ if (operation.hasAspect(Aspect.COMMAND_BATCHING) && !operation.canRetryWrite) {
234
+ throw previousOperationError;
235
+ }
236
+
233
237
  if (hasWriteAspect && !isRetryableWriteError(previousOperationError))
234
238
  throw previousOperationError;
235
239
 
@@ -260,6 +264,10 @@ async function tryOperation<
260
264
  }
261
265
 
262
266
  try {
267
+ // If tries > 0 and we are command batching we need to reset the batch.
268
+ if (tries > 0 && operation.hasAspect(Aspect.COMMAND_BATCHING)) {
269
+ operation.resetBatch();
270
+ }
263
271
  return await operation.execute(server, session);
264
272
  } catch (operationError) {
265
273
  if (!(operationError instanceof MongoError)) throw operationError;
@@ -1,6 +1,7 @@
1
1
  import type { Document } from '../bson';
2
2
  import { CursorResponse, ExplainedCursorResponse } from '../cmap/wire_protocol/responses';
3
3
  import { MongoInvalidArgumentError } from '../error';
4
+ import { type ExplainOptions } from '../explain';
4
5
  import { ReadConcern } from '../read_concern';
5
6
  import type { Server } from '../sdam/server';
6
7
  import type { ClientSession } from '../sessions';
@@ -15,7 +16,7 @@ import { Aspect, defineAspects, type Hint } from './operation';
15
16
  */
16
17
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
17
18
  export interface FindOptions<TSchema extends Document = Document>
18
- extends Omit<CommandOperationOptions, 'writeConcern'> {
19
+ extends Omit<CommandOperationOptions, 'writeConcern' | 'explain'> {
19
20
  /** Sets the limit of documents returned in the query. */
20
21
  limit?: number;
21
22
  /** Set to sort the documents coming back from the query. Array of indexes, `[['a', 1]]` etc. */
@@ -63,6 +64,12 @@ export interface FindOptions<TSchema extends Document = Document>
63
64
  * @deprecated Starting from MongoDB 4.4 this flag is not needed and will be ignored.
64
65
  */
65
66
  oplogReplay?: boolean;
67
+
68
+ /**
69
+ * Specifies the verbosity mode for the explain output.
70
+ * @deprecated This API is deprecated in favor of `collection.find().explain()`.
71
+ */
72
+ explain?: ExplainOptions['explain'];
66
73
  }
67
74
 
68
75
  /** @internal */