Skip to content

Commit a2d9dcc

Browse files
committed
chore: address comments
1 parent ddcae44 commit a2d9dcc

File tree

10 files changed

+97
-35
lines changed

10 files changed

+97
-35
lines changed

src/cursor/client_bulk_write_cursor.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Document } from '../bson';
22
import { type ClientBulkWriteCursorResponse } from '../cmap/wire_protocol/responses';
3+
import { MongoBulkWriteCursorError } from '../error';
34
import type { MongoClient } from '../mongo_client';
45
import { ClientBulkWriteOperation } from '../operations/client_bulk_write/client_bulk_write';
56
import { type ClientBulkWriteOptions } from '../operations/client_bulk_write/common';
@@ -43,7 +44,9 @@ export class ClientBulkWriteCursor extends AbstractCursor {
4344
*/
4445
get response(): ClientBulkWriteCursorResponse {
4546
if (this.cursorResponse) return this.cursorResponse;
46-
throw new Error('no cursor response');
47+
throw new MongoBulkWriteCursorError(
48+
'No client bulk write cursor response returned from the server.'
49+
);
4750
}
4851

4952
clone(): ClientBulkWriteCursor {

src/error.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,33 @@ export class MongoGCPError extends MongoOIDCError {
616616
}
617617
}
618618

619+
/**
620+
* An error generated when a ChangeStream operation fails to execute.
621+
*
622+
* @public
623+
* @category Error
624+
*/
625+
export class MongoBulkWriteCursorError extends MongoRuntimeError {
626+
/**
627+
* **Do not use this constructor!**
628+
*
629+
* Meant for internal use only.
630+
*
631+
* @remarks
632+
* This class is only meant to be constructed within the driver. This constructor is
633+
* not subject to semantic versioning compatibility guarantees and may change at any time.
634+
*
635+
* @public
636+
**/
637+
constructor(message: string) {
638+
super(message);
639+
}
640+
641+
override get name(): string {
642+
return 'MongoBulkWriteCursorError';
643+
}
644+
}
645+
619646
/**
620647
* An error generated when a ChangeStream operation fails to execute.
621648
*

src/operations/client_bulk_write/client_bulk_write.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Aspect, defineAspects } from '../operation';
99
import { type ClientBulkWriteOptions } from './common';
1010

1111
/**
12-
* Executes a single vlient bulk write operation within a potential batch.
12+
* Executes a single client bulk write operation within a potential batch.
1313
* @internal
1414
*/
1515
export class ClientBulkWriteOperation extends CommandOperation<ClientBulkWriteCursorResponse> {

src/operations/client_bulk_write/command_builder.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { type Document, ObjectId } from '../../bson';
1+
import { type Document } from '../../bson';
22
import { DocumentSequence } from '../../cmap/commands';
3+
import { type PkFactory } from '../../mongo_client';
34
import type { Filter, OptionalId, UpdateFilter, WithoutId } from '../../mongo_types';
5+
import { DEFAULT_PK_FACTORY } from '../../utils';
46
import { type CollationOptions } from '../command';
57
import { type Hint } from '../operation';
68
import type {
@@ -30,14 +32,20 @@ export interface ClientBulkWriteCommand {
3032
export class ClientBulkWriteCommandBuilder {
3133
models: AnyClientBulkWriteModel[];
3234
options: ClientBulkWriteOptions;
35+
pkFactory: PkFactory;
3336

3437
/**
3538
* Create the command builder.
3639
* @param models - The client write models.
3740
*/
38-
constructor(models: AnyClientBulkWriteModel[], options: ClientBulkWriteOptions) {
41+
constructor(
42+
models: AnyClientBulkWriteModel[],
43+
options: ClientBulkWriteOptions,
44+
pkFactory?: PkFactory
45+
) {
3946
this.models = models;
4047
this.options = options;
48+
this.pkFactory = pkFactory ?? DEFAULT_PK_FACTORY;
4149
}
4250

4351
/**
@@ -63,10 +71,10 @@ export class ClientBulkWriteCommandBuilder {
6371
const ns = model.namespace;
6472
const index = namespaces.get(ns);
6573
if (index != null) {
66-
operations.push(buildOperation(model, index));
74+
operations.push(buildOperation(model, index, this.pkFactory));
6775
} else {
6876
namespaces.set(ns, currentNamespaceIndex);
69-
operations.push(buildOperation(model, currentNamespaceIndex));
77+
operations.push(buildOperation(model, currentNamespaceIndex, this.pkFactory));
7078
currentNamespaceIndex++;
7179
}
7280
}
@@ -90,7 +98,9 @@ export class ClientBulkWriteCommandBuilder {
9098
command.let = this.options.let;
9199
}
92100

93-
if (this.options.comment != null) {
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) {
94104
command.comment = this.options.comment;
95105
}
96106
return [command];
@@ -111,13 +121,14 @@ interface ClientInsertOperation {
111121
*/
112122
export const buildInsertOneOperation = (
113123
model: ClientInsertOneModel,
114-
index: number
124+
index: number,
125+
pkFactory: PkFactory
115126
): ClientInsertOperation => {
116127
const document: ClientInsertOperation = {
117128
insert: index,
118129
document: model.document
119130
};
120-
document.document._id = model.document._id ?? new ObjectId();
131+
document.document._id = model.document._id ?? pkFactory.createPk();
121132
return document;
122133
};
123134

@@ -279,10 +290,14 @@ export const buildReplaceOneOperation = (
279290
};
280291

281292
/** @internal */
282-
export function buildOperation(model: AnyClientBulkWriteModel, index: number): Document {
293+
export function buildOperation(
294+
model: AnyClientBulkWriteModel,
295+
index: number,
296+
pkFactory: PkFactory
297+
): Document {
283298
switch (model.name) {
284299
case 'insertOne':
285-
return buildInsertOneOperation(model, index);
300+
return buildInsertOneOperation(model, index, pkFactory);
286301
case 'deleteOne':
287302
return buildDeleteOneOperation(model, index);
288303
case 'deleteMany':

src/operations/client_bulk_write/common.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,11 @@ export interface ClientUpdateResult {
209209
* these two cases.
210210
*/
211211
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;
212217
}
213218

214219
/** @public */

src/operations/client_bulk_write/executor.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { type MongoClient } from '../../mongo_client';
55
import { WriteConcern } from '../../write_concern';
66
import { executeOperation } from '../execute_operation';
77
import { ClientBulkWriteOperation } from './client_bulk_write';
8-
import { ClientBulkWriteCommandBuilder } from './command_builder';
8+
import { type ClientBulkWriteCommand, ClientBulkWriteCommandBuilder } from './command_builder';
99
import {
1010
type AnyClientBulkWriteModel,
1111
type ClientBulkWriteOptions,
@@ -51,8 +51,13 @@ export class ClientBulkWriteExecutor {
5151
async execute(): Promise<ClientBulkWriteResult | { ok: 1 }> {
5252
// The command builder will take the user provided models and potential split the batch
5353
// into multiple commands due to size.
54-
const commmandBuilder = new ClientBulkWriteCommandBuilder(this.operations, this.options);
55-
const commands = commmandBuilder.buildCommands();
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();
5661
if (this.options.writeConcern?.w === 0) {
5762
return await executeUnacknowledged(this.client, this.options, commands);
5863
}
@@ -66,7 +71,7 @@ export class ClientBulkWriteExecutor {
6671
async function executeAcknowledged(
6772
client: MongoClient,
6873
options: ClientBulkWriteOptions,
69-
commands: Document[]
74+
commands: ClientBulkWriteCommand[]
7075
): Promise<ClientBulkWriteResult> {
7176
const resultsMerger = new ClientBulkWriteResultsMerger(options);
7277
// For each command will will create and exhaust a cursor for the results.

src/operations/client_bulk_write/results_merger.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,21 @@ export class ClientBulkWriteResultsMerger {
2222
*/
2323
constructor(options: ClientBulkWriteOptions) {
2424
this.options = options;
25-
const baseResult = {
25+
this.result = {
2626
insertedCount: 0,
2727
upsertedCount: 0,
2828
matchedCount: 0,
2929
modifiedCount: 0,
30-
deletedCount: 0
30+
deletedCount: 0,
31+
insertResults: undefined,
32+
updateResults: undefined,
33+
deleteResults: undefined
3134
};
3235

3336
if (options.verboseResults) {
34-
this.result = {
35-
...baseResult,
36-
insertResults: new Map<number, ClientInsertOneResult>(),
37-
updateResults: new Map<number, ClientUpdateResult>(),
38-
deleteResults: new Map<number, ClientDeleteResult>()
39-
};
40-
} else {
41-
this.result = baseResult;
37+
this.result.insertResults = new Map<number, ClientInsertOneResult>();
38+
this.result.updateResults = new Map<number, ClientUpdateResult>();
39+
this.result.deleteResults = new Map<number, ClientDeleteResult>();
4240
}
4341
}
4442

@@ -75,7 +73,10 @@ export class ClientBulkWriteResultsMerger {
7573
if ('update' in operation) {
7674
const result: ClientUpdateResult = {
7775
matchedCount: document.n,
78-
modifiedCount: document.nModified || 0
76+
modifiedCount: document.nModified ?? 0,
77+
// We do specifically want to check undefined here since a null _id is valid.
78+
// eslint-disable-next-line no-restricted-syntax
79+
didUpsert: document.upserted?._id !== undefined
7980
};
8081
if (document.upserted) {
8182
result.upsertedId = document.upserted._id;

test/tools/unified-spec-runner/match.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ export function resultCheck(
217217
// to convert the expected keys from the string numbers to actual numbers since the key
218218
// values in the maps are actual numbers.
219219
const isActualMap = actual instanceof Map;
220-
const mapKey = !Number.isNaN(key) ? Number(key) : key;
220+
const mapKey = !Number.isNaN(Number(key)) ? Number(key) : key;
221221
resultCheck(
222222
isActualMap ? actual.get(mapKey) : actual[key],
223223
value,

test/unit/operations/client_bulk_write/command_builder.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
type ClientReplaceOneModel,
1515
type ClientUpdateManyModel,
1616
type ClientUpdateOneModel,
17+
DEFAULT_PK_FACTORY,
1718
DocumentSequence,
1819
ObjectId
1920
} from '../../../mongodb';
@@ -232,7 +233,7 @@ describe('ClientBulkWriteCommandBuilder', function () {
232233
namespace: 'test.coll',
233234
document: { name: 1 }
234235
};
235-
const operation = buildInsertOneOperation(model, 5);
236+
const operation = buildInsertOneOperation(model, 5, DEFAULT_PK_FACTORY);
236237

237238
it('generates the insert operation with an _id', function () {
238239
expect(operation.insert).to.equal(5);
@@ -248,7 +249,7 @@ describe('ClientBulkWriteCommandBuilder', function () {
248249
namespace: 'test.coll',
249250
document: { _id: id, name: 1 }
250251
};
251-
const operation = buildInsertOneOperation(model, 5);
252+
const operation = buildInsertOneOperation(model, 5, DEFAULT_PK_FACTORY);
252253

253254
it('generates the insert operation with an _id', function () {
254255
expect(operation).to.deep.equal({ insert: 5, document: { _id: id, name: 1 } });

test/unit/operations/client_bulk_write/results_merger.test.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ describe('ClientBulkWriteResultsMerger', function () {
1818
upsertedCount: 0,
1919
matchedCount: 0,
2020
modifiedCount: 0,
21-
deletedCount: 0
21+
deletedCount: 0,
22+
deleteResults: undefined,
23+
insertResults: undefined,
24+
updateResults: undefined
2225
});
2326
});
2427
});
@@ -99,15 +102,17 @@ describe('ClientBulkWriteResultsMerger', function () {
99102
it('sets the update results', function () {
100103
expect(result.updateResults.get(1)).to.deep.equal({
101104
matchedCount: 1,
102-
modifiedCount: 1
105+
modifiedCount: 1,
106+
didUpsert: false
103107
});
104108
});
105109

106110
it('sets the upsert results', function () {
107111
expect(result.updateResults.get(2)).to.deep.equal({
108112
matchedCount: 0,
109113
modifiedCount: 0,
110-
upsertedId: 1
114+
upsertedId: 1,
115+
didUpsert: true
111116
});
112117
});
113118

@@ -172,7 +177,7 @@ describe('ClientBulkWriteResultsMerger', function () {
172177
});
173178

174179
it('sets no insert results', function () {
175-
expect(result).to.not.have.property('insertResults');
180+
expect(result.insertResults).to.equal(undefined);
176181
});
177182

178183
it('merges the upserted count', function () {
@@ -188,15 +193,15 @@ describe('ClientBulkWriteResultsMerger', function () {
188193
});
189194

190195
it('sets no update results', function () {
191-
expect(result).to.not.have.property('updateResults');
196+
expect(result.updateResults).to.equal(undefined);
192197
});
193198

194199
it('merges the deleted count', function () {
195200
expect(result.deletedCount).to.equal(1);
196201
});
197202

198203
it('sets no delete results', function () {
199-
expect(result).to.not.have.property('deleteResults');
204+
expect(result.deleteResults).to.equal(undefined);
200205
});
201206
});
202207
});

0 commit comments

Comments
 (0)