Why is it that if I delete a node in Ratel, I can still query for it?

and it still shows up as a child node in other queries:

is this because it’s only meaningful to delete the edges?

Answered my own question, I think. This solved my problem:

{
  delete {
    <0x4220d> <Lexicon.lexicalEntries> <0x445c8> .
  }
}

However, still wondering whether it’s actually possible to delete a node??

This is an old situation that comes and goes again and again. Dgraph does not delete system UIDs. Because it is a fixed range, a fixed length address. It expands and there is no way to delete addresses or “jump”. It’s like a RAM(virtual?) memory that allocates more addresses to be used. Previous addresses cannot be deleted. It is also comparable to Blockchain(in a very abstract way), you cannot erase previous blocks(prune). So by design these addresses will exist until the end of the DB’s life. So this design creates nodes that are called “null nodes”.

However, we see that you also have incoming dangling edges. These you really need to clean manually through logic in your code. There are several ways to do this. It’s almost like a manual “garbage collection” control.

Another thing you can do when deleting a node. Instead of letting it become a null node. You can put(after using delete) a predicate(as a label e.g: “deleted”) telling your program (application) that that UID is free to be used elsewhere. It’s a way. But in the end letting null nodes appear is not a problem that you should be worried about too much.

3 Likes

There are two things that come together here that make the situation confusing.

The first is that Dgraph only tracks edges and fields. Nodes exist implicitly because they are the from-uid of an edge (and sometimes the to-uid). (NB: in RDF terminology the from/to uids are called the Subject and Object). So it’s a little bit inaccurate to talk about deleting a node - but we use that terminology anyway to mean deleting all edges and scalar fields associated with the node.

The second is that the wildcard delete uses the type system to know which edges and fields to delete to accomplish a full delete. For the wildcard delete to work (with a wildcard in the 2nd “predicate” position) you need BOTH a dgraph.type field on the node/uid so the system knows what type to use for the delete AND the current DQL schema has to mention all the fields and edges. Then the delete will work.

This is why explicitly using the edge <Lexicon.lexicalEntries> can work in some cases where * fails to delete all edges; typically a wildcard delete failed because dgraph.type is missing from the node; typically a wildcard delete failed because dgraph.type is missing from the node.

A third item to note: the uid in a func:uid() based query like q(func:uid(0xffffffffffff)) { uid } returns the uid from the list, even if the uid is not in the DB. Every query selection set (field list at some level) is relative to the parent list of uids, so they are not looked up, but rather inherited. If you supply the uid in the query, it will be returned even if it is has no fields or edges and/or has never been used. So querying that way does not tell you if the node “exists” meaning it does not check if the node is used in real edges/fields.

The best way to avoid this seemingly undeleted nodes is to add the type filter to every query. But this requires you to use types, which may or may not cause other problems if you start dealing with huge amounts of data because all of the types are stored individually for each node in a single tablet that cannot be split. So try to limit the length of your type names.