Experience Report for Feature Request
Update: 7/1/21 - to comply with Feature Request Template:
What you wanted to do
1.) Update nested nodes from the parent Update Mutation.
2.) Delete nested nodes from the parent Delete Mutation
3.) Choose which nodes allow this Cascade Delete and Cascade Update, as not all parent / children relationships should have this ability
What you actually did
1.) This can only be done by creating a new update mutation for EVERY SINGLE child individually. If I am updating 10 children nodes, I need 11 mutations when including the parent.
2.) This is currently impossible, even with multiple mutations, unless you query every single ID.
3.) Obviously I don’t want to delete some nested nodes like country, language, etc. Currently, all nested updates just update the connection, not the data.
Why that wasn’t great, with examples
1.) How to Update a Book’s Chapters
mutation {
updateChapter0: updateChapter(input: {
filter: { id: "0xfffd8d6aa985abef" }, set: { ... changed info here } }
) {
numUids
}
updateChapter1: updateChapter(input: {
filter: { id: "0xfffd8d6aa985abf1" }, set: { ... changed info here } }
) {
numUids
}
updateBook(input: {
filter: { id: "0xfffd8d6aa985abee" }, set: { ... changed info here } }
) {
chapter {
id
name
slug
description
}
numUids
}
}
For every single chapter you’re updating, you need to manually query the id, and create a separate mutation with a separate namespace. The should be done in one mutation like on add.
2.) How to Delete a Book (with its chapters):
- First Get all Chapter Ids:
query {
queryBook(filter: { id: "0xfffd8d6aa985abdf" }) {
chapters {
id
...
}
}
}
- Use the returned Ids to delete the chapters one by one…, then delete the book (no other way without querying first)
mutation {
deleteChapter(filter: { id: ["0xfffd8d6aa985abde", "0xfffd8d6aa985abe0"] }) {
chapter {
id
...
}
numUids
msg
}
deleteBook(filter: { id: "0xfffd8d6aa985abdf" }) {
book {
id
...
}
numUids
msg
}
}
This isn’t even one step, but several steps, and several mutations. Nested Mutations would make this one step, but it will still be several mutations.
Any external references to support your case
Original Post
There needs to be a way update deep fields, and delete deep fields.
While it is clear why this is not default behavior, not having it as an option is also a grave problem.
I realize there are many many posts on this, but in summary:
Deleting
Right now it is 100% impossible to delete nested fields without using DQL. I theoretically can query to get every single ID to do this manually, but it is not even advisable if I could, since there could be thousands. I also can’t flip the node, since it would be a nested field as well.
So, I end up with ghost nodes. Again, it is currently IMPOSSIBLE to avoid this in graphql. I understand nested filters are on the way eventually:
But, the best we can hope for if you are a cloud user like myself is November at the earliest. This again, does not guarantee (or almost guarantee since dgraph graphql is not perfect) a lack of ghost nodes.
So we need to start thinking about this now.
Updating
If I want to update one record with nested fields (say an array of data), I currently have to create a mutation for that node, plus a mutation for every single node in the array I need to update. If that array (nested field) is 10 items, I need to create 11 different mutations. Part of that problem is the lack of multiple sets in update mutations, but I can save that for a different post.
Solving the Problem
The best way to solve this problem is what @amaster507 said:
While this is way too complicated IMHO:
So we do something like this:
Type Student {
id: ID!
name: String
classes: Class @hasInverse(field: students)
...
}
Type Book {
id: ID!
name: String
...
}
Type Class {
id: ID!
...
students: [Student] @reference(onUpdate: null, onDelete: restrict)
books: [Book] @reference(onUpdate: cascade: onDelete: cascade);
}
And just like mySQL there are four options and two paramenters (onUpdate and onDelete):
options: [restrict, cascade, null, nothing]
- nothing, short for doNothing or noAction would be the default behavior for onUpdate to be backwards compatible
- cascade - delete or update
- restrict - throws error if trying to delete or update
- null - removes connection, does not delete, would be the default behavior for onDelete to be backwards compatible
(Note: Default should be a fifth option when Dgraph implements Default values)
We should be able to do this, it is simple, makes sense, and keeps Dgraph consistent.
J