Skip to content

Commit 30ea597

Browse files
authored
feat(examples, docs): add typegoose discriminator e2e test and docs (#390)
### Description This pull request introduces an end-to-end test for Typegoose discriminators, adds crucial documentation for this feature, and fixes a bug in array field filtering that was discovered during the investigation. #### Motivation I began this work because I was unable to get Typegoose discriminators working correctly in my own application. The existing examples and documentation did not cover this specific use case, making it difficult to debug. The primary goal was to create a fully functional, standalone example and document the correct implementation pattern. In the process, we also uncovered and fixed a separate, underlying bug in the query library. #### What's in this PR? 1. **New E2E Test for Typegoose Discriminators** * A new example, `examples/typegoose-discriminators`, has been added. * This example includes a robust e2e test that validates the full CRUD lifecycle (create, query, update, delete) for discriminated entities. 2. **New Documentation for Discriminators** * To make this powerful feature more accessible, a new "Discriminators" section has been added to the Typegoose `relations.mdx` documentation. * This new section provides a clear explanation and a complete code example based on the new e2e test, demonstrating the correct pattern for implementation. 3. **Bug Fix: Filtering on Array Fields** * A bug was discovered that prevented filtering on array fields (e.g., `string[]`). This would cause an `Unable to create filter comparison for [null]` error. I'm surprised this hasn't surfaced sooner, as using arrays in MongoDB as fields is normal practice. * The `FieldComparisonFactory` has been fixed to correctly infer the type from array fields. * A new unit test has been added to cover this specific case and prevent future regressions. #### Summary of Changes * **feat(examples)**: Add new `typegoose-discriminators` e2e test. * **docs(typegoose)**: Add documentation and example for using discriminators. * **fix(query-graphql)**: Correctly create filter comparisons for array fields. * **test(query-graphql)**: Add unit test for array field filter creation. * **test(query-graphql)**: Update existing unit tests to align with the array filter fix. This PR should make it much easier for future users to implement Typegoose discriminators and resolves a tricky bug in the filtering logic. Hope it can be merged soon. 😁 Scott
2 parents 77ae229 + f71e99d commit 30ea597

30 files changed

+2034
-226
lines changed

documentation/docs/persistence/typegoose/relations.mdx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,3 +401,85 @@ export class TagDTO {
401401
</TabItem>
402402
</Tabs>
403403

404+
405+
## Discriminators
406+
407+
Typegoose supports mongoose [discriminators](https://mongoosejs.com/docs/discriminators.html). `nestjs-query` provides support for them through the `NestjsQueryTypegooseModule`.
408+
409+
To use discriminators you need to define them in your `NestjsQueryTypegooseModule.forFeature` call.
410+
411+
When working with discriminators, `nestjs-query` requires you to provide a service class for each discriminated entity. This is necessary for the auto-generated resolvers to be created correctly. These service classes are also the perfect place to add any custom business logic for your discriminated entities.
412+
413+
```ts title="todo-item/todo-item.module.ts"
414+
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
415+
import { NestjsQueryTypegooseModule, TypegooseQueryService } from '@ptc-org/nestjs-query-typegoose';
416+
import { Module } from '@nestjs/common';
417+
import { TodoItemEntity } from './entities/todo-item.entity';
418+
import { TodoTaskEntity } from './entities/todo-task.entity';
419+
import { TodoAppointmentEntity } from './entities/todo-appointment.entity';
420+
import { TodoItemDTO } from './dto/todo-item.dto';
421+
import { TodoTaskDTO } from './dto/todo-task.dto';
422+
import { TodoAppointmentDTO } from './dto/todo-appointment.dto';
423+
import { CreateTodoTaskInput } from './dto/create-todo-task.input';
424+
import { CreateTodoAppointmentInput } from './dto/create-todo-appointment.input';
425+
import { InjectModel } from '@m8a/nestjs-typegoose';
426+
import { ReturnModelType } from '@typegoose/typegoose';
427+
428+
const typegooseModule = NestjsQueryTypegooseModule.forFeature([
429+
{
430+
typegooseClass: TodoItemEntity,
431+
discriminators: [
432+
{ typegooseClass: TodoTaskEntity },
433+
{ typegooseClass: TodoAppointmentEntity }
434+
]
435+
}
436+
]);
437+
438+
439+
class TodoTaskEntityService extends TypegooseQueryService<TodoTaskEntity> {
440+
constructor(@InjectModel(TodoTaskEntity) readonly model: ReturnModelType<typeof TodoTaskEntity>) {
441+
super(model)
442+
}
443+
}
444+
445+
class TodoAppointmentService extends TypegooseQueryService<TodoAppointmentEntity> {
446+
constructor(@InjectModel(TodoAppointmentEntity) readonly model: ReturnModelType<typeof TodoAppointmentEntity>) {
447+
super(model)
448+
}
449+
}
450+
451+
@Module({
452+
imports: [
453+
NestjsQueryGraphQLModule.forFeature({
454+
imports: [typegooseModule],
455+
resolvers: [
456+
{
457+
DTOClass: TodoItemDTO,
458+
EntityClass: TodoItemEntity,
459+
create: {disabled: true}, // Disable create for the base entity
460+
update: {disabled: true} // Disable update for the base entity
461+
},
462+
{
463+
DTOClass: TodoTaskDTO,
464+
EntityClass: TodoTaskEntity,
465+
// Tell the resolver to use our custom input for the create operation
466+
CreateDTOClass: CreateTodoTaskInput,
467+
ServiceClass: TodoTaskEntityService
468+
},
469+
{
470+
DTOClass: TodoAppointmentDTO,
471+
EntityClass: TodoAppointmentEntity,
472+
// Tell the resolver to use our custom input for the create operation
473+
CreateDTOClass: CreateTodoAppointmentInput,
474+
ServiceClass: TodoAppointmentService
475+
}
476+
],
477+
services: [
478+
TodoTaskEntityService,
479+
TodoAppointmentService
480+
]
481+
})
482+
]
483+
})
484+
export class TodoItemModule {}
485+
```

examples/nest-cli.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,15 @@
173173
"tsConfigPath": "./tsconfig.json"
174174
}
175175
},
176+
"typegoose-discriminators": {
177+
"type": "application",
178+
"root": "typegoose-discriminators",
179+
"entryFile": "main",
180+
"sourceRoot": "typegoose-discriminators/src",
181+
"compilerOptions": {
182+
"tsConfigPath": "./tsconfig.json"
183+
}
184+
},
176185
"typeorm": {
177186
"type": "application",
178187
"root": "typeorm",
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export const TODO_TASK_FRAGMENT = `
2+
fragment TodoTaskFragment on TodoTask {
3+
id
4+
title
5+
completed
6+
documentType
7+
priority
8+
}
9+
`
10+
11+
export const TODO_APPOINTMENT_FRAGMENT = `
12+
fragment TodoAppointmentFragment on TodoAppointment {
13+
id
14+
title
15+
completed
16+
documentType
17+
dateTime
18+
participants
19+
}
20+
`

0 commit comments

Comments
 (0)