Soft Delete

I am looking at the possibility of adding soft deletes into my graph. I am thinking this would be useful to restore accidentally deleted data. Thinking there should be a clean up process periodically to delete any soft deleted nodes after a given duration. The more I think about how I might do this, the more I think of how nice it would be as a core feature.

Here is what I am thinking so far to accomplish this.

  • Map a delete predicate (or maybe even and edge for user tracking) onto every type that needs a soft delete.
  • To help simplify DQL queries, use the @dgraph directive to map to a single predicate instead of the GraphQL type dotted notation used for predicates.
  • Add @auth query rules that prevent users from reading nodes that have this soft delete.
  • Somehow add a query rule that bypasses this rule above for when users want to search for deleted nodes as well.
  • Create a script that cleans up the database permenantly deleting any nodes that have been pending delete for X days.

Has anyone else implemented soft deletes? How did you do it?

@chengxuncc I found an old post where you did something similar to this before GraphQL, how is that working for you?

1 Like

I think this feature would be also very nice to have.

But I think it should be possible today using @auth directive? You could just extend the permission query to also check the deletedAt field? I will try to stitch something together, brb.

Edit Here is a working example:

Schema:

type Foo
  @auth(
    query: {
      and: [
        {
          or: [
            {
              rule: "query {queryFoo(filter: {not: {has: deletedAt}}) {deletedAt}}"
            }
            { rule: "{$SHOW_SOFT_DELETED: {eq: \"true\"}}" }
          ]
        }
        {
          rule: "query($USERNAME: String!) { queryFoo {owner(filter: { username: {eq: $USERNAME }}) { username }}}"
        }
      ]
    }
  ) {
  owner: User!

  deletedAt: DateTime @search

  content: String!
}

queryFoo { content } will now return only Foos that don’t have a deletedAt Timestamp set.

SoftDeleting an object will now work using the updateFoo mutation and setting the timestamp. Regular delete would still work.

To allow also querying deleted objects, set VIEW_SOFT_DELTED to true in your JWT.
I must admit, that this of course very inconvenient and requires the client to either:

  • Always request two JWTs on login (one for normal operation, one for allowing to see soft-deleted nodes)
  • Request the tokens ad-hoc

Either way, the client would need to switch tokens to get the required functionality. So integrated soft-delete would be obviously the preferred way.

Edit2
The more I think about it, the functionality you described (especially the requirement to bypass the filtering) is already implemented using update mutations for softDelete (setting the flag) and use filtered query to optionally filter out soft-deleted content or show them. Am I right?

Let’s continue this discussion here: