Slash Graphql Error on query

Hi,

So i have this query

query u($b: [ID!]){
        queryProduct {
          id
          name
          isSoldBy(filter: {id: $b}) {
            __typename
          }
          size
        }
      }

This works locally on my dgraph image 20.04 standalone image. When I run the same on slash graphql it fails.

{"data":{"queryProduct":[null]},"errors":[{"locations":[{"column":11,"line":5}],"message":"Non-nullable field 'isSoldBy' (type Account!) was not present in result from Dgraph.  GraphQL error propagation triggered.","path":["queryProduct",0,"isSoldBy"]}]}

Schema

type Product implements Node 
@auth(
	add: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"True\" } }" },
		{ rule: "{ $role: { eq: \"USER\" } }"},
		{ rule: """
		 	query($email: String!) {
								queryProduct {
											user(filter: { username: { eq: $email}}) {
												username
											}
									}
				}
			"""
		},
	]},
	query: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"True\" } }" },
		{ rule: "{ $role: { eq: \"USER\" } }"},
		{ rule: """
		 	query($email: String!) {
								queryProduct {
											user(filter: { username: { eq: $email}}) {
												username
											}
									}
								}
				"""
		},
	]},
	update: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"True\" } }" },
		{ rule: "{ $role: { eq: \"USER\" } }"},
		{ rule: """
		 	query($email: String!) {
								queryProduct {
											user(filter: { username: { eq: $email}}) {
												username
											}
									}
								}
				"""
		},
	]},
	delete: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"True\" } }" },
		{ rule: "{ $role: { eq: \"USER\" } }"},
		{ rule: """query($email: String!) {
								queryProduct {
											user(filter: { username: { eq: $email}}) {
												username
											}
									}
								}
				"""
		},
	]},
)
{
    name: String! @search(by: [fulltext])
    size: String! @search
    user: User!
    isSoldBy: Account! @hasInverse(field: products)
}
}

@slotlocker2, @arijit

Was this: isSoldBy: Account! not required previously? (i.e. did it not have, exclamation mark before?)

When new data is added, this constraint can be applied. (i.e in a fresh local instance, this schema will work because every new record will have isSoldBy value!)

But if you change the schema later, and the existing data doesn’t already have the isSoldBy value, then you’ll get that error.

So you have to migrate existing data to match the new schema change.

1 Like

Hi, Abhijit, I am observing this behaviour when there is no data. Or an entry was made if a different account. I am happy for you too at my endpoint on slash.

It appears that reason why query is failing is as it has null data and it is trying to enforce Not null constraint on null data.

I have tried to re-drop the data and create everything from scratch.

Did you try, @cascade directive?

Documentation: @cascade Directive - GraphQL

Excerpt:

With the @cascade directive, nodes that don’t have all predicates specified in the query are removed. This can be useful in cases where some filter was applied or if nodes might not have all listed predicates.

And remove exclamation mark from the isSoldBy predicate:
isSoldBy: Account! @hasInverse(field: products)

Hi Abhijit but i am creating nodes with actual data . Let me show you.

mutation AP($data: AddProductInput!) {
    addProduct(input: [$data]) {
        product {
            id
        }
    }
}

Variables

{
  "variables": {
    "data": {
      "name": "Product2",
      "size": "11*3",
      "designs": [
        {
          "id": null,
          "url": "https://someurl",
          "thumbnail": null,
          "notes": "some notes",
          "version": 1,
          "modifiedBy": "harshad",
          "modifiedOn": "2020-08-26T20:23:43.880",
          "user": {
            "id": "0x9c43"
          }
        }
      ],
      "user": {
        "id": "0x9c43"
      },
      "isSoldBy": {
        "id": "0x9c47"
      }
    }
  }
}

Response

{"data":{"addProduct":{"product":[{"id":"0x9c4b"}]}},"errors":null}

Query

{
    query u($b: [ID!]){
        queryProduct {
            id
            name
            isSoldBy(filter: {id: $b}) {
                __typename
            }
            size
            designs {
                id
                version
            }
        }
    }
}

Variables

variables: {b: ["0x9c47"]}

Result with errors:

{
  "data": {
    "queryProduct": [
      null,
      {
        "designs": [
          {
            "id": "0x9c4a",
            "version": 1
          }
        ],
        "id": "0x9c4b",
        "isSoldBy": {
          "__typename": "Account"
        },
        "name": "Product2",
        "size": "11"
      }
    ]
  },
  "errors": [
    {
      "locations": [
        {
          "column": 11,
          "line": 5
        }
      ],
      "message": "Non-nullable field 'isSoldBy' (type Account!) was not present in result from Dgraph.  GraphQL error propagation triggered.",
      "path": [
        "queryProduct",
        0,
        "isSoldBy"
      ]
    }
  ]
}

Happy to do a live session to show this.

Ok if i remove the ! after Account and apply cascade . It works !!

However, that should not be the case as if the node is created with id field, it should not complain that it is not present.

{
    query {
        queryProduct {
            id
            name
            isSoldBy {
                __typename
            }
            size
            designs {
                id
                version
            }
        }
    }
}

What does the query without the filter on isSoldBy return? I am guessing since you have two products, both have different isSoldBy account associated with it. Only of them passes the filter and the other one returns an error.

Hi Pawan,

I created a product under different account. So in the database there is only one other product.

Result without any filter

{"data":{"queryProduct":[{"designs":[{"id":"0x9c52","version":1}],"id":"0x9c51","isSoldBy":{"id":"0x9c4f"},"name":"Product2","size":"11*3"}]},"errors":null}

I hav recreated the data so IDs might be different.

{"data":{"queryProduct":[{"designs":[{"id":"0x9c58","version":1}],"id":"0x9c57","isSoldBy":null,"name":"Product1","size":"11*3"},{"designs":[{"id":"0x9c59","version":1}],"id":"0x9c5a","isSoldBy":null,"name":"Product2","size":"8*8"}]},"errors":null}

When i remove the ! from IsSoldBy now my data is created without the record.

What query is this the response to? I am trying to find out if this is a bug on our end or some misunderstanding about how this works. Removing ! should not cause the data to be created without the record.

1 Like

The one you asked me to run. Returns all data with no filters.

hi @pawan, that was attempt to get queries working as advised by abhijit.

Yes, I believe there is a defect on slash side as you can see data is being created as null values despite being provided and subsequently query fails which makes sense as it is trying to enforce not null constraint for which it has null data.

Can you share a reproducible example for this program with exact steps so that we can reproduce and fix this on our side? This can also be a Go program if you like.

Hi Pawan,

Above i have included the schema , queries and mutation I am performing via curl. The endpoint is simply my slash url. I am happy to define the steps.

  1. Create the schema like

interface Node {
  id: ID!
}

type Account implements Node 
@auth(
	add: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"True\" } }" },
		{ rule: "{ $role: { eq: \"USER\" } }"},
		{ rule: """
		 	query($email: String!) {
								queryAccount {
											user(filter: { username: { eq: $email}}) {
												username
											}
									}
				}
			"""
		},
	]},
	query: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"True\" } }" },
		{ rule: "{ $role: { eq: \"USER\" } }"},
		{ rule: """
		 	query($email: String!) {
								queryAccount {
											user(filter: { username: { eq: $email}}) {
												username
											}
									}
								}
				"""
		},
	]},
	update: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"True\" } }" },
		{ rule: "{ $role: { eq: \"USER\" } }"},
		{ rule: """
		 	query($email: String!) {
				queryAccount {
						user(filter: { username: { eq: $email}}) {
							username
						}
					}
				}
				"""
		},
	]},
	delete: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"True\" } }" },
		{ rule: "{ $role: { eq: \"USER\" } }"},
		{ rule: """query($email: String!) {
						queryAccount {
							user(filter: { username: { eq: $email}}) {
								username
							}
						}
					}
				"""
		},
	]},
)
{
    name: String! @search(by: [fulltext])
    gst: String! @search
	user: User!
	ofCompany: Company! @hasInverse(field: accounts)
	products: [Product!]
}



type Product implements Node 
@auth(
	add: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"True\" } }" },
		{ rule: "{ $role: { eq: \"USER\" } }"},
		{ rule: """
		 	query($email: String!) {
								queryProduct {
											user(filter: { username: { eq: $email}}) {
												username
											}
									}
				}
			"""
		},
	]},
	query: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"True\" } }" },
		{ rule: "{ $role: { eq: \"USER\" } }"},
		{ rule: """
		 	query($email: String!) {
								queryProduct {
											user(filter: { username: { eq: $email}}) {
												username
											}
									}
								}
				"""
		},
	]},
	update: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"True\" } }" },
		{ rule: "{ $role: { eq: \"USER\" } }"},
		{ rule: """
		 	query($email: String!) {
								queryProduct {
											user(filter: { username: { eq: $email}}) {
												username
											}
									}
								}
				"""
		},
	]},
	delete: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"True\" } }" },
		{ rule: "{ $role: { eq: \"USER\" } }"},
		{ rule: """query($email: String!) {
								queryProduct {
											user(filter: { username: { eq: $email}}) {
												username
											}
									}
								}
				"""
		},
	]},
)
{
    name: String! @search(by: [fulltext])
    size: String! @search
    user: User!
    isSoldBy: Account! @hasInverse(field: products)
}
}
  1. Now create an account using mutation. (Posted in post before)
  2. Now create a product using account ID as you just created. (Posted in post before)
  3. Now create a second account.
    5.Now run query on product using the newly created Account. (Posted in post before)
  4. You should get errors in your return response. (Posted in post before)

P.S. This is only happening when i am using slash. It did not happen on Docker dgraph v20.03.

Hope this helps.

Hi, thanks for detailed example. This is the expected behaviour. The second account is not linked to any product. While quering , we are filtering the account which is not linked to the product. So, we get null in account, which graphql is not accepting because field isSoldBy is non-nullable in type Product. This error will also come if you have two different products with their own accounts and if you apply filter for only one account.

The easiest solution to avoid the error that you are getting is to use @cascade at outer level. It will return only the product which has all queried fields in result and in your case it will return empty result.

{
    query u($b: [ID!]){
        queryProduct @cascade {
            id
            name
            isSoldBy(filter: {id: $b}) {
                __typename
            }
            size
            designs {
                id
                version
            }
        }
    }
}

Let me know if it doesn’t solve the issue or you need any other help in this.