Skip to content

Commit b92a507

Browse files
authored
feat: Support options for upsert model method (#711)
1 parent 0b14413 commit b92a507

File tree

3 files changed

+67
-11
lines changed

3 files changed

+67
-11
lines changed

docs/api/model.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -471,10 +471,11 @@ Calls the MongoDB [`findOneAndUpdate()`](https://mongodb.github.io/node-mongodb-
471471

472472
**Parameters:**
473473

474-
| Name | Type | Attribute |
475-
| -------- | --------------------------- | --------- |
476-
| `filter` | `PaprFilter<TSchema>` | required |
477-
| `update` | `PaprUpdateFilter<TSchema>` | required |
474+
| Name | Type | Attribute |
475+
| --------- | --------------------------- | --------- |
476+
| `filter` | `PaprFilter<TSchema>` | required |
477+
| `update` | `PaprUpdateFilter<TSchema>` | required |
478+
| `options` | `FindOneAndUpdateOptions` | optional |
478479

479480
**Returns:**
480481

@@ -484,4 +485,12 @@ Calls the MongoDB [`findOneAndUpdate()`](https://mongodb.github.io/node-mongodb-
484485

485486
```ts
486487
const user = await User.upsert({ firstName: 'John', lastName: 'Wick' }, { $set: { age: 40 } });
488+
489+
const userProjected = await User.upsert(
490+
{ firstName: 'John', lastName: 'Wick' },
491+
{ $set: { age: 40 } },
492+
{ projection: { lastName: 1 } }
493+
);
494+
userProjected.firstName; // TypeScript error
495+
userProjected.lastName; // valid
487496
```

src/__tests__/model.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2506,6 +2506,41 @@ describe('model', () => {
25062506
);
25072507
});
25082508

2509+
test('with projection', async () => {
2510+
const date = new Date();
2511+
const result = await simpleModel.upsert(
2512+
{ foo: 'foo' },
2513+
{ $set: { ham: date } },
2514+
{ projection }
2515+
);
2516+
2517+
expect(result).toBe(doc);
2518+
expectType<{
2519+
_id: ObjectId;
2520+
foo: string;
2521+
ham?: Date;
2522+
}>(result);
2523+
2524+
expectType<string>(result.foo);
2525+
// @ts-expect-error `bar` is undefined here
2526+
result.bar;
2527+
expectType<Date | undefined>(result.ham);
2528+
2529+
expect(collection.findOneAndUpdate).toHaveBeenCalledWith(
2530+
{ foo: 'foo' },
2531+
{
2532+
$set: { ham: date },
2533+
$setOnInsert: { bar: 123456 },
2534+
},
2535+
{
2536+
ignoreUndefined: true,
2537+
projection,
2538+
returnDocument: 'after',
2539+
upsert: true,
2540+
}
2541+
);
2542+
});
2543+
25092544
test('throws error on failure', async () => {
25102545
(
25112546
collection.findOneAndUpdate as jest.Mocked<Collection['findOneAndUpdate']>

src/model.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,11 @@ export interface Model<TSchema extends BaseSchema, TOptions extends SchemaOption
131131
options?: UpdateOptions
132132
) => Promise<UpdateResult>;
133133

134-
upsert: (
134+
upsert: <TProjection extends Projection<TSchema> | undefined>(
135135
filter: PaprFilter<TSchema>,
136-
update: PaprUpdateFilter<TSchema>
137-
) => Promise<WithId<TSchema>>;
136+
update: PaprUpdateFilter<TSchema>,
137+
options?: Omit<FindOneAndUpdateOptions, 'projection' | 'upsert'> & { projection?: TProjection }
138+
) => Promise<ProjectionType<TSchema, TProjection>>;
138139
}
139140

140141
/* eslint-disable @typescript-eslint/ban-types */
@@ -1032,6 +1033,7 @@ export function build<TSchema extends BaseSchema, TOptions extends SchemaOptions
10321033
*
10331034
* @param filter {PaprFilter<TSchema>}
10341035
* @param update {PaprUpdateFilter<TSchema>}
1036+
* @param [options] {FindOneAndUpdateOptions}
10351037
*
10361038
* @returns {Promise<TSchema>}
10371039
*
@@ -1040,19 +1042,29 @@ export function build<TSchema extends BaseSchema, TOptions extends SchemaOptions
10401042
* { firstName: 'John', lastName: 'Wick' },
10411043
* { $set: { age: 40 } }
10421044
* );
1045+
*
1046+
* const userProjected = await User.upsert(
1047+
* { firstName: 'John', lastName: 'Wick' },
1048+
* { $set: { age: 40 } },
1049+
* { projection: { lastName: 1 } }
1050+
* );
1051+
* userProjected.firstName; // TypeScript error
1052+
* userProjected.lastName; // valid
10431053
*/
1044-
model.upsert = async function upsert(
1054+
model.upsert = async function upsert<TProjection extends Projection<TSchema> | undefined>(
10451055
filter: PaprFilter<TSchema>,
1046-
update: PaprUpdateFilter<TSchema>
1047-
): Promise<WithId<TSchema>> {
1056+
update: PaprUpdateFilter<TSchema>,
1057+
options?: Omit<FindOneAndUpdateOptions, 'projection' | 'upsert'> & { projection?: TProjection }
1058+
): Promise<ProjectionType<TSchema, TProjection>> {
10481059
const item = await model.findOneAndUpdate(filter, update, {
1060+
...options,
10491061
upsert: true,
10501062
});
10511063

10521064
if (!item) {
10531065
throw new Error('upsert failed');
10541066
}
10551067

1056-
return item as unknown as WithId<TSchema>;
1068+
return item;
10571069
};
10581070
}

0 commit comments

Comments
 (0)