Skip to content

Commit e07e564

Browse files
authored
fix(NODE-3765): make replacement for replaceOne operations without _id (#3040)
1 parent f2dcc34 commit e07e564

File tree

4 files changed

+70
-12
lines changed

4 files changed

+70
-12
lines changed

src/bulk/common.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import type { Collection } from '../collection';
3333
import type { Topology } from '../sdam/topology';
3434
import type { CommandOperationOptions, CollationOptions } from '../operations/command';
3535
import type { Hint } from '../operations/operation';
36-
import type { Filter, OneOrMore, OptionalId, UpdateFilter } from '../mongo_types';
36+
import type { Filter, OneOrMore, WithoutId, OptionalId, UpdateFilter } from '../mongo_types';
3737

3838
/** @internal */
3939
const kServerError = Symbol('serverError');
@@ -79,7 +79,7 @@ export interface ReplaceOneModel<TSchema extends Document = Document> {
7979
/** The filter to limit the replaced document. */
8080
filter: Filter<TSchema>;
8181
/** The document with which to replace the matched document. */
82-
replacement: TSchema;
82+
replacement: WithoutId<TSchema>;
8383
/** Specifies a collation. */
8484
collation?: CollationOptions;
8585
/** The index to use. If specified, then the query system will only consider plans using the hinted index. */

src/collection.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ import type {
9393
TODO_NODE_3286,
9494
UpdateFilter,
9595
WithId,
96+
WithoutId,
9697
OptionalId,
9798
Flatten
9899
} from './mongo_types';
@@ -459,26 +460,29 @@ export class Collection<TSchema extends Document = Document> {
459460
* @param options - Optional settings for the command
460461
* @param callback - An optional callback, a Promise will be returned if none is provided
461462
*/
462-
replaceOne(filter: Filter<TSchema>, replacement: TSchema): Promise<UpdateResult | Document>;
463463
replaceOne(
464464
filter: Filter<TSchema>,
465-
replacement: TSchema,
465+
replacement: WithoutId<TSchema>
466+
): Promise<UpdateResult | Document>;
467+
replaceOne(
468+
filter: Filter<TSchema>,
469+
replacement: WithoutId<TSchema>,
466470
callback: Callback<UpdateResult | Document>
467471
): void;
468472
replaceOne(
469473
filter: Filter<TSchema>,
470-
replacement: TSchema,
474+
replacement: WithoutId<TSchema>,
471475
options: ReplaceOptions
472476
): Promise<UpdateResult | Document>;
473477
replaceOne(
474478
filter: Filter<TSchema>,
475-
replacement: TSchema,
479+
replacement: WithoutId<TSchema>,
476480
options: ReplaceOptions,
477481
callback: Callback<UpdateResult | Document>
478482
): void;
479483
replaceOne(
480484
filter: Filter<TSchema>,
481-
replacement: TSchema,
485+
replacement: WithoutId<TSchema>,
482486
options?: ReplaceOptions | Callback<UpdateResult | Document>,
483487
callback?: Callback<UpdateResult | Document>
484488
): Promise<UpdateResult | Document> | void {
@@ -1279,26 +1283,29 @@ export class Collection<TSchema extends Document = Document> {
12791283
* @param options - Optional settings for the command
12801284
* @param callback - An optional callback, a Promise will be returned if none is provided
12811285
*/
1282-
findOneAndReplace(filter: Filter<TSchema>, replacement: Document): Promise<ModifyResult<TSchema>>;
12831286
findOneAndReplace(
12841287
filter: Filter<TSchema>,
1285-
replacement: Document,
1288+
replacement: WithoutId<TSchema>
1289+
): Promise<ModifyResult<TSchema>>;
1290+
findOneAndReplace(
1291+
filter: Filter<TSchema>,
1292+
replacement: WithoutId<TSchema>,
12861293
callback: Callback<ModifyResult<TSchema>>
12871294
): void;
12881295
findOneAndReplace(
12891296
filter: Filter<TSchema>,
1290-
replacement: Document,
1297+
replacement: WithoutId<TSchema>,
12911298
options: FindOneAndReplaceOptions
12921299
): Promise<ModifyResult<TSchema>>;
12931300
findOneAndReplace(
12941301
filter: Filter<TSchema>,
1295-
replacement: Document,
1302+
replacement: WithoutId<TSchema>,
12961303
options: FindOneAndReplaceOptions,
12971304
callback: Callback<ModifyResult<TSchema>>
12981305
): void;
12991306
findOneAndReplace(
13001307
filter: Filter<TSchema>,
1301-
replacement: Document,
1308+
replacement: WithoutId<TSchema>,
13021309
options?: FindOneAndReplaceOptions | Callback<ModifyResult<TSchema>>,
13031310
callback?: Callback<ModifyResult<TSchema>>
13041311
): Promise<ModifyResult<TSchema>> | void {

test/types/community/collection/bulkWrite.test-d.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,38 @@ collectionType.bulkWrite([
181181
}
182182
}
183183
]);
184+
// allow a literal replacement doc without an _id
185+
collectionType.bulkWrite([
186+
{
187+
replaceOne: {
188+
filter: {},
189+
replacement: {
190+
dateField: new Date(),
191+
fruitTags: [],
192+
numberField: 0,
193+
readonlyFruitTags: [],
194+
stringField: 'string',
195+
subInterfaceArray: [],
196+
subInterfaceField: { field1: '1', field2: '2' }
197+
},
198+
upsert: true
199+
}
200+
}
201+
]);
202+
// disallow a literal replacement doc with an _id
203+
expectError(
204+
collectionType.bulkWrite([
205+
{
206+
replaceOne: {
207+
filter: {},
208+
replacement: {
209+
_id: new ObjectId()
210+
},
211+
upsert: true
212+
}
213+
}
214+
])
215+
);
184216

185217
expectError(
186218
collectionType.bulkWrite([
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { expectError } from 'tsd';
2+
import { MongoClient, ObjectId } from '../../../../src';
3+
4+
// test collection.replaceX functions
5+
const client = new MongoClient('');
6+
const db = client.db('test');
7+
8+
interface TestModel {
9+
_id: ObjectId;
10+
stringField: string;
11+
}
12+
13+
const collection = db.collection<TestModel>('testCollection');
14+
15+
// should accept a replacement doc without an _id
16+
await collection.replaceOne({}, { stringField: 'b' });
17+
18+
// should not accept a literal replacement doc with an _id
19+
expectError(await collection.replaceOne({}, { _id: new ObjectId(), stringField: 'a' }));

0 commit comments

Comments
 (0)