Dgraph: GraphQL: removed nodes still visible, errors with "non-nullable field was not present"

Created on 4 May 2020  路  2Comments  路  Source: dgraph-io/dgraph

What version of Dgraph are you using?

v20.03.1 and master

Have you tried reproducing the issue with the latest release?

yes

What is the hardware spec (RAM, OS)?

linux (not relevant)

Steps to reproduce the issue (command/config used to run Dgraph).

Load GraphQL schema:

interface Record {
  id: ID!
}

type Parent implements Record {
    name: String
    children: [Child]
}

type Child implements Record {
  label: String! @search(by: [hash])
}

Add records:

mutation AddParent {
  addParent (input:[name:"First parent",children:[
    {label:"Child 1"},
    {label:"Child 2"},
    {label:"Child 3"}
  ]}]) {
    numUids
  } 
}

Remove one child record by id:

mutation RemoveChild {
  deleteChild (filter:{id:["0x3"]}) {
    msg
    numUids
  }
}

List all parent records with children:

query ListParents {
  queryParent 
  {
    name
    id
    children {
      id
      label
    }
  }
}

Expected behaviour and actual result.

Expected result:

{
  "data": {
    "queryParent": [
      {
        "name": "First parent",
        "id": "0x4",
        "children": [
          {
            "id": "0x2",
            "label": "Child 3"
          },
          {
            "id": "0x5",
            "label": "Child 2"
          }
        ]
      }
    ]
  }
}

Actual result:

{
  "errors": [
    {
      "message": "Non-nullable field 'label' (type String!) was not present in result from Dgraph.  GraphQL error propagation triggered.",
      "locations": [
        {
          "line": 8,
          "column": 7
        }
      ],
      "path": [
        "queryParent",
        0,
        "children",
        1,
        "label"
      ]
    }
  ],
  "data": {
    "queryParent": [
      {
        "name": "First parent",
        "id": "0x4",
        "children": [
          {
            "id": "0x2",
            "label": "Child 3"
          },
          null,
          {
            "id": "0x5",
            "label": "Child 2"
          }
        ]
      }
    ]
  }
}

I think the null record (and the error message) should not be returned.

aregraphql kinquestion

Most helpful comment

Hi @miko

This is expected behaviour. When you delete a node, the incoming edges to it are not deleted, only the outgoing edges are. Since the child doesn't have any outgoing edges to the Parent, they are not deleted. Also, the node itself remains, so there still remains a node with id 0x3 and the parent node would still point to it.

first_diagram

Hence, when you do the query you get the error because label field has been deleted for the node with id 0x3. If you ran the query, without asking for the label, no error would be returned.

query ListParents {
  queryParent 
  {
    name
    id
    children {
      id
    }
  }
}

If you want the cleanup of the parent => child edge to happen automatically, you'll have to modify the schema to have the @hasInverse directive i.e.

interface Record {
  id: ID!
}

type Parent implements Record {
  name: String
  children: [Child] @hasInverse(field: parent)
}

type Child implements Record {
  label: String! @search(by: [hash])
  parent: [Parent]
}

in this case your Graph would look like the diagram below
second_diagram

so when you delete a child node, we would follow along to all the outgoing edges and delete the inward edges coming from them. In your case, we would delete the link from parent 0x4 to child 0x3. I am closing this issue but feel free to reopen if you have anymore questions.

All 2 comments

Hi @miko

This is expected behaviour. When you delete a node, the incoming edges to it are not deleted, only the outgoing edges are. Since the child doesn't have any outgoing edges to the Parent, they are not deleted. Also, the node itself remains, so there still remains a node with id 0x3 and the parent node would still point to it.

first_diagram

Hence, when you do the query you get the error because label field has been deleted for the node with id 0x3. If you ran the query, without asking for the label, no error would be returned.

query ListParents {
  queryParent 
  {
    name
    id
    children {
      id
    }
  }
}

If you want the cleanup of the parent => child edge to happen automatically, you'll have to modify the schema to have the @hasInverse directive i.e.

interface Record {
  id: ID!
}

type Parent implements Record {
  name: String
  children: [Child] @hasInverse(field: parent)
}

type Child implements Record {
  label: String! @search(by: [hash])
  parent: [Parent]
}

in this case your Graph would look like the diagram below
second_diagram

so when you delete a child node, we would follow along to all the outgoing edges and delete the inward edges coming from them. In your case, we would delete the link from parent 0x4 to child 0x3. I am closing this issue but feel free to reopen if you have anymore questions.

Great explanation. Can it be added to the documentation?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

andrewsmedina picture andrewsmedina  路  4Comments

KadoBOT picture KadoBOT  路  5Comments

bytefish picture bytefish  路  4Comments

captain-me0w picture captain-me0w  路  4Comments

MichelDiz picture MichelDiz  路  3Comments