feature: Polymorphic Relations
See original GitHub issueFeature Request: Polymorphic Relations
The ability to create polymorphic one-to-one
, one-to-many
, and many-to-many
relationships in Hasura. This would necessitate Hasura generating appropriate union
or interface
graphql types for the polymorphic association (or some way for the user to create these types).
- an example of a polymorphic association is a
contact list
which contains bothperson
andorganization
subscribers.
Polymorphic associations have a number of advantages over regular associations which cannot be currently achieved:
- The ability to cascade deletion of a polymorphic association (or restrict updates, etc).
- The ability to provide the appropriate domain model in your graphql schema (using
union
orinterface
types).
Some examples which could benefit from polymorphic associations:
- A database has Person, Organization, and Event records which are each associated with an Address record. The address record has a standard shape, so it might make sense to model the data with
people
,organizations
,events
, andaddresses
tables.- Currently, you might add an
address_id
column to thepeople
,organizations
, andevents
tables which acts as a foreign key to an address record. However, this does not allow you to automatically delete the address associated with a Person, Organization, or Event.- To automatically delete the address associated with a person when the person is deleted, you can manually create a postgres trigger on the
people
table. It would be nice if Hasura supplied an option to create this trigger for you.
- To automatically delete the address associated with a person when the person is deleted, you can manually create a postgres trigger on the
- Alternatively, you could add
person_addresses
,organization_addresses
, andevent_addresses
tables instead of a singleaddresses
table. However this denormalizes your database schema and unnecessarily complicates the generated graphql schema.
- Currently, you might add an
- A database has ContactList records which have a
many-to-many
association with both Person and Organization records.- Currently, this must be modeled using
organization_subscription
andperson_subscription
join tables. If a graphql client wants to be able to pull the first 10 subscribers of a contact list in alphabetical order, you must create a custom sql view. Problematically, the graphql associated with this custom SQL view cannot return an interface/union type oforganization
andperson
records. This can be worked around by returning an artificial subscription object withperson
andorganization
properties which would contain either a person or organization. However jumping through these hoops is not ideal.
- Currently, this must be modeled using
Possible implementations
As an example, Rails’ Active Record supports polymorphic associations by utilizing an additional type
column for associations. The type
column contains the name of the table, while the standard foreign key column contains the id of the table row.
- In our first example above, the
addresses
table might model its polymorphic association usingowner_id
andowner_type
columns. Theowner_type
column would contain the table of the address owner (eitherpeople
,organizations
, orevents
) while theowner_id
column would contain the rowid
of the address owner.
A major problem with the Active Record solution is that its logic is handled outside of the database in the ruby application. Active Record does not utilize a foreign key constraint for polymorphic associations because postgres can’t handle that scenerio. i.e. the Hasura graphql-engine server would be handling the logic, checking for the existance of foreign records, etc. This strategy would likely run into major problems around concurrency and would be a major change for hasura, which seems to delegate as much logic as possible to the database.
Doing a bit of research, I’ve found a few helpful stackoverflow answers on how to handle polymorphic associations within the database:
The major takeaway is that postgres/sql does not formally support polymorphic associations. Given that polymorphic associations are a very common part of many data models though, and given that polymorphic associations are a widely used aspect of graphql, it seems worthwhile to find a way of supporting them within Hasura.
Since there doesn’t appear to be an “official” way to support polymorphic associations, Hasura would need to take an opinionated route here (which, personally, I think is fine). The super-table / sub-table approach described in this Stackoverflow answer seems like a good solution: https://stackoverflow.com/a/4970646/5490505.
A super-table / sub-table approach would allow for describing both graphql interface types and union types (an interface type would be described by the super-table. A union type would also be described by the super table, but the super-table wouldn’t contain any information other than id
). Hasura would take care of joining the tables and presenting a single record to the graphql client. Hasura would also take care of handling mutations as if only a single table were being updated. Unfortunately, a super/sub table approach still wouldn’t allow for cascade deletion of a polymorphic association (perhaps automatically generating postgres triggers could help with this).
Related: #1167
Issue Analytics
- State:
- Created 4 years ago
- Reactions:96
- Comments:12 (2 by maintainers)
Any updates on that?
@cllns Yes, and while that is “slightly” better than creating separate tables (from an organization perspective), it still requires you to create a relationship view for every object type. It’s still quite clumsy, particularly when coming from a Laravel world where you don’t need to do this. You just specify a morph-map that transforms the active record classes into slugs like “model-name”, then it handles the rest for you. I’m really hoping for proper support for polymorphic relations at some point 😦