-
Notifications
You must be signed in to change notification settings - Fork 860
[RFC] Datamodel v1.1 - Polymorphic Relations #3407
Comments
Impact of Polymorphic Relations on the APIGenerally speaking we need to enable polymorphic input types so that the user can choose which type should be affected by a mutation. But polymorphic input types are not possible in GraphQL. Therefore we try to emulate them through composition of existing input types. E.g.: input FacebookUserCreateInput {
nick: String!
facebookId: String!
}
input GoogleUserCreateInput {
nick: String!
googleId: String!
}
# poor mans version of:
# type UserCreateInput = FacebookUserCreateInput | GoogleUserCreateInput
input UserCreateInput {
facebookUser: FacebookUserCreateInput
googleUser: GoogleUserCreateInput
} This way we can mostly rely on the validation of the GraphQL server for the schema. The only validation that we have to is that exactly one of the fields must be provided in the input type. The following examples show GraphQL snippets for mutations to demonstrate the impact of polymorphic relations on Prismas OpenCRUD API. Createmutation {
createUser(
facebookUser: {
data: {
nick: "thezuck"
facebookId: "cjn0..."
}
}
){ id }
} Updatemutation {
updateUser(
facebookUser: {
where: { nick: "thezuck" }
data: { facebookId: "cjn0..." }
}
)
}{ id } Upsertmutation {
upsertUser(
facebookUser: {
where: { nick: "thezuck" }
update: { facebookId: "cjn0..." }
create: { nick: "thezuck" facebookId: "cjn0..." }
}
)
}{ id } Nested Connectmutation {
createComment(data:{
text: "this is a comment"
author: {
connect: {
facebookUser: { nick: "thezuck" }
# OR: googleUser: { nick: "pichi" }
}
}
})
} In the case of inheritance structures that specify a unique field on the inheritance type may also use the following: mutation {
createComment(data:{
text: "this is a comment"
author: {
connect: {
user: { nick: "thezuck" }
}
}
})
} Nested Disconnectfor single relation fields mutation {
updateComment(
where: { id: "cjn0..." }
data:{
text: "this is an updated comment"
author: {
disconnect: true
}
}
)
} For list relation fields: mutation {
updateComment(
where: { id: "cjn0..." }
data:{
text: "this is an updated comment"
authors: {
disconnect: [
{ facebookUser: { nick: "thezuck" } },
{ googleUser: { nick: "pichi" } },
]
}
}
)
} In the case of inheritance structures that specify a unique field on the inheritance type may also use the following: mutation {
updateComment(
where: { id: "cjn0..." }
data:{
text: "this is an updated comment"
authors: {
disconnect: [
{ user: { nick: "pichi" } },
]
}
}
)
} Nested Createmutation {
createComment(data:{
text: "this is a comment"
author: {
create: {
facebookUser: {
nick: "thezuck"
facebookId: "cjn0..."
}
}
}
})
} Nested Updatemutation {
updateComment(
where: { id: "cjn0..." }
data:{
text: "this is an updated comment"
author: {
update: {
facebookUser: {
where: { nick: "thezuck" }
data: { facebookId: "cjn0..." }
}
}
}
}
)
} Nested Upsertmutation {
updateComment(
where: { id: "cjn0..." }
data:{
text: "this is an updated comment"
author: {
upsert: {
facebookUser: {
where: { nick: "thezuck" }
update: { facebookId: "cjn0..." }
create: { nick: "thezuck" facebookId: "cjn0..." }
}
}
}
}
)
} Nested Deletefor single relation fields mutation {
updateComment(
where: { id: "cjn0..." }
data:{
text: "this is an updated comment"
author: {
delete: true
}
}
)
} For list relation fields: mutation {
updateComment(
where: { id: "cjn0..." }
data:{
text: "this is an updated comment"
authors: {
delete: [
{ facebookUser: { nick: "thezuck" } },
{ googleUser: { nick: "pichi" } },
]
}
}
)
} In the case of inheritance structures that specify a unique field on the inheritance type may also use the following: mutation {
updateComment(
where: { id: "cjn0..." }
data:{
text: "this is an updated comment"
authors: {
delete: [
{ user: { nick: "pichi" } },
]
}
}
)
} |
All looks good, I'm very eager to get my hands on this!!! 😄 |
So nice i'll be damn right to get my hands on this... just hoping when likely the community can get this in... |
interface Master @inheritance @discriminator(name: "type") {
id: ID! @id
# ...
}
type AType implements Master @discriminator(value: "A") {
details: BankAccount!
}
type BType implements Master @discriminator(value: "B") {
details: CreditCardInformation!
} |
@schickling Is there any way to try this out? I have tried the |
|
@williamluke4 : This is not implemented yet. This RFC is still in the draft status. |
This whole Datamodel v1.1 spec is just awesome! Thanks guys! 😎 However, I hope I'm not too late in the game to make a suggestion 🤓 While working on some code to generate GraphQL data models, I've stumbled upon a naming-things-right-problem regarding the discriminator directive. Doing a quick recollection of how I've used APIs that solved a similar problem, I came up with descriptor. A quicklook at the Oxford English Dictionary confirmed my gut feeling defining as: "Descriptor Computing a piece of stored data that indicates how other data is stored. Just to test my theory I went ahead and generated some of my GraphQL test-models which look identical to this spec, with the renamed directive. What I've found out is that the resulting code is easier to read, and comprehend. What do you think? interface User @inheritance @descriptor(name:"type") {
id: ID! @id
nick: String! @unique
friends: [User] @relation(name: "Friends")
}
type FacebookUser implements User @descriptor(value: "facebook") {
facebookId: String!
}
type GoogleUser implements User @descriptor(value: "google") {
googleId: String!
}
type Comment {
id: ID! @id
text: String!
author: User! @relation(link: INLINE) @descriptor(name: "author_type")
}
union User = FacebookUser | GoogleUser
type FacebookUser @descriptor(value: "facebook") {
id: ID! @id
nick: String! @unique
facebookId: String!
}
type GoogleUser @descriptor(value: "google") {
id: ID! @id
nick: String! @unique
googleId: String!
} |
@untouchable : Thanks for your suggestion. 🙏 The term descriptor make sense but our research indicates that the commonly used term for this functionality is discriminator in ORMs. But we will keep your feedback in mind in case more people struggle with it. |
@mavilein : thanks for you reply. 🙏 but I wouldn't call it a struggle as there's no known movement for the lexicographically challenged 😅 honestly, conceding to historical conventions is part of the job description. |
I think you have a valid point here @untouchable. Let's discuss the pros/cons a bit more. I agree that writing out |
|
graphql/graphql-spec#488 (comment) Oh @sorenbs from Prisma has already been contributing to the union discussion 😄 |
Regarding type User @inheritable(baseName: "User") {
id: ID! @id
nick: String! @unique
friends: [User] @relation(name: "Friends")
}
type FacebookUser implements User @inherit(name: "Facebook") {
facebookId: String!
}
type GoogleUser implements User @inherit(name: "Google") {
googleId: String!
} Should generates:
This should satisfy 3NF, and it is much more prototypical like we could do with NoSQL. And we could update the user category table easier in the future (e.g. expand more types). However right now this is only constructed on the basis of classical single inheritance, to implement multiple inheritance/union remains a hard question. |
When using Mongo, would it be possible to combine this with @Embedded so the varying data is stored directly in the document rather than a related collection? |
Any idea when this can be added to the beta? |
Has this been scrapped? Are there any updates on this? |
Same as above. There were many discussions around the various aspects of inheritance and polymorphism - all the way to a formal proposal and RFC from the Prisma team. Then the whole thing wait very quiet. I'm now reading about Prisma2, but still no update on this important topic. |
Also very interested in this as it would help a lot when implementing a graph schema with vertexes and edges. Having Vertex and Edge interfaces that I can extend would be very valuable. @mavilein any updates on this? Will it be included in Prisma 2 / Photon? |
@nikolasburk @mavilein Could we get some information on the state of this? |
@williamluke4 please note that we're not working on new features for Prisma 1 (more info here). However, polymorphic relations are certainly a feature we're thinking about in the context of Prisma 2, you can track the development in this GitHub issue: prisma/prisma#253 |
Thanks, @nikolasburk It's probably worth closing this then :) |
This part of the spec describes the syntax for polymorphic relations. See the issue #3408 to learn about the other parts of the spec.
tables created with: https://stackedit.io/app
Introduction
Interfaces and unions are both a means to model polymorphic relations. Consider the following two examples. The first example shows a union can be used to model a polymorphic relationship where the types won't be stored in the same table/collection. The second example shows how the types can be stored within the same table/collection.
Example: Modeling a polymorphic relation with a union. In this case the types
FacebookUser
andGoogleUser
will be stored within different tables/collections in the database. This approach is recommended if the types do not have much in common. This approach is very similar to Rails Active Record implementation for polymorphic relations.Example: Modeling a polymorphic relation with an interface. In this case the type
FacebookUser
andGoogleUser
inherit all the fields from the super typeUser
. In this case the typesFacebookUser
andGoogleUser
will be stored with a single table/collection calledUser
. This approach is recommended if types share common fields and are often accessed together.The
inheritance
directiveuser
andusers
query.The
discriminator
directiveinheritance
On types
On interfaces and relation fields
discriminator
exists in the inheritance hierarchy.discriminator
._discriminator
, e.g.user_discriminator
.A
type
unionuser
orusers
query.Examples
A Union
In this example
FacebookUser
andGoogleUser
would be stored in different tables or collections. The discriminator is stored in the relation link in this case.Semantic Subtleties:
author
in the tableComment
because it is referencing two tables.The resulting database schema would look like this:
An interface
In this example
FacebookUser
andGoogleUser
would be stored in the same table or collection, which would contain the discriminator.Semantic Subtleties:
googleId
andfacebookId
will not map to required columns in a SQL database. Prisma will guarantee that this constraint is met.The resulting database schema would look like this:
Polymorphic self relations
Polymorphic self relations are only possible with inheritance interfaces because unions do not define any common fields.
The resulting database schema would look like this:
The text was updated successfully, but these errors were encountered: