Pairs of Relationship fields (ie. "backlinks") store 2 copies of relationship data; copies not kept in sync
See original GitHub issueI have a Keystone, running on Knex/Postgres, using the latest packages (@keystonejs/keystone: 5.1.1
, @keystonejs/adapter-knex: 5.1.0
, @keystonejs/app-admin-ui: 5.0.2
, @keystonejs/app-graphql: 5.0.0
, @keystonejs/fields: 5.1.0
).
It has two related lists:
keystone.createList('User', {
fields: {
name: { type: Text },
posts: { type: Relationship, ref: 'Post.author', many: true },
// ...
},
});
keystone.createList('Post', {
fields: {
name: { type: Text },
author: { type: Relationship, ref: 'User.posts' },
// ...
},
});
There’s a bunch of data in this system already. For example, the user 1747
is author of post ABCD
. GraphQL bears this out:
{
User(where: { id: "1747" }) {
id
name
posts { id name }
}
}
Which correctly returns:
{
"data": {
"User": {
"id": "1747",
"name": "Aida Yasuaki",
"posts": [
{
"id": "ABCD",
"name": "Soroban For Dummies"
}
]
}
}
}
The Admin UI for this post gives me a drop down to select the single author (user). Updating the author to user 1598
performs a query like this:
mutation {
updatePost(
id: "ABCD"
data: {
author: { connect: { id: "1598" } }
}
) { id __typename }
}
But when we look at the data we see now both users think they own the post:
{
allUsers(where: { id_in: ["1747", "1598"] }) {
id
name
posts { id name }
}
}
Returns…
{
"data": {
"allUsers": [
{
"id": "1747",
"name": "Aida Yasuaki",
"posts": [{ "id": "ABCD", "name": "Soroban For Dummies" }]
},
{
"id": "1598",
"name": "Yoshida Mitsuyoshi",
"posts": [{ "id": "ABCD", "name": "Soroban For Dummies" }]
}
]
}
}
So, the bug here is that the Admin UI isn’t including disconnectAll: true
when setting many: false
relationships. The GraphQL mutation above should be something like:
mutation {
updatePost(
id: "ABCD"
data: {
author: { disconnectAll: true, connect: { id: "1598" } }
}
) { id __typename }
}
That specific issue shouldn’t be hard to fix but the real issue here is the concept of backlinks as a whole. We’re actually maintaining two separate relationships here: A one-to-many relationship and a separate many-to-many relationship linking the same tables. (Note that, when even when both authors think they “own” the post, the post itself links correctly to just one.)
I’ll write this larger issue separately but, sufficed to say, backlinks add complexity (surface area for bugs like this! ^^), slow down data access (reads and writes) and makes maintaining data integrity a lot harder. They’re also unnecessary 😃
Issue Analytics
- State:
- Created 4 years ago
- Comments:9 (8 by maintainers)
Top GitHub Comments
Fixed in #2000
As above, it’s not possible to store “one side” of a relationship; you either store it or you don’t. If the link between two items is stored somewhere there’s nothing left to “compute”.
We need to get far away from “backlinks” as a concept. They simply don’t exist in any meaningful way. We want to be able to configure:
There is no blessed/special/front/back side of a relationship in our conceptual model, regardless of where the config exists in the codebase.