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.
- package/lib/beta.d.ts +407 -12
- package/lib/bson.js +1 -0
- package/lib/bson.js.map +1 -1
- package/lib/bulk/common.js +60 -71
- package/lib/bulk/common.js.map +1 -1
- package/lib/bulk/unordered.js +3 -3
- package/lib/bulk/unordered.js.map +1 -1
- package/lib/change_stream.js +3 -2
- package/lib/change_stream.js.map +1 -1
- package/lib/cmap/auth/mongo_credentials.js +2 -7
- package/lib/cmap/auth/mongo_credentials.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/callback_workflow.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/command_builders.js +1 -1
- package/lib/cmap/auth/mongodb_oidc/command_builders.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/human_callback_workflow.js +1 -1
- package/lib/cmap/auth/mongodb_oidc/human_callback_workflow.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/machine_workflow.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
- package/lib/cmap/command_monitoring_events.js +10 -1
- package/lib/cmap/command_monitoring_events.js.map +1 -1
- package/lib/cmap/commands.js +50 -18
- package/lib/cmap/commands.js.map +1 -1
- package/lib/cmap/connection.js +9 -2
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/wire_protocol/constants.js +2 -2
- package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -1
- package/lib/cmap/wire_protocol/responses.js +26 -1
- package/lib/cmap/wire_protocol/responses.js.map +1 -1
- package/lib/connection_string.js +1 -7
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/client_bulk_write_cursor.js +52 -0
- package/lib/cursor/client_bulk_write_cursor.js.map +1 -0
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/db.js +1 -1
- package/lib/error.js +76 -3
- package/lib/error.js.map +1 -1
- package/lib/explain.js +6 -6
- package/lib/explain.js.map +1 -1
- package/lib/index.js +6 -3
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +14 -0
- package/lib/mongo_client.js.map +1 -1
- package/lib/mongo_client_auth_providers.js +6 -2
- package/lib/mongo_client_auth_providers.js.map +1 -1
- package/lib/mongo_types.js.map +1 -1
- package/lib/operations/aggregate.js.map +1 -1
- package/lib/operations/client_bulk_write/client_bulk_write.js +83 -0
- package/lib/operations/client_bulk_write/client_bulk_write.js.map +1 -0
- package/lib/operations/client_bulk_write/command_builder.js +154 -19
- package/lib/operations/client_bulk_write/command_builder.js.map +1 -1
- package/lib/operations/client_bulk_write/executor.js +109 -0
- package/lib/operations/client_bulk_write/executor.js.map +1 -0
- package/lib/operations/client_bulk_write/results_merger.js +204 -0
- package/lib/operations/client_bulk_write/results_merger.js.map +1 -0
- package/lib/operations/execute_operation.js +7 -0
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/operation.js +5 -1
- package/lib/operations/operation.js.map +1 -1
- package/lib/operations/search_indexes/create.js.map +1 -1
- package/lib/operations/search_indexes/drop.js.map +1 -1
- package/lib/operations/search_indexes/update.js.map +1 -1
- package/lib/sdam/server.js +2 -1
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/server_description.js +3 -0
- package/lib/sdam/server_description.js.map +1 -1
- package/lib/sdam/srv_polling.js +5 -1
- package/lib/sdam/srv_polling.js.map +1 -1
- package/lib/sdam/topology_description.js.map +1 -1
- package/lib/sessions.js +9 -2
- package/lib/sessions.js.map +1 -1
- package/lib/utils.js +25 -9
- package/lib/utils.js.map +1 -1
- package/lib/write_concern.js.map +1 -1
- package/mongodb.d.ts +407 -12
- package/package.json +1 -1
- package/src/beta.ts +1 -1
- package/src/bson.ts +3 -0
- package/src/bulk/common.ts +80 -120
- package/src/bulk/unordered.ts +3 -4
- package/src/change_stream.ts +5 -2
- package/src/cmap/auth/mongo_credentials.ts +2 -8
- package/src/cmap/auth/mongodb_oidc/callback_workflow.ts +1 -1
- package/src/cmap/auth/mongodb_oidc/command_builders.ts +1 -2
- package/src/cmap/auth/mongodb_oidc/human_callback_workflow.ts +1 -2
- package/src/cmap/auth/mongodb_oidc/machine_workflow.ts +1 -1
- package/src/cmap/auth/mongodb_oidc.ts +1 -2
- package/src/cmap/command_monitoring_events.ts +16 -2
- package/src/cmap/commands.ts +71 -25
- package/src/cmap/connection.ts +17 -4
- package/src/cmap/wire_protocol/constants.ts +2 -2
- package/src/cmap/wire_protocol/on_demand/document.ts +1 -2
- package/src/cmap/wire_protocol/responses.ts +31 -2
- package/src/connection_string.ts +2 -9
- package/src/cursor/aggregation_cursor.ts +2 -2
- package/src/cursor/client_bulk_write_cursor.ts +79 -0
- package/src/cursor/find_cursor.ts +2 -2
- package/src/db.ts +1 -1
- package/src/error.ts +98 -2
- package/src/explain.ts +47 -11
- package/src/index.ts +26 -1
- package/src/mongo_client.ts +29 -1
- package/src/mongo_client_auth_providers.ts +8 -2
- package/src/mongo_types.ts +2 -1
- package/src/operations/aggregate.ts +9 -1
- package/src/operations/client_bulk_write/client_bulk_write.ts +107 -0
- package/src/operations/client_bulk_write/command_builder.ts +216 -30
- package/src/operations/client_bulk_write/common.ts +148 -23
- package/src/operations/client_bulk_write/executor.ts +137 -0
- package/src/operations/client_bulk_write/results_merger.ts +260 -0
- package/src/operations/execute_operation.ts +8 -0
- package/src/operations/find.ts +8 -1
- package/src/operations/operation.ts +6 -1
- package/src/operations/search_indexes/create.ts +1 -2
- package/src/operations/search_indexes/drop.ts +1 -2
- package/src/operations/search_indexes/update.ts +1 -2
- package/src/sdam/server.ts +2 -1
- package/src/sdam/server_description.ts +9 -0
- package/src/sdam/srv_polling.ts +5 -2
- package/src/sdam/topology_description.ts +0 -1
- package/src/sessions.ts +16 -2
- package/src/utils.ts +40 -10
- package/src/write_concern.ts +4 -1
- package/lib/cmap/auth/mongocr.js +0 -35
- package/lib/cmap/auth/mongocr.js.map +0 -1
- package/src/cmap/auth/mongocr.ts +0 -38
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { MongoClientBulkWriteExecutionError, ServerType } from '../../beta';
|
|
2
|
+
import { ClientBulkWriteCursorResponse } from '../../cmap/wire_protocol/responses';
|
|
3
|
+
import type { Server } from '../../sdam/server';
|
|
4
|
+
import type { ClientSession } from '../../sessions';
|
|
5
|
+
import { MongoDBNamespace } from '../../utils';
|
|
6
|
+
import { CommandOperation } from '../command';
|
|
7
|
+
import { Aspect, defineAspects } from '../operation';
|
|
8
|
+
import { type ClientBulkWriteCommandBuilder } from './command_builder';
|
|
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
|
+
commandBuilder: ClientBulkWriteCommandBuilder;
|
|
17
|
+
override options: ClientBulkWriteOptions;
|
|
18
|
+
|
|
19
|
+
override get commandName() {
|
|
20
|
+
return 'bulkWrite' as const;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
constructor(commandBuilder: ClientBulkWriteCommandBuilder, options: ClientBulkWriteOptions) {
|
|
24
|
+
super(undefined, options);
|
|
25
|
+
this.commandBuilder = commandBuilder;
|
|
26
|
+
this.options = options;
|
|
27
|
+
this.ns = new MongoDBNamespace('admin', '$cmd');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
override resetBatch(): boolean {
|
|
31
|
+
return this.commandBuilder.resetBatch();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override get canRetryWrite(): boolean {
|
|
35
|
+
return this.commandBuilder.isBatchRetryable;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Execute the command. Superclass will handle write concern, etc.
|
|
40
|
+
* @param server - The server.
|
|
41
|
+
* @param session - The session.
|
|
42
|
+
* @returns The response.
|
|
43
|
+
*/
|
|
44
|
+
override async execute(
|
|
45
|
+
server: Server,
|
|
46
|
+
session: ClientSession | undefined
|
|
47
|
+
): Promise<ClientBulkWriteCursorResponse> {
|
|
48
|
+
let command;
|
|
49
|
+
|
|
50
|
+
if (server.description.type === ServerType.LoadBalancer) {
|
|
51
|
+
if (session) {
|
|
52
|
+
let connection;
|
|
53
|
+
if (!session.pinnedConnection) {
|
|
54
|
+
// Checkout a connection to build the command.
|
|
55
|
+
connection = await server.pool.checkOut();
|
|
56
|
+
// Pin the connection to the session so it get used to execute the command and we do not
|
|
57
|
+
// perform a double check-in/check-out.
|
|
58
|
+
session.pin(connection);
|
|
59
|
+
} else {
|
|
60
|
+
connection = session.pinnedConnection;
|
|
61
|
+
}
|
|
62
|
+
command = this.commandBuilder.buildBatch(
|
|
63
|
+
connection.hello?.maxMessageSizeBytes,
|
|
64
|
+
connection.hello?.maxWriteBatchSize,
|
|
65
|
+
connection.hello?.maxBsonObjectSize
|
|
66
|
+
);
|
|
67
|
+
} else {
|
|
68
|
+
throw new MongoClientBulkWriteExecutionError(
|
|
69
|
+
'Session provided to the client bulk write operation must be present.'
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
// At this point we have a server and the auto connect code has already
|
|
74
|
+
// run in executeOperation, so the server description will be populated.
|
|
75
|
+
// We can use that to build the command.
|
|
76
|
+
if (
|
|
77
|
+
!server.description.maxWriteBatchSize ||
|
|
78
|
+
!server.description.maxMessageSizeBytes ||
|
|
79
|
+
!server.description.maxBsonObjectSize
|
|
80
|
+
) {
|
|
81
|
+
throw new MongoClientBulkWriteExecutionError(
|
|
82
|
+
'In order to execute a client bulk write, both maxWriteBatchSize, maxMessageSizeBytes and maxBsonObjectSize must be provided by the servers hello response.'
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
command = this.commandBuilder.buildBatch(
|
|
86
|
+
server.description.maxMessageSizeBytes,
|
|
87
|
+
server.description.maxWriteBatchSize,
|
|
88
|
+
server.description.maxBsonObjectSize
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check after the batch is built if we cannot retry it and override the option.
|
|
93
|
+
if (!this.canRetryWrite) {
|
|
94
|
+
this.options.willRetryWrite = false;
|
|
95
|
+
}
|
|
96
|
+
return await super.executeCommand(server, session, command, ClientBulkWriteCursorResponse);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Skipping the collation as it goes on the individual ops.
|
|
101
|
+
defineAspects(ClientBulkWriteOperation, [
|
|
102
|
+
Aspect.WRITE_OPERATION,
|
|
103
|
+
Aspect.SKIP_COLLATION,
|
|
104
|
+
Aspect.CURSOR_CREATING,
|
|
105
|
+
Aspect.RETRYABLE,
|
|
106
|
+
Aspect.COMMAND_BATCHING
|
|
107
|
+
]);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { type Document } from '../../bson';
|
|
1
|
+
import { BSON, type Document } from '../../bson';
|
|
2
2
|
import { DocumentSequence } from '../../cmap/commands';
|
|
3
|
+
import { MongoAPIError, MongoInvalidArgumentError } from '../../error';
|
|
4
|
+
import { type PkFactory } from '../../mongo_client';
|
|
3
5
|
import type { Filter, OptionalId, UpdateFilter, WithoutId } from '../../mongo_types';
|
|
6
|
+
import { DEFAULT_PK_FACTORY, hasAtomicOperators } from '../../utils';
|
|
4
7
|
import { type CollationOptions } from '../command';
|
|
5
8
|
import { type Hint } from '../operation';
|
|
6
9
|
import type {
|
|
@@ -23,20 +26,44 @@ export interface ClientBulkWriteCommand {
|
|
|
23
26
|
nsInfo: DocumentSequence;
|
|
24
27
|
bypassDocumentValidation?: boolean;
|
|
25
28
|
let?: Document;
|
|
29
|
+
comment?: any;
|
|
26
30
|
}
|
|
27
31
|
|
|
32
|
+
/**
|
|
33
|
+
* The bytes overhead for the extra fields added post command generation.
|
|
34
|
+
*/
|
|
35
|
+
const MESSAGE_OVERHEAD_BYTES = 1000;
|
|
36
|
+
|
|
28
37
|
/** @internal */
|
|
29
38
|
export class ClientBulkWriteCommandBuilder {
|
|
30
|
-
models: AnyClientBulkWriteModel
|
|
39
|
+
models: ReadonlyArray<AnyClientBulkWriteModel<Document>>;
|
|
31
40
|
options: ClientBulkWriteOptions;
|
|
41
|
+
pkFactory: PkFactory;
|
|
42
|
+
/** The current index in the models array that is being processed. */
|
|
43
|
+
currentModelIndex: number;
|
|
44
|
+
/** The model index that the builder was on when it finished the previous batch. Used for resets when retrying. */
|
|
45
|
+
previousModelIndex: number;
|
|
46
|
+
/** The last array of operations that were created. Used by the results merger for indexing results. */
|
|
47
|
+
lastOperations: Document[];
|
|
48
|
+
/** Returns true if the current batch being created has no multi-updates. */
|
|
49
|
+
isBatchRetryable: boolean;
|
|
32
50
|
|
|
33
51
|
/**
|
|
34
52
|
* Create the command builder.
|
|
35
53
|
* @param models - The client write models.
|
|
36
54
|
*/
|
|
37
|
-
constructor(
|
|
55
|
+
constructor(
|
|
56
|
+
models: ReadonlyArray<AnyClientBulkWriteModel<Document>>,
|
|
57
|
+
options: ClientBulkWriteOptions,
|
|
58
|
+
pkFactory?: PkFactory
|
|
59
|
+
) {
|
|
38
60
|
this.models = models;
|
|
39
61
|
this.options = options;
|
|
62
|
+
this.pkFactory = pkFactory ?? DEFAULT_PK_FACTORY;
|
|
63
|
+
this.currentModelIndex = 0;
|
|
64
|
+
this.previousModelIndex = 0;
|
|
65
|
+
this.lastOperations = [];
|
|
66
|
+
this.isBatchRetryable = true;
|
|
40
67
|
}
|
|
41
68
|
|
|
42
69
|
/**
|
|
@@ -51,34 +78,135 @@ export class ClientBulkWriteCommandBuilder {
|
|
|
51
78
|
}
|
|
52
79
|
|
|
53
80
|
/**
|
|
54
|
-
*
|
|
81
|
+
* Determines if there is another batch to process.
|
|
82
|
+
* @returns True if not all batches have been built.
|
|
83
|
+
*/
|
|
84
|
+
hasNextBatch(): boolean {
|
|
85
|
+
return this.currentModelIndex < this.models.length;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* When we need to retry a command we need to set the current
|
|
90
|
+
* model index back to its previous value.
|
|
91
|
+
*/
|
|
92
|
+
resetBatch(): boolean {
|
|
93
|
+
this.currentModelIndex = this.previousModelIndex;
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Build a single batch of a client bulk write command.
|
|
99
|
+
* @param maxMessageSizeBytes - The max message size in bytes.
|
|
100
|
+
* @param maxWriteBatchSize - The max write batch size.
|
|
101
|
+
* @returns The client bulk write command.
|
|
55
102
|
*/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
103
|
+
buildBatch(
|
|
104
|
+
maxMessageSizeBytes: number,
|
|
105
|
+
maxWriteBatchSize: number,
|
|
106
|
+
maxBsonObjectSize: number
|
|
107
|
+
): ClientBulkWriteCommand {
|
|
108
|
+
// We start by assuming the batch has no multi-updates, so it is retryable
|
|
109
|
+
// until we find them.
|
|
110
|
+
this.isBatchRetryable = true;
|
|
111
|
+
let commandLength = 0;
|
|
59
112
|
let currentNamespaceIndex = 0;
|
|
113
|
+
const command: ClientBulkWriteCommand = this.baseCommand();
|
|
60
114
|
const namespaces = new Map<string, number>();
|
|
61
|
-
|
|
115
|
+
// In the case of retries we need to mark where we started this batch.
|
|
116
|
+
this.previousModelIndex = this.currentModelIndex;
|
|
117
|
+
|
|
118
|
+
while (this.currentModelIndex < this.models.length) {
|
|
119
|
+
const model = this.models[this.currentModelIndex];
|
|
62
120
|
const ns = model.namespace;
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
121
|
+
const nsIndex = namespaces.get(ns);
|
|
122
|
+
|
|
123
|
+
// Multi updates are not retryable.
|
|
124
|
+
if (model.name === 'deleteMany' || model.name === 'updateMany') {
|
|
125
|
+
this.isBatchRetryable = false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (nsIndex != null) {
|
|
129
|
+
// Build the operation and serialize it to get the bytes buffer.
|
|
130
|
+
const operation = buildOperation(model, nsIndex, this.pkFactory);
|
|
131
|
+
let operationBuffer;
|
|
132
|
+
try {
|
|
133
|
+
operationBuffer = BSON.serialize(operation);
|
|
134
|
+
} catch (cause) {
|
|
135
|
+
throw new MongoInvalidArgumentError(`Could not serialize operation to BSON`, { cause });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
validateBufferSize('ops', operationBuffer, maxBsonObjectSize);
|
|
139
|
+
|
|
140
|
+
// Check if the operation buffer can fit in the command. If it can,
|
|
141
|
+
// then add the operation to the document sequence and increment the
|
|
142
|
+
// current length as long as the ops don't exceed the maxWriteBatchSize.
|
|
143
|
+
if (
|
|
144
|
+
commandLength + operationBuffer.length < maxMessageSizeBytes &&
|
|
145
|
+
command.ops.documents.length < maxWriteBatchSize
|
|
146
|
+
) {
|
|
147
|
+
// Pushing to the ops document sequence returns the total byte length of the document sequence.
|
|
148
|
+
commandLength = MESSAGE_OVERHEAD_BYTES + command.ops.push(operation, operationBuffer);
|
|
149
|
+
// Increment the builder's current model index.
|
|
150
|
+
this.currentModelIndex++;
|
|
151
|
+
} else {
|
|
152
|
+
// The operation cannot fit in the current command and will need to
|
|
153
|
+
// go in the next batch. Exit the loop.
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
66
156
|
} else {
|
|
157
|
+
// The namespace is not already in the nsInfo so we will set it in the map, and
|
|
158
|
+
// construct our nsInfo and ops documents and buffers.
|
|
67
159
|
namespaces.set(ns, currentNamespaceIndex);
|
|
68
|
-
|
|
69
|
-
currentNamespaceIndex
|
|
160
|
+
const nsInfo = { ns: ns };
|
|
161
|
+
const operation = buildOperation(model, currentNamespaceIndex, this.pkFactory);
|
|
162
|
+
let nsInfoBuffer;
|
|
163
|
+
let operationBuffer;
|
|
164
|
+
try {
|
|
165
|
+
nsInfoBuffer = BSON.serialize(nsInfo);
|
|
166
|
+
operationBuffer = BSON.serialize(operation);
|
|
167
|
+
} catch (cause) {
|
|
168
|
+
throw new MongoInvalidArgumentError(`Could not serialize ns info to BSON`, { cause });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
validateBufferSize('nsInfo', nsInfoBuffer, maxBsonObjectSize);
|
|
172
|
+
validateBufferSize('ops', operationBuffer, maxBsonObjectSize);
|
|
173
|
+
|
|
174
|
+
// Check if the operation and nsInfo buffers can fit in the command. If they
|
|
175
|
+
// can, then add the operation and nsInfo to their respective document
|
|
176
|
+
// sequences and increment the current length as long as the ops don't exceed
|
|
177
|
+
// the maxWriteBatchSize.
|
|
178
|
+
if (
|
|
179
|
+
commandLength + nsInfoBuffer.length + operationBuffer.length < maxMessageSizeBytes &&
|
|
180
|
+
command.ops.documents.length < maxWriteBatchSize
|
|
181
|
+
) {
|
|
182
|
+
// Pushing to the ops document sequence returns the total byte length of the document sequence.
|
|
183
|
+
commandLength =
|
|
184
|
+
MESSAGE_OVERHEAD_BYTES +
|
|
185
|
+
command.nsInfo.push(nsInfo, nsInfoBuffer) +
|
|
186
|
+
command.ops.push(operation, operationBuffer);
|
|
187
|
+
// We've added a new namespace, increment the namespace index.
|
|
188
|
+
currentNamespaceIndex++;
|
|
189
|
+
// Increment the builder's current model index.
|
|
190
|
+
this.currentModelIndex++;
|
|
191
|
+
} else {
|
|
192
|
+
// The operation cannot fit in the current command and will need to
|
|
193
|
+
// go in the next batch. Exit the loop.
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
70
196
|
}
|
|
71
197
|
}
|
|
198
|
+
// Set the last operations and return the command.
|
|
199
|
+
this.lastOperations = command.ops.documents;
|
|
200
|
+
return command;
|
|
201
|
+
}
|
|
72
202
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// The base command.
|
|
203
|
+
private baseCommand(): ClientBulkWriteCommand {
|
|
76
204
|
const command: ClientBulkWriteCommand = {
|
|
77
205
|
bulkWrite: 1,
|
|
78
206
|
errorsOnly: this.errorsOnly,
|
|
79
207
|
ordered: this.options.ordered ?? true,
|
|
80
|
-
ops: new DocumentSequence(
|
|
81
|
-
nsInfo: new DocumentSequence(nsInfo)
|
|
208
|
+
ops: new DocumentSequence('ops'),
|
|
209
|
+
nsInfo: new DocumentSequence('nsInfo')
|
|
82
210
|
};
|
|
83
211
|
// Add bypassDocumentValidation if it was present in the options.
|
|
84
212
|
if (this.options.bypassDocumentValidation != null) {
|
|
@@ -88,7 +216,22 @@ export class ClientBulkWriteCommandBuilder {
|
|
|
88
216
|
if (this.options.let) {
|
|
89
217
|
command.let = this.options.let;
|
|
90
218
|
}
|
|
91
|
-
|
|
219
|
+
|
|
220
|
+
// we check for undefined specifically here to allow falsy values
|
|
221
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
222
|
+
if (this.options.comment !== undefined) {
|
|
223
|
+
command.comment = this.options.comment;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return command;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function validateBufferSize(name: string, buffer: Uint8Array, maxBsonObjectSize: number) {
|
|
231
|
+
if (buffer.length > maxBsonObjectSize) {
|
|
232
|
+
throw new MongoInvalidArgumentError(
|
|
233
|
+
`Client bulk write operation ${name} of length ${buffer.length} exceeds the max bson object size of ${maxBsonObjectSize}`
|
|
234
|
+
);
|
|
92
235
|
}
|
|
93
236
|
}
|
|
94
237
|
|
|
@@ -105,13 +248,15 @@ interface ClientInsertOperation {
|
|
|
105
248
|
* @returns the operation.
|
|
106
249
|
*/
|
|
107
250
|
export const buildInsertOneOperation = (
|
|
108
|
-
model: ClientInsertOneModel
|
|
109
|
-
index: number
|
|
251
|
+
model: ClientInsertOneModel<Document>,
|
|
252
|
+
index: number,
|
|
253
|
+
pkFactory: PkFactory
|
|
110
254
|
): ClientInsertOperation => {
|
|
111
255
|
const document: ClientInsertOperation = {
|
|
112
256
|
insert: index,
|
|
113
257
|
document: model.document
|
|
114
258
|
};
|
|
259
|
+
document.document._id = model.document._id ?? pkFactory.createPk();
|
|
115
260
|
return document;
|
|
116
261
|
};
|
|
117
262
|
|
|
@@ -130,7 +275,10 @@ export interface ClientDeleteOperation {
|
|
|
130
275
|
* @param index - The namespace index.
|
|
131
276
|
* @returns the operation.
|
|
132
277
|
*/
|
|
133
|
-
export const buildDeleteOneOperation = (
|
|
278
|
+
export const buildDeleteOneOperation = (
|
|
279
|
+
model: ClientDeleteOneModel<Document>,
|
|
280
|
+
index: number
|
|
281
|
+
): Document => {
|
|
134
282
|
return createDeleteOperation(model, index, false);
|
|
135
283
|
};
|
|
136
284
|
|
|
@@ -140,7 +288,10 @@ export const buildDeleteOneOperation = (model: ClientDeleteOneModel, index: numb
|
|
|
140
288
|
* @param index - The namespace index.
|
|
141
289
|
* @returns the operation.
|
|
142
290
|
*/
|
|
143
|
-
export const buildDeleteManyOperation = (
|
|
291
|
+
export const buildDeleteManyOperation = (
|
|
292
|
+
model: ClientDeleteManyModel<Document>,
|
|
293
|
+
index: number
|
|
294
|
+
): Document => {
|
|
144
295
|
return createDeleteOperation(model, index, true);
|
|
145
296
|
};
|
|
146
297
|
|
|
@@ -148,7 +299,7 @@ export const buildDeleteManyOperation = (model: ClientDeleteManyModel, index: nu
|
|
|
148
299
|
* Creates a delete operation based on the parameters.
|
|
149
300
|
*/
|
|
150
301
|
function createDeleteOperation(
|
|
151
|
-
model: ClientDeleteOneModel | ClientDeleteManyModel
|
|
302
|
+
model: ClientDeleteOneModel<Document> | ClientDeleteManyModel<Document>,
|
|
152
303
|
index: number,
|
|
153
304
|
multi: boolean
|
|
154
305
|
): ClientDeleteOperation {
|
|
@@ -175,6 +326,7 @@ export interface ClientUpdateOperation {
|
|
|
175
326
|
hint?: Hint;
|
|
176
327
|
upsert?: boolean;
|
|
177
328
|
arrayFilters?: Document[];
|
|
329
|
+
collation?: CollationOptions;
|
|
178
330
|
}
|
|
179
331
|
|
|
180
332
|
/**
|
|
@@ -184,7 +336,7 @@ export interface ClientUpdateOperation {
|
|
|
184
336
|
* @returns the operation.
|
|
185
337
|
*/
|
|
186
338
|
export const buildUpdateOneOperation = (
|
|
187
|
-
model: ClientUpdateOneModel
|
|
339
|
+
model: ClientUpdateOneModel<Document>,
|
|
188
340
|
index: number
|
|
189
341
|
): ClientUpdateOperation => {
|
|
190
342
|
return createUpdateOperation(model, index, false);
|
|
@@ -197,20 +349,37 @@ export const buildUpdateOneOperation = (
|
|
|
197
349
|
* @returns the operation.
|
|
198
350
|
*/
|
|
199
351
|
export const buildUpdateManyOperation = (
|
|
200
|
-
model: ClientUpdateManyModel
|
|
352
|
+
model: ClientUpdateManyModel<Document>,
|
|
201
353
|
index: number
|
|
202
354
|
): ClientUpdateOperation => {
|
|
203
355
|
return createUpdateOperation(model, index, true);
|
|
204
356
|
};
|
|
205
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Validate the update document.
|
|
360
|
+
* @param update - The update document.
|
|
361
|
+
*/
|
|
362
|
+
function validateUpdate(update: Document) {
|
|
363
|
+
if (!hasAtomicOperators(update)) {
|
|
364
|
+
throw new MongoAPIError(
|
|
365
|
+
'Client bulk write update models must only contain atomic modifiers (start with $) and must not be empty.'
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
206
370
|
/**
|
|
207
371
|
* Creates a delete operation based on the parameters.
|
|
208
372
|
*/
|
|
209
373
|
function createUpdateOperation(
|
|
210
|
-
model: ClientUpdateOneModel | ClientUpdateManyModel
|
|
374
|
+
model: ClientUpdateOneModel<Document> | ClientUpdateManyModel<Document>,
|
|
211
375
|
index: number,
|
|
212
376
|
multi: boolean
|
|
213
377
|
): ClientUpdateOperation {
|
|
378
|
+
// Update documents provided in UpdateOne and UpdateMany write models are
|
|
379
|
+
// required only to contain atomic modifiers (i.e. keys that start with "$").
|
|
380
|
+
// Drivers MUST throw an error if an update document is empty or if the
|
|
381
|
+
// document's first key does not start with "$".
|
|
382
|
+
validateUpdate(model.update);
|
|
214
383
|
const document: ClientUpdateOperation = {
|
|
215
384
|
update: index,
|
|
216
385
|
multi: multi,
|
|
@@ -226,6 +395,9 @@ function createUpdateOperation(
|
|
|
226
395
|
if (model.arrayFilters) {
|
|
227
396
|
document.arrayFilters = model.arrayFilters;
|
|
228
397
|
}
|
|
398
|
+
if (model.collation) {
|
|
399
|
+
document.collation = model.collation;
|
|
400
|
+
}
|
|
229
401
|
return document;
|
|
230
402
|
}
|
|
231
403
|
|
|
@@ -237,6 +409,7 @@ export interface ClientReplaceOneOperation {
|
|
|
237
409
|
updateMods: WithoutId<Document>;
|
|
238
410
|
hint?: Hint;
|
|
239
411
|
upsert?: boolean;
|
|
412
|
+
collation?: CollationOptions;
|
|
240
413
|
}
|
|
241
414
|
|
|
242
415
|
/**
|
|
@@ -246,9 +419,15 @@ export interface ClientReplaceOneOperation {
|
|
|
246
419
|
* @returns the operation.
|
|
247
420
|
*/
|
|
248
421
|
export const buildReplaceOneOperation = (
|
|
249
|
-
model: ClientReplaceOneModel
|
|
422
|
+
model: ClientReplaceOneModel<Document>,
|
|
250
423
|
index: number
|
|
251
424
|
): ClientReplaceOneOperation => {
|
|
425
|
+
if (hasAtomicOperators(model.replacement)) {
|
|
426
|
+
throw new MongoAPIError(
|
|
427
|
+
'Client bulk write replace models must not contain atomic modifiers (start with $) and must not be empty.'
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
|
|
252
431
|
const document: ClientReplaceOneOperation = {
|
|
253
432
|
update: index,
|
|
254
433
|
multi: false,
|
|
@@ -261,14 +440,21 @@ export const buildReplaceOneOperation = (
|
|
|
261
440
|
if (model.upsert) {
|
|
262
441
|
document.upsert = model.upsert;
|
|
263
442
|
}
|
|
443
|
+
if (model.collation) {
|
|
444
|
+
document.collation = model.collation;
|
|
445
|
+
}
|
|
264
446
|
return document;
|
|
265
447
|
};
|
|
266
448
|
|
|
267
449
|
/** @internal */
|
|
268
|
-
export function buildOperation(
|
|
450
|
+
export function buildOperation(
|
|
451
|
+
model: AnyClientBulkWriteModel<Document>,
|
|
452
|
+
index: number,
|
|
453
|
+
pkFactory: PkFactory
|
|
454
|
+
): Document {
|
|
269
455
|
switch (model.name) {
|
|
270
456
|
case 'insertOne':
|
|
271
|
-
return buildInsertOneOperation(model, index);
|
|
457
|
+
return buildInsertOneOperation(model, index, pkFactory);
|
|
272
458
|
case 'deleteOne':
|
|
273
459
|
return buildDeleteOneOperation(model, index);
|
|
274
460
|
case 'deleteMany':
|