Skip to content

Commit 3b65525

Browse files
authored
feat: Add findCursor model method (plexinc#454)
This returns the mongodb `FindCursor` allowing users to handle iteration however they please in their applications without loading all results into memory at once.
1 parent d56a0f6 commit 3b65525

File tree

3 files changed

+100
-1
lines changed

3 files changed

+100
-1
lines changed

docs/api/model.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,33 @@ userProjected.firstName; // TypeScript error
260260
userProjected.lastName; // valid
261261
```
262262

263+
## `findCursor`
264+
265+
Calls the MongoDB [`find()`](https://mongodb.github.io/node-mongodb-native/5.0/classes/Collection.html#find) method and returns the cursor.
266+
267+
Useful when you want to process many records without loading them all into
268+
memory at once.
269+
270+
**Parameters:**
271+
272+
| Name | Type | Attribute |
273+
| --------- | ---------------------- | --------- |
274+
| `filter` | `PaprFilter<TSchema>` | required |
275+
| `options` | `FindOptions<TSchema>` | optional |
276+
277+
**Example:**
278+
279+
```ts
280+
const cursor = await User.findCursor(
281+
{ active: true, email: { $exists: true } },
282+
{ projection: { email: 1 } }
283+
);
284+
285+
for await (const user of cursor) {
286+
await notify(user.email);
287+
}
288+
```
289+
263290
## `findOne`
264291

265292
Calls the MongoDB [`findOne()`](https://mongodb.github.io/node-mongodb-native/5.0/classes/Collection.html#findOne) method.

src/__tests__/model.test.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { beforeEach, describe, expect, jest, test } from '@jest/globals';
2-
import { Collection, MongoError, ObjectId } from 'mongodb';
2+
import { Collection, FindCursor, MongoError, ObjectId } from 'mongodb';
33
import { expectType } from 'ts-expect';
44
import { Hooks } from '../hooks';
55
import { abstract, build, Model } from '../model';
@@ -902,6 +902,37 @@ describe('model', () => {
902902
});
903903
});
904904

905+
describe('findCursor', () => {
906+
test('default', async () => {
907+
const cursor = await simpleModel.findCursor({});
908+
909+
expectType<FindCursor<SimpleDocument>>(cursor);
910+
});
911+
912+
test('with projection', async () => {
913+
const cursor = await simpleModel.findCursor(
914+
{},
915+
{
916+
projection: {
917+
...projection,
918+
'nested.direct': 1,
919+
},
920+
}
921+
);
922+
923+
expectType<
924+
FindCursor<{
925+
_id: ObjectId;
926+
foo: string;
927+
ham?: Date;
928+
nested?: {
929+
direct: string;
930+
};
931+
}>
932+
>(cursor);
933+
});
934+
});
935+
905936
describe('findById', () => {
906937
test('default', async () => {
907938
const result = await simpleModel.findById('123456789012345678901234');

src/model.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
DeleteResult,
1111
DistinctOptions,
1212
Filter,
13+
FindCursor,
1314
FindOneAndDeleteOptions,
1415
FindOneAndUpdateOptions,
1516
FindOptions,
@@ -86,6 +87,11 @@ export interface Model<TSchema extends BaseSchema, TOptions extends SchemaOption
8687
options?: Omit<FindOptions<TSchema>, 'projection'> & { projection?: TProjection }
8788
) => Promise<ProjectionType<TSchema, TProjection> | null>;
8889

90+
findCursor: <TProjection extends Projection<TSchema> | undefined>(
91+
filter: PaprFilter<TSchema>,
92+
options?: Omit<FindOptions<TSchema>, 'projection'> & { projection?: TProjection }
93+
) => Promise<FindCursor<ProjectionType<TSchema, TProjection>>>;
94+
8995
findOne: <TProjection extends Projection<TSchema> | undefined>(
9096
filter: PaprFilter<TSchema>,
9197
options?: Omit<FindOptions<TSchema>, 'projection'> & { projection?: TProjection }
@@ -158,6 +164,7 @@ export function abstract<TSchema extends BaseSchema, TOptions extends SchemaOpti
158164
deleteOne: abstractMethod,
159165
find: abstractMethod,
160166
findById: abstractMethod,
167+
findCursor: abstractMethod,
161168
findOne: abstractMethod,
162169
findOneAndDelete: abstractMethod,
163170
findOneAndUpdate: abstractMethod,
@@ -668,6 +675,40 @@ export function build<TSchema extends BaseSchema, TOptions extends SchemaOptions
668675
}
669676
);
670677

678+
/**
679+
* @description
680+
* Calls the MongoDB [`find()`](https://mongodb.github.io/node-mongodb-native/5.0/classes/Collection.html#find) method and returns the cursor.
681+
*
682+
* Useful when you want to process many records without loading them all into
683+
* memory at once.
684+
*
685+
* @param filter {PaprFilter<TSchema>}
686+
* @param [options] {FindOptions<TSchema>}
687+
*
688+
* @example
689+
* const cursor = await User.findCursor(
690+
* { active: true, email: { $exists: true } },
691+
* { projection: { email: 1 } }
692+
* )
693+
*
694+
* for await (const user of cursor) {
695+
* await notify(user.email);
696+
* }
697+
*/
698+
model.findCursor = wrap(model, async function findCursor<
699+
TProjection extends Projection<TSchema> | undefined
700+
>(filter: PaprFilter<TSchema>, options?: Omit<FindOptions<TSchema>, 'projection'> & { projection?: TProjection }): Promise<
701+
FindCursor<ProjectionType<TSchema, TProjection>>
702+
> {
703+
return model.collection.find(
704+
filter as Filter<TSchema>,
705+
{
706+
...model.defaultOptions,
707+
...options,
708+
} as FindOptions<TSchema>
709+
) as FindCursor<ProjectionType<TSchema, TProjection>>;
710+
});
711+
671712
/**
672713
* @description
673714
* Calls the MongoDB [`findOne()`](https://mongodb.github.io/node-mongodb-native/5.0/classes/Collection.html#findOne) method.

0 commit comments

Comments
 (0)