If a node and the outgoing edge is deleted, then the outgoing edge of that node still shows up in the query response

What I want to do

I have a node that has an outgoing reverse edge. I want to delete this node and also the outgoing edge associated with it. So that when I query the data, the outgoing edges shouldn’t show up in the query response.

What I did

  1. I added new data in Dgraph, by executing the below mutation-
{
  "set": [
    {
      "name": "Red",
      "luckyColour": [
        {
          "uid": "_:Sham",
          "name": "Sham",
          "dgraph.type": "Person"
        }
      ],
      "dgraph.type": "Colour"
    },

    {
      "name": "Green",
      "luckyColour": [
        {
          "name": "John",
          "connectedTo": [
            {
              "uid": "_:Sham",
              "dgraph.type": "Person"
            }
          ],
          "dgraph.type": "Person"
        }
      ],
      "dgraph.type": "Colour"
    }
  ]
}
  1. I executed this query to delete the node having the name Sham and all the outgoing edges belonging to this node-
{
  delete {
	<0x17966> * * .
  }
}

According to this https://dgraph.io/docs/mutations/delete/#wildcard-delete, the above mutation should delete the node and all the outgoing edges(including reverse edges) of this node.

The pattern S * * deletes all the known edges out of a node, any reverse edges corresponding to the removed edges, and any indexing for the removed data.

But when I execute this query-

{
  result(func:type(Colour)) @recurse(depth:3) {
   	expand(_all_)
        dgraph.type
  }
}

I get the below response. In that JSON response, if you check the node with uid= “0x17966”, it still has the “~connectedTo” edge, even though it was deleted by the above query.

{
  "result": [
    {
      "uid": "0x17965",
      "name": "Red",
      "luckyColour": [
        {
          "uid": "0x17966",
          "~connectedTo": [
            {
              "uid": "0x17964",
              "name": "John",
              "dgraph.type": [
                "Person"
              ]
            }
          ]
        }
      ],
      "dgraph.type": [
        "Colour"
      ]
    },
    {
      "uid": "0x17967",
      "name": "Green",
      "luckyColour": [
        {
          "uid": "0x17964",
          "connectedTo": [
            {
              "uid": "0x17966"
            }
          ],
          "name": "John",
          "dgraph.type": [
            "Person"
          ]
        }
      ],
      "dgraph.type": [
        "Colour"
      ]
    }
  ]
}

Dgraph metadata

This is the schema for the above data-

<connectedTo>: [uid] @reverse .
<dgraph.drop.op>: string .
<dgraph.graphql.p_query>: string @index(sha256) .
<dgraph.graphql.schema>: string .
<dgraph.graphql.xid>: string @index(exact) @upsert .
<luckyColor>: [uid] .
<luckyColour>: [uid] .
<name>: string .
type <Colour> {
	name
	luckyColour
}
type <Person> {
	<~connectedTo>
	connectedTo
	name
}
type <colour> {
	name
	luckyColor
}
type <dgraph.graphql> {
	dgraph.graphql.schema
	dgraph.graphql.xid
}
type <dgraph.graphql.persisted_query> {
	dgraph.graphql.p_query
}

Dgraph version- v21.03.0

Some more observations-

1) The outgoing edge does not show up in the query response if we directly query the deleted node-
This is the query I executed:

{
  result(func:uid(0x17966)) @recurse(depth:4){
    uid
     expand(_all_)
    dgraph.type
  }
}

I got the below response. The outgoing edge “~connectedTo” is not present in the response-

{
    "result": [
      {
        "uid": "0x17966"
      }
    ]
}

2) If I query for both John and Sham nodes, then the outgoing edge “~connectedTo” shows up in the query response.
I executed this query:

{
  result(func:uid(0x17964, 0x17966)) @recurse(depth:4){
    uid
   	expand(_all_)
    dgraph.type
  }
}

and got the below response. The outgoing edge “~connectedTo” is present inside the deleted node with uid= “0x17966”

{
    "result": [
      {
        "uid": "0x17966",
        "~connectedTo": [
          {
            "uid": "0x17964",
            "name": "John",
            "dgraph.type": [
              "Person"
            ]
          }
        ]
      },
      {
        "uid": "0x17964",
        "connectedTo": [
          {
            "uid": "0x17966"
          }
        ],
        "name": "John",
        "dgraph.type": [
          "Person"
        ]
      }
    ]
  }

It seems like we see the above behavior/issue only if nodes of the same type have a bidirectional edge between them. In this case, a node of type person(John) is connected to another node which is also of type person(Sham) via an edge “connectedTo”, and we also have a reverse edge “~connectedTo” between them.

I tried the above query (observation no. 2) for a similar scenario where type of nodes connected to each other is different. I don’t see the reverse edge(outgoing edge) in the query response after deleting it.


Consider this scenario, we have a node of type Plant(Plant-Flower) connected to a node of type Flower(Rose) via an edge “flower”. Node flower(Rose) has a reverse edge “~flower” that connects to the node Plant(Plant-Flower).
Now we will delete the node having the name Rose and all the outgoing edges of this node. Below is the mutation we will execute-

{
  delete {
		< 0x179c1 > * * .
  }
}

This deletes the node Rose and the outgoing edge “~flower”. If I now execute the query present in observation no. 2, I don’t see the outgoing edge “~flower” in the response.

{
  result(func:uid(0x179c0,0x179c1)) @recurse(depth:4){
    uid
   	expand(_all_)
    dgraph.type
  }
}

Below is the response I get-

{
    "result": [
      {
        "uid": "0x179c0",
        "flower": {
          "uid": "0x179c1"
        },
        "name": "Plant-Flower",
        "dgraph.type": [
          "Plant"
        ]
      },
      {
        "uid": "0x179c1"
      }
    ]
}

I am still not sure if the above behavior that we are seeing for a node joined with a node of the same type, is expected behavior or a bug.

In general this behavior have two possibilities. 1 - Bad schema(not all predicates are in the schema). 2 - A child delete can’t delete its parents. So you need to do the operation in the parent and the child. A delete all in the target and a “delete edge” in the parent. e.g. “del (parent) [friend_of] => (child)”. Also for reverse.

And that’s not a bug. It how it is.

Cheers.

Dgraph_Ques

Hi @MichelDiz, thanks for the reply. I understand this is not a bug but the question is more around why we see different behavior in these two scenarios even if we perform the same operation and query. Consider the above image, in both the scenarios I delete the child node(Red colored node), and ideally, it should delete all the outgoing edges(edges in red color) and associated predicates. In both scenarios, all the associated predicates are successfully deleted but the outgoing reverse edge is still visible in scenario-1 if I do the below query-

{
  result(func:uid({uid of parent node},{uid of child node})) @recurse(depth:4){
    uid
    expand(_all_)
    dgraph.type
  }
}

After deleting the child node and executing the above query, in scenario-1 we still see the reverse edge(~connectedTo) in the query response but in scenario-2 we don’t see the reverse edge(~flower) in the query response. The only difference between these 2 scenarios is the Type of nodes linked with eachother.