Find all orphan nodes

I am trying to find and delete all orphan nodes in DQL. That is, all nodes which only consist of an uid and no other predicates.
One method could be finding the nodes by excluding all known predicates (e.g. not has(predicate1) and not has(predicate2) and ...). Is there a better way?

That’s not possible. One thing you can do is upsert the orphan deleting its content and then mark it as “deleted” with a predicate. Só you could “see” the orphans.

Orphan nodes aren’t stored in Dgraph. If there are no incoming or outgoing edges for a node, then there’s nothing to clean up.

If you’re looking for “dangling nodes” (i.e., edges to empty nodes), then you can create a query to find those and delete the edge to the dangling node.

{
  set {
    _:new <role_name> "test123" .
    _:new <dgraph.type> "Role" .
  }
}
{
  q(func: has(role_name)){
    uid
    role_name
    dgraph.type
  }
}

Results in:

      {
        "uid": "0x45e2a8",
        "role_name": "test123",
        "dgraph.type": [
          "Role"
        ]
      }
{
  delete {
    <0x45e2a8> * * .
  }
}

Query for uid:

{
  q(func: uid(0x45e2a8)){
    uid
  }
}

Results in:

"q": [
      {
        "uid": "0x45e2a8"
      }
    ]

So the orphan node still exists.

Just saw this post: Could not delete node by uid. Message is Success, but node is still there. Slash returns different types then Ratel - #2 by MichelDiz

So the behavior above is explained, but the node is actually still “there”.

That’s fine.

Change this mutation to this

upsert {
  query {
    v as q(func: uid(0x45e2a8)) 
  }

  mutation {
    delete {
      uid(v) * * .
    }
    set {
      uid(v) <deleted> "" .
    }
  }

So now you can “recycle” it in a new upsert block.

The uid function will always return back the requested uid. This is covered in the docs: Functions - Query language

uid(<uid>), like an identity function, will return the requested UID even if the node does not have any edges.

So your query { q(func: uid(0x45e2a8)){ uid } } is returning back the expected result.

Yep, the logical way would be to use this query instead.
q(func: has(role_name)) Or some more specific.

Thank you both for the responses. It is more clear to me now.