Skip to content

Commit f31fc03

Browse files
committed
feat(NODE-6350): add typescript support to client bulkWrite API
1 parent 3f9d243 commit f31fc03

File tree

6 files changed

+336
-42
lines changed

6 files changed

+336
-42
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ export type {
476476
} from './operations/aggregate';
477477
export type {
478478
AnyClientBulkWriteModel,
479+
ClientBulkWriteModel,
479480
ClientBulkWriteOptions,
480481
ClientBulkWriteResult,
481482
ClientDeleteManyModel,

src/mongo_client.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {
3131
} from './mongo_logger';
3232
import { TypedEventEmitter } from './mongo_types';
3333
import {
34-
type AnyClientBulkWriteModel,
34+
type ClientBulkWriteModel,
3535
type ClientBulkWriteOptions,
3636
type ClientBulkWriteResult
3737
} from './operations/client_bulk_write/common';
@@ -331,7 +331,6 @@ export type MongoClientEvents = Pick<TopologyEvents, (typeof MONGO_CLIENT_EVENTS
331331
};
332332

333333
/** @internal */
334-
335334
const kOptions = Symbol('options');
336335

337336
/**
@@ -489,11 +488,12 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
489488
* @param options - The client bulk write options.
490489
* @returns A ClientBulkWriteResult for acknowledged writes and ok: 1 for unacknowledged writes.
491490
*/
492-
async bulkWrite(
493-
models: AnyClientBulkWriteModel[],
491+
async bulkWrite<SchemaMap extends Record<string, Document> = Record<string, Document>>(
492+
models: ReadonlyArray<ClientBulkWriteModel<SchemaMap>>,
494493
options?: ClientBulkWriteOptions
495494
): Promise<ClientBulkWriteResult | { ok: 1 }> {
496-
return await new ClientBulkWriteExecutor(this, models, options).execute();
495+
/// We do not need schema type information past this point ("as any" is fine)
496+
return await new ClientBulkWriteExecutor(this, models as any, options).execute();
497497
}
498498

499499
/**

src/operations/client_bulk_write/command_builder.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export interface ClientBulkWriteCommand {
3030

3131
/** @internal */
3232
export class ClientBulkWriteCommandBuilder {
33-
models: AnyClientBulkWriteModel[];
33+
models: ReadonlyArray<AnyClientBulkWriteModel<Document>>;
3434
options: ClientBulkWriteOptions;
3535
pkFactory: PkFactory;
3636

@@ -39,7 +39,7 @@ export class ClientBulkWriteCommandBuilder {
3939
* @param models - The client write models.
4040
*/
4141
constructor(
42-
models: AnyClientBulkWriteModel[],
42+
models: ReadonlyArray<AnyClientBulkWriteModel<Document>>,
4343
options: ClientBulkWriteOptions,
4444
pkFactory?: PkFactory
4545
) {
@@ -120,7 +120,7 @@ interface ClientInsertOperation {
120120
* @returns the operation.
121121
*/
122122
export const buildInsertOneOperation = (
123-
model: ClientInsertOneModel,
123+
model: ClientInsertOneModel<Document>,
124124
index: number,
125125
pkFactory: PkFactory
126126
): ClientInsertOperation => {
@@ -147,7 +147,10 @@ export interface ClientDeleteOperation {
147147
* @param index - The namespace index.
148148
* @returns the operation.
149149
*/
150-
export const buildDeleteOneOperation = (model: ClientDeleteOneModel, index: number): Document => {
150+
export const buildDeleteOneOperation = (
151+
model: ClientDeleteOneModel<Document>,
152+
index: number
153+
): Document => {
151154
return createDeleteOperation(model, index, false);
152155
};
153156

@@ -157,15 +160,18 @@ export const buildDeleteOneOperation = (model: ClientDeleteOneModel, index: numb
157160
* @param index - The namespace index.
158161
* @returns the operation.
159162
*/
160-
export const buildDeleteManyOperation = (model: ClientDeleteManyModel, index: number): Document => {
163+
export const buildDeleteManyOperation = (
164+
model: ClientDeleteManyModel<Document>,
165+
index: number
166+
): Document => {
161167
return createDeleteOperation(model, index, true);
162168
};
163169

164170
/**
165171
* Creates a delete operation based on the parameters.
166172
*/
167173
function createDeleteOperation(
168-
model: ClientDeleteOneModel | ClientDeleteManyModel,
174+
model: ClientDeleteOneModel<Document> | ClientDeleteManyModel<Document>,
169175
index: number,
170176
multi: boolean
171177
): ClientDeleteOperation {
@@ -202,7 +208,7 @@ export interface ClientUpdateOperation {
202208
* @returns the operation.
203209
*/
204210
export const buildUpdateOneOperation = (
205-
model: ClientUpdateOneModel,
211+
model: ClientUpdateOneModel<Document>,
206212
index: number
207213
): ClientUpdateOperation => {
208214
return createUpdateOperation(model, index, false);
@@ -215,7 +221,7 @@ export const buildUpdateOneOperation = (
215221
* @returns the operation.
216222
*/
217223
export const buildUpdateManyOperation = (
218-
model: ClientUpdateManyModel,
224+
model: ClientUpdateManyModel<Document>,
219225
index: number
220226
): ClientUpdateOperation => {
221227
return createUpdateOperation(model, index, true);
@@ -225,7 +231,7 @@ export const buildUpdateManyOperation = (
225231
* Creates a delete operation based on the parameters.
226232
*/
227233
function createUpdateOperation(
228-
model: ClientUpdateOneModel | ClientUpdateManyModel,
234+
model: ClientUpdateOneModel<Document> | ClientUpdateManyModel<Document>,
229235
index: number,
230236
multi: boolean
231237
): ClientUpdateOperation {
@@ -268,7 +274,7 @@ export interface ClientReplaceOneOperation {
268274
* @returns the operation.
269275
*/
270276
export const buildReplaceOneOperation = (
271-
model: ClientReplaceOneModel,
277+
model: ClientReplaceOneModel<Document>,
272278
index: number
273279
): ClientReplaceOneOperation => {
274280
const document: ClientReplaceOneOperation = {
@@ -291,7 +297,7 @@ export const buildReplaceOneOperation = (
291297

292298
/** @internal */
293299
export function buildOperation(
294-
model: AnyClientBulkWriteModel,
300+
model: AnyClientBulkWriteModel<Document>,
295301
index: number,
296302
pkFactory: PkFactory
297303
): Document {

src/operations/client_bulk_write/common.ts

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,50 +32,50 @@ export interface ClientWriteModel {
3232
}
3333

3434
/** @public */
35-
export interface ClientInsertOneModel extends ClientWriteModel {
35+
export interface ClientInsertOneModel<TSchema> extends ClientWriteModel {
3636
name: 'insertOne';
3737
/** The document to insert. */
38-
document: OptionalId<Document>;
38+
document: OptionalId<TSchema>;
3939
}
4040

4141
/** @public */
42-
export interface ClientDeleteOneModel extends ClientWriteModel {
42+
export interface ClientDeleteOneModel<TSchema> extends ClientWriteModel {
4343
name: 'deleteOne';
4444
/**
4545
* The filter used to determine if a document should be deleted.
4646
* For a deleteOne operation, the first match is removed.
4747
*/
48-
filter: Filter<Document>;
48+
filter: Filter<TSchema>;
4949
/** Specifies a collation. */
5050
collation?: CollationOptions;
5151
/** The index to use. If specified, then the query system will only consider plans using the hinted index. */
5252
hint?: Hint;
5353
}
5454

5555
/** @public */
56-
export interface ClientDeleteManyModel extends ClientWriteModel {
56+
export interface ClientDeleteManyModel<TSchema> extends ClientWriteModel {
5757
name: 'deleteMany';
5858
/**
5959
* The filter used to determine if a document should be deleted.
6060
* For a deleteMany operation, all matches are removed.
6161
*/
62-
filter: Filter<Document>;
62+
filter: Filter<TSchema>;
6363
/** Specifies a collation. */
6464
collation?: CollationOptions;
6565
/** The index to use. If specified, then the query system will only consider plans using the hinted index. */
6666
hint?: Hint;
6767
}
6868

6969
/** @public */
70-
export interface ClientReplaceOneModel extends ClientWriteModel {
70+
export interface ClientReplaceOneModel<TSchema> extends ClientWriteModel {
7171
name: 'replaceOne';
7272
/**
7373
* The filter used to determine if a document should be replaced.
7474
* For a replaceOne operation, the first match is replaced.
7575
*/
76-
filter: Filter<Document>;
76+
filter: Filter<TSchema>;
7777
/** The document with which to replace the matched document. */
78-
replacement: WithoutId<Document>;
78+
replacement: WithoutId<TSchema>;
7979
/** Specifies a collation. */
8080
collation?: CollationOptions;
8181
/** The index to use. If specified, then the query system will only consider plans using the hinted index. */
@@ -85,19 +85,19 @@ export interface ClientReplaceOneModel extends ClientWriteModel {
8585
}
8686

8787
/** @public */
88-
export interface ClientUpdateOneModel extends ClientWriteModel {
88+
export interface ClientUpdateOneModel<TSchema> extends ClientWriteModel {
8989
name: 'updateOne';
9090
/**
9191
* The filter used to determine if a document should be updated.
9292
* For an updateOne operation, the first match is updated.
9393
*/
94-
filter: Filter<Document>;
94+
filter: Filter<TSchema>;
9595
/**
9696
* The modifications to apply. The value can be either:
9797
* UpdateFilter<Document> - A document that contains update operator expressions,
9898
* Document[] - an aggregation pipeline.
9999
*/
100-
update: UpdateFilter<Document> | Document[];
100+
update: UpdateFilter<TSchema> | Document[];
101101
/** A set of filters specifying to which array elements an update should apply. */
102102
arrayFilters?: Document[];
103103
/** Specifies a collation. */
@@ -109,19 +109,19 @@ export interface ClientUpdateOneModel extends ClientWriteModel {
109109
}
110110

111111
/** @public */
112-
export interface ClientUpdateManyModel extends ClientWriteModel {
112+
export interface ClientUpdateManyModel<TSchema> extends ClientWriteModel {
113113
name: 'updateMany';
114114
/**
115115
* The filter used to determine if a document should be updated.
116116
* For an updateMany operation, all matches are updated.
117117
*/
118-
filter: Filter<Document>;
118+
filter: Filter<TSchema>;
119119
/**
120120
* The modifications to apply. The value can be either:
121121
* UpdateFilter<Document> - A document that contains update operator expressions,
122122
* Document[] - an aggregation pipeline.
123123
*/
124-
update: UpdateFilter<Document> | Document[];
124+
update: UpdateFilter<TSchema> | Document[];
125125
/** A set of filters specifying to which array elements an update should apply. */
126126
arrayFilters?: Document[];
127127
/** Specifies a collation. */
@@ -137,13 +137,42 @@ export interface ClientUpdateManyModel extends ClientWriteModel {
137137
* to MongoClient#bulkWrite.
138138
* @public
139139
*/
140-
export type AnyClientBulkWriteModel =
141-
| ClientInsertOneModel
142-
| ClientReplaceOneModel
143-
| ClientUpdateOneModel
144-
| ClientUpdateManyModel
145-
| ClientDeleteOneModel
146-
| ClientDeleteManyModel;
140+
export type AnyClientBulkWriteModel<TSchema extends Document> =
141+
| ClientInsertOneModel<TSchema>
142+
| ClientReplaceOneModel<TSchema>
143+
| ClientUpdateOneModel<TSchema>
144+
| ClientUpdateManyModel<TSchema>
145+
| ClientDeleteOneModel<TSchema>
146+
| ClientDeleteManyModel<TSchema>;
147+
148+
/**
149+
* Take a Typescript type that maps namespaces to schema types.
150+
* @public
151+
*
152+
* @example
153+
* ```ts
154+
* type MongoDBSchemas = {
155+
* 'db.books': Book;
156+
* 'db.authors': Author;
157+
* }
158+
*
159+
* const model: ClientBulkWriteModel<MongoDBSchemas> = {
160+
* namespace: 'db.books'
161+
* name: 'insertOne',
162+
* document: { title: 'Practical MongoDB Aggregations', authorName: 3 } // error `authorName` cannot be number
163+
* };
164+
* ```
165+
*
166+
* The type of the `namespace` field narrows other parts of the BulkWriteModel to use the correct schema for type assertions.
167+
*
168+
*/
169+
export type ClientBulkWriteModel<
170+
SchemaMap extends Record<string, Document> = Record<string, Document>
171+
> = {
172+
[Namespace in keyof SchemaMap]: AnyClientBulkWriteModel<SchemaMap[Namespace]> & {
173+
namespace: Namespace;
174+
};
175+
}[keyof SchemaMap];
147176

148177
/** @public */
149178
export interface ClientBulkWriteResult {

src/operations/client_bulk_write/executor.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ import { ClientBulkWriteResultsMerger } from './results_merger';
1818
* @internal
1919
*/
2020
export class ClientBulkWriteExecutor {
21-
client: MongoClient;
22-
options: ClientBulkWriteOptions;
23-
operations: AnyClientBulkWriteModel[];
21+
private readonly client: MongoClient;
22+
private readonly options: ClientBulkWriteOptions;
23+
private readonly operations: ReadonlyArray<AnyClientBulkWriteModel<Document>>;
2424

2525
/**
2626
* Instantiate the executor.
@@ -30,7 +30,7 @@ export class ClientBulkWriteExecutor {
3030
*/
3131
constructor(
3232
client: MongoClient,
33-
operations: AnyClientBulkWriteModel[],
33+
operations: ReadonlyArray<AnyClientBulkWriteModel<Document>>,
3434
options?: ClientBulkWriteOptions
3535
) {
3636
this.client = client;

0 commit comments

Comments
 (0)