mongodb 6.9.0-dev.20240918.sha.643a8755 → 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.
- package/lib/beta.d.ts +243 -0
- package/lib/cmap/command_monitoring_events.js +10 -1
- package/lib/cmap/command_monitoring_events.js.map +1 -1
- package/lib/cmap/commands.js +4 -4
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/wire_protocol/responses.js +23 -1
- package/lib/cmap/wire_protocol/responses.js.map +1 -1
- package/lib/cursor/client_bulk_write_cursor.js +50 -0
- package/lib/cursor/client_bulk_write_cursor.js.map +1 -0
- package/lib/error.js +27 -1
- package/lib/error.js.map +1 -1
- package/lib/index.js +4 -3
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +10 -0
- package/lib/mongo_client.js.map +1 -1
- package/lib/operations/client_bulk_write/client_bulk_write.js +35 -0
- package/lib/operations/client_bulk_write/client_bulk_write.js.map +1 -0
- package/lib/operations/client_bulk_write/command_builder.js +20 -6
- package/lib/operations/client_bulk_write/command_builder.js.map +1 -1
- package/lib/operations/client_bulk_write/executor.js +71 -0
- package/lib/operations/client_bulk_write/executor.js.map +1 -0
- package/lib/operations/client_bulk_write/results_merger.js +79 -0
- package/lib/operations/client_bulk_write/results_merger.js.map +1 -0
- package/mongodb.d.ts +243 -0
- package/package.json +1 -1
- package/src/cmap/command_monitoring_events.ts +16 -2
- package/src/cmap/commands.ts +4 -4
- package/src/cmap/wire_protocol/responses.ts +26 -0
- package/src/cursor/client_bulk_write_cursor.ts +73 -0
- package/src/error.ts +27 -0
- package/src/index.ts +16 -0
- package/src/mongo_client.ts +19 -0
- package/src/operations/client_bulk_write/client_bulk_write.ts +45 -0
- package/src/operations/client_bulk_write/command_builder.ts +35 -6
- package/src/operations/client_bulk_write/common.ts +79 -0
- package/src/operations/client_bulk_write/executor.ts +99 -0
- package/src/operations/client_bulk_write/results_merger.ts +95 -0
|
@@ -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(
|
|
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(
|
|
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
|
+
}
|