How to apply a Hasura `where` filter only if a variable is not null?
Asked Answered
M

3

20

I have a query like this:

query getUsers ($setId: Int) {
  user(where: { user_sets: { set_id: { _in: [$setId] } } }) {
    id
    name
    status
    user_sets {
      set {
        name
      }
    }
    # more fields...
  }
}

What I'm looking for, is a way to not apply the where filter and give all entries if $setId is null. I'd like to avoid dynamically writing the query - it'd be easy to do something like this, but we want queries in static .graphql files:

const query = `
query getUsers (${ setId ? '$setId: Int' : ''}) {
  user(${ setId ? 'where: { user_sets: { set_id: { _in: [$setId] } } }' : '' }) {
`

Some things I've tried:

  • Using GraphQL directives like @skip and @include, but it seems these only apply to fields returned, not to any part of a where filter
  • Using Hasura boolExps like _is_null and _or, but it seems these can't test variables directly, they can only compare variables to columns contents
Marmion answered 23/4, 2019 at 11:2 Comment(0)
M
15

This behaviour changed somewhere between v1.3.4. Therefore there are two correct answers.

You can read more about this change in the hasura repository.

Before Version 1.3.4

This answer describes it.

After Version 1.3.4

Using null in comparisons is dangerous when defaulting to true because an accidentally unset variable could result in a dropped table. The maintainers removed this behaviour but made it accessible by setting the variable HASURA_GRAPHQL_V1_BOOLEAN_NULL_COLLAPSE.

When comparing with {_eq: null}, Hasura will throw an error because it is assumed that this is a mistake.

If you want to compare to a value and evaluate to true when the value is null, you need to handle the case on the client side and pass the whole boolean expression to Hasura.

query getUsers ($userSetsWhere: user_sets_bool_exp) {
  user(where: { user_sets: { $userSetsWhere } }) {
    id
    name
    status
    user_sets {
      set {
        name
      }
    }
    # more fields...
  }
}
const userSetsWhere = setId ? { set_id: { _eq: $setId } } : {};

What it does, is that only in case the value is not null or undefined a non-empty expression gets passed to Hasura.

Mooneyham answered 16/7, 2021 at 11:40 Comment(2)
This should be the top answer since Hasura is on v2 now.Balls
Bro, you save my life today. ThanksBarrator
B
9

You can use bool expressions as binding variables for such situations

query getUsers ($condition: user_bool_exp!) {
  user (where: $condition) {
    id
    name
    status
    user_sets {
      set {
        name
      }
    }
    # more fields...
  }
}

And you can build conditions depending on your variables

{ condition: { user_sets: { set_id: { _in: [$setId] } } } }

or

{ condition: { user_sets: {} }
Basement answered 17/3, 2021 at 9:8 Comment(0)
M
6

This answer only applies to Hasura v1.X, see other answers for more recent versions.


It seems like matching all if the variable is null is the default behaviour if the _eq boolExp is used. I wasn't seeing this because this query was using _in.

Changing to this gives all items if $setId is passed as null:

query getUsers ($setId: Int) {
  user(where: { user_sets: { set_id: { _eq: $setId } } }) {
    id
    name
    status
    user_sets {
      set {
        name
      }
    }
    # more fields...
  }
}

This is because Hasura follows SQL by having null not comparable to anything (only _is_null can match values that are set as null in nullable columns).

Therefore, { _eq: null } logically can't match anything, so it's simply optimised away. This:

(where: { user_sets: { set_id: { _eq: $setId } } })

...becomes this:

(where: { user_sets: { set_id: {} } }

...and because {} is trueExp, treated as true, it optimises away to be effectively WHERE 'true'.

Marmion answered 23/4, 2019 at 11:10 Comment(5)
do you have a solution for other options such as gte, lte and empty arrays for in?Polynomial
This changed in v2.0. More in the release notes, behaviour changes section: github.com/hasura/graphql-engine/releases/tag/v2.0.0-alpha.1Mooneyham
@Mooneyham If you can write an up to date v2.0 answer, that'd be greatMarmion
@Mooneyham I was thinking more that you post it as your own answer, and I'll accept it (I'd also probably edit a comment into mine saying mine is for v1.x and linking to yours as the v2.x answer). That way you get the credit for it and you'd be better able to keep it up to date or respond to comments (I'm not working with Hasura any more so I couldn't do that). Also SO usually doesn't like big changes in 3rd party edits like that.Marmion
Okay, I will do that.Mooneyham

© 2022 - 2024 — McMap. All rights reserved.