FindMany nested object queries returning NULL
spencerbull opened this issue · comments
Describe the Bug
FindMany nested object queries are returning NULL for the Many-to-One query use case. For the example below, I have a database in a star schema with fact and dimension tables. The FindMany queries return valid children when making queries in a One-to-Many direction, but return NULL in a Many-to-one Direction.
To Reproduce
Using typegraphql generator with basic configuration and simplified data model
generator typegraphql {
provider = "typegraphql-prisma"
}
model DimJobRole {
JobRoleId BigInt @default(autoincrement())
JobFunctionName String @db.VarChar(128)
JobRoleEvents FactJobRoleEvent[]
}
model FactJobRoleEvent {
JobRoleEventId BigInt @id(map: "PK__1832492348")
JobRoleId BigInt
DimJobRole DimJobRole @relation(fields: [JobRoleId], references: [JobRoleId], onUpdate: NoAction, map: "FK_FactJobRoleEvents_DimJobRole"
}
Example query FindMany queries are returning null.
query FactJobRoleEvents($take: Int) {
factJobRoleEvent(take: $take) {
jobRoleEventId
jobRoleEventName
DimJobRole {
JobFunctionName
}
}
}
This query results in an error since DimJobRole cannot be null. I did confirm by making the following generated relations resolver nullable set to true. If the take
parameter is set to 1
, then the query resolves, if the query is set to >1
, then the query returns null for all children.
Attached below in the logs section are the Prisma queries that are being run with verbose debug logs.
Expected Behavior
When making a GraphQL findMany query in the direction of Many-to-One, the children should return valid objects corresponding the to the table.
Logs
For take
set to 1
prisma:client:libraryEngine sending request, this.libraryStarted: true +45ms
prisma:query SELECT ....(full table).... WHERE ([dbo].[FactJobRoleEvent].[JobRoleEventId] = @P1 AND 1=1) ORDER BY 1 OFFSET @P2 ROWS FETCH NEXT @P3 ROWS ONLY
prisma:query SELECT [dbo].[DimJobRole].[JobRoleId] FROM [dbo].[DimJobRole] WHERE [dbo].[DimJobRole].[JobRoleId] IN (@P1)
For take
set to 2
prisma:query SELECT ....(full table) WHERE 1=1 ORDER BY [dbo].[FactJobRoleEvnet].[JobRoleEventId] ASC OFFSET @P1 ROWS FETCH NEXT @P2 ROWS ONLY
prisma:query SELECT ....(query fields) .... FROM [dbo].[FactJobRoleEvnet] WHERE [dbo].[FactJobRoleEvnet].[JobRoleEventId] IN (@P1,@P2)
prisma:query SELECT ....(full table).... WHERE [dbo].[DimJobRole].[JobRoleId] IN (@P1,@P2)
Environment:
- OS: [e.g. Windows 10 22H2]
- Node (e.g. v18.15.0)
typegraphql-prisma
version 0.24.2- Prisma version 4.11.0
- TypeScript version 4.9.5
** Aditional Context **
Possibly similar to #248
I had a quick update I wanted to provide. In my relationResolvers. I found that replacing the following findUnique
to findUniqueOrThrow
is now returning values. For a temporary fix, I'm updating the generated code.
Example of the change...
Generated Relation Resolvers
@TypeGraphQL.FieldResolver(_type => DimJobRole, {
nullable: false
})
async DimJobRole(@TypeGraphQL.Root() factJobRoleEvent: FactJobRoleEvent, @TypeGraphQL.Ctx() ctx: any, @TypeGraphQL.Info() info: GraphQLResolveInfo): Promise<DimJobRole> {
const { _count } = transformInfoIntoPrismaArgs(info);
return getPrismaFromContext(ctx).factJobRoleEvent.findUnique({
where: {
FactJobEventId: factJobRoleEvent.jobRoleEventId,
},
}).DimCalendar({
...(_count && transformCountFieldIntoSelectRelationsCount(_count)),
});
}
Updated Relation Resolvers
@TypeGraphQL.FieldResolver(_type => DimJobRole, {
nullable: false
})
async DimJobRole(@TypeGraphQL.Root() factJobRoleEvent: FactJobRoleEvent, @TypeGraphQL.Ctx() ctx: any, @TypeGraphQL.Info() info: GraphQLResolveInfo): Promise<DimJobRole> {
const { _count } = transformInfoIntoPrismaArgs(info);
return getPrismaFromContext(ctx).factJobRoleEvent.findUniqueOrThrow({
where: {
FactJobEventId: factJobRoleEvent.jobRoleEventId,
},
}).DimCalendar({
...(_count && transformCountFieldIntoSelectRelationsCount(_count)),
});
}
I would recommend not marking this issue as fixed, but I did want to highlight a path forward here with an update.
I am able to reproduce the issue 👍
However, after digging a bit I think you've discovered an issue in Prisma Client built-in data-loader 😄
Let's reproduce the issue:
Schema:
model DimJobRole {
JobRoleId BigInt @id @default(autoincrement())
JobFunctionName String @db.VarChar(128)
JobRoleEvents FactJobRoleEvent[]
}
model FactJobRoleEvent {
JobRoleEventId BigInt @id(map: "PK__1832492348")
JobRoleId BigInt
DimJobRole DimJobRole @relation(fields: [JobRoleId], references: [JobRoleId], onUpdate: NoAction, map: "FK_FactJobRoleEvents_DimJobRole")
}
Seed:
const [job1, job2, job3] = await Promise.all([
prisma.dimJobRole.create({
data: { JobFunctionName: "JobFunctionName 1" },
}),
prisma.dimJobRole.create({
data: { JobFunctionName: "JobFunctionName 2" },
}),
prisma.dimJobRole.create({
data: { JobFunctionName: "JobFunctionName 3" },
}),
]);
await Promise.all([
prisma.factJobRoleEvent.create({
data: {
JobRoleEventId: 1,
DimJobRole: {
connect: { JobRoleId: job1.JobRoleId },
},
},
}),
prisma.factJobRoleEvent.create({
data: {
JobRoleEventId: 2,
DimJobRole: {
connect: { JobRoleId: job1.JobRoleId },
},
},
}),
prisma.factJobRoleEvent.create({
data: {
JobRoleEventId: 3,
DimJobRole: {
connect: { JobRoleId: job2.JobRoleId },
},
},
}),
]);
Script 1 - await one by one:
const factJobRoleEvents = await prisma.factJobRoleEvent.findMany({
take: 3,
});
console.log(factJobRoleEvents);
for (const factJobRoleEvent of factJobRoleEvents) {
const dimJobRole = await prisma.factJobRoleEvent
.findUnique({
where: {
JobRoleEventId: factJobRoleEvent.JobRoleEventId,
},
})
.DimJobRole({});
console.log({dimJobRole});
}
Logs:
[
{ JobRoleEventId: 1n, JobRoleId: 23n },
{ JobRoleEventId: 2n, JobRoleId: 23n },
{ JobRoleEventId: 3n, JobRoleId: 22n }
]
{
dimJobRole: { JobRoleId: 23n, JobFunctionName: 'JobFunctionName 1' }
}
{
dimJobRole: { JobRoleId: 23n, JobFunctionName: 'JobFunctionName 1' }
}
{
dimJobRole: { JobRoleId: 22n, JobFunctionName: 'JobFunctionName 2' }
}
Script 2 - await Promise.all (like paralel field resolvers execution):
const factJobRoleEvents = await prisma.factJobRoleEvent.findMany({
take: 3,
});
console.log(factJobRoleEvents);
await Promise.all(
factJobRoleEvents.map(async factJobRoleEvent => {
const dimJobRole = await prisma.factJobRoleEvent
.findUnique({
where: {
JobRoleEventId: factJobRoleEvent.JobRoleEventId,
},
})
.DimJobRole({});
console.log({ dimJobRole });
}),
);
Logs:
[
{ JobRoleEventId: 1n, JobRoleId: 19n },
{ JobRoleEventId: 2n, JobRoleId: 19n },
{ JobRoleEventId: 3n, JobRoleId: 21n }
]
{ dimJobRole: null }
{ dimJobRole: null }
{ dimJobRole: null }
Script 3 - promise all + OrThrow:
const factJobRoleEvents = await prisma.factJobRoleEvent.findMany({
take: 3,
});
console.log(factJobRoleEvents);
await Promise.all(
factJobRoleEvents.map(async factJobRoleEvent => {
const dimJobRole = await prisma.factJobRoleEvent
.findUniqueOrThrow({
where: {
JobRoleEventId: factJobRoleEvent.JobRoleEventId,
},
})
.DimJobRole({});
console.log({ dimJobRole });
}),
);
Logs:
[
{ JobRoleEventId: 1n, JobRoleId: 25n },
{ JobRoleEventId: 2n, JobRoleId: 25n },
{ JobRoleEventId: 3n, JobRoleId: 26n }
]
{
dimJobRole: { JobRoleId: 25n, JobFunctionName: 'JobFunctionName 1' }
}
{
dimJobRole: { JobRoleId: 25n, JobFunctionName: 'JobFunctionName 1' }
}
{
dimJobRole: { JobRoleId: 26n, JobFunctionName: 'JobFunctionName 2' }
}
Reported in Prisma:
prisma/prisma#18326
But I will fix this issue by just using findUniqueOrThrow
- it's a proper way anyway, as the record has to exist as it was returned from a parent db query 👀
Fixed in 3ea20f6 - @spencerbull let me know if the fix does not work for you 😉
This looks great thanks!