question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

docs: graphql query with optional `where` can return incorrect results

See original GitHub issue

Bug summary

If a graphql query includes an optional where clause which filters on a relation (e.g. Form(where: { opportunity: { id: { _eq: $opportunityId} } })) and the variable associated with the where clause is undefined (i.e. the where clause should be ignored) that query will always return an empty result set if the relation (in this case Form.opportunity) has no records.

The example schema

  • In the following schema, both ContactList and Opportunity have one associated Form object.
CREATE TABLE "Form" (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  name text
);

CREATE TABLE "Opportunity" (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  "formId" uuid NOT NULL REFERENCES "Form" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
);

CREATE TABLE "ContactList" (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  "formId" uuid NOT NULL REFERENCES "Form" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
);

The example query

Assume the database contains one ContactList record with an associated Form record, but does not contain any Opportunity records.

# Important! the `opportunityId` variable is undefined (i.e. should be ignored)

query FormQuery($opportunityId: uuid){
  Form(where: {
    opportunity: { id: { _eq: $opportunityId} }
  }) {
    id
    name
  }
}

Expected result

  • A single Form object should be returned (the one associated with the ContactList)

Actual result

  • An empty array is returned.

Reproduction

A reproduction can be seen here: https://hasura-issue-4214.herokuapp.com/console/api-explorer.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
0x777commented, Jun 11, 2019

undefined doesn’t exist in GraphQL. Your js client will probably not send the variable to the server when you it is undefined or set it to null (depends on the client).

At the server, if the variable is declared to be nullable (as is the case in your example) the variable having an explicit null or the variable being absent are treated the same, i.e, the variable gets a null value. So your gets translated to this:

query FormQuery {
  Form(where: {
    opportunity: { id: { _eq: null} }
  }) {
    id
    name
  }
}

So the behaviour that you are seeing is because of how we handle null values for _operator fields. When we a see a clause like {_eq: null}, we ignore it. However, in your case, you are doing it inside a relationship. Now your query becomes:

query FormQuery {
  Form(where: {
    opportunity: {}
  }) {
    id
    name
  }
}

which means that you are querying for forms where there exists an opportunity. To get the behaviour that you need you can either construct the where clause dynamically by making it a variable or user the _or and _and operators.

2reactions
mikyoenginecommented, Jan 28, 2020

@0x777 Thanks for the detailed explanation. This was very helpful and is very much needed in the documentation. I think the confusion arrises when developers start creating filterable lists. In my case I stated by adding filters via parameters e.g.

query Users($country: String) {
  users(where: { country: { _eq: $country }})
}

and these filters worked great for columns on user but not for something like

query Users($country: String, $interests: [String!]) {
  users(where: { country: { _eq: $country } user_interests: { topic: { _in: $interests } } })
}

as it ends up filtering out all users that don’t have entries in user_interests when $interests is not set (which is not immediately obvious IMO)

As mentioned, using the _and to add dynamic elements to the query proved to be the best solution for me. E.g.

query Users($country: String, $dynamicFilters: [user_bool_exp]) {
  users(where: { country: { _eq: $country } _and: dynamicFilters })
}

Hope this simple example proves useful to others in a similar boat.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Error handling - Apollo GraphQL Docs
A client sent the hash of a query string to execute via automatic persisted queries, but the server has disabled APQ. OPERATION_RESOLUTION_FAILURE. The...
Read more >
Queries and Mutations - GraphQL
On this page, you'll learn in detail about how to query a GraphQL server. Fields#. At its simplest, GraphQL is about asking for...
Read more >
Nulls in GraphQL: Cheatsheet - Hasura
A GraphQL query can have fields and inputs, with or without variables. Fields are always optional, whether nullable or not. ... In inputs,...
Read more >
Resolver mapping template programming guide - AWS AppSync
If you run your GraphQL query, the data will be returned as normal. However, if you change the id argument to something other...
Read more >
Optional variable in Graphql - Stack Overflow
You'll just have to update the client that's actually issuing the query to use the right conditional logic regarding whether or not the ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found