Graphql-ruby: Query with clause on relationship/nested type

Created on 25 Nov 2020  路  1Comment  路  Source: rmosolgo/graphql-ruby

Describe the bug

A clear and concise description of what the bug is.

I am attempting to do a query on a type that contains another type as a field, it is a 1 - many relationship (Schools -> Students).

On the many side I am attempting to to only get records that have a truthy field for sick and I am hoping to get all schools no matter if they have students or not.

Versions

graphiql-rails (1.7.0)
graphql (1.11.1)
graphql_devise (0.13.4)
'rails', '~> 6.0.3', '>= 6.0.3.2'

GraphQL schema

Include relevant types and fields (in Ruby is best, in GraphQL IDL is ok).
Are you using interpreter? Any custom instrumentation, etc?

module Types
  class StudentType < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: true
    field :sick, Boolean, null: true
  end
end

module Types
  class SchoolType < Types::BaseObject
    field :id, ID, null: false
    field :rating, Integer, null: true
    field :name, String, null: false
    field :students, [StudentType], null: true
  end
end

GraphQL query

I have tried two approaches:

query SchoolsSickStudents($sick: Boolean = true) {
  schools {
    id
    rating
    name
    students @include(if: $sick) {
      name
      sick
    }
  }
}

Result doesn't error but the logic operation doesn't work:

{
  "data": {
    "schools": [
      {
        "id": "1",
        "rating": 2,
        "name": "School A",
        "students": [
          {
            "name": "Joe",
            "sick": true
          },
          {
            "name": "Sally",
            "sick": false
          }
        ]
      },
    ]
  }

These approach looks like it might be more accurate:

query SchoolsSickStudents {
  schools {
    id
    rating
    name
    students ( where: { sick: {_eq: true} 
    }) {
      name
      sick
            }
    }
  }

Results:

Error: unknown argument "where" on field students of type schools

Steps to reproduce

Run the query

Expected behavior
I am attempting to do a query on a type that contains another type as a field, it is a 1 - many relationship (Schools -> Students)

Actual behavior
Getting all results back or getting an error
What specifically went wrong?

Place full backtrace here (if a Ruby exception is involved):


Click to view exception backtrace

{
  "errors": [
    {
      "message": "Field 'students' doesn't accept argument 'where'",
      "sctudents": [
        {
          "line": 243,
          "column": 17
        }
      ],
      "path": [
        "query SchoolsSickStudents",
        "schools",
        "students",
        "where"
      ],
      "extensions": {
        "code": "argumentNotAccepted",
        "name": "students",
        "typeName": "Field",
        "argumentName": "where"
      }
    }
  ]
}

Additional context

If we had an example of this in the documentation where we did this type of query that would be helpful.

I looked there and in other places.

Most helpful comment

Hi! If you want to filter data in your application, then you should:

  • Add arguments to the field you want to filter
  • Then, use those arguments to filter data

For example, you could add an argument like this:

  module Types
    class SchoolType < Types::BaseObject
      field :id, ID, null: false
      field :rating, Integer, null: true
      field :name, String, null: false
-     field :students, [StudentType], null: true
+     field :students, [StudentType], null: true do 
+       argument :is_sick, Boolean, required: false 
+     end 
    end
  end

Then, implement def students to perform filtering with the given is_sick value:

  module Types
    class SchoolType < Types::BaseObject
      field :id, ID, null: false
      field :rating, Integer, null: true
      field :name, String, null: false
      field :students, [StudentType], null: true do 
        argument :is_sick, Boolean, required: false 
      end 
+     
+     def students(is_sick: nil) 
+       students = object.students 
+       if !is_sick.nil? 
+         students = students.where(sick: is_sick) 
+       end 
+       students 
+     end 
    end
  end

Then, you can query the schema like this:

query SchoolsSickStudents {
  schools {
    id
    rating
    name
    students(isSick: true) {
      name
      sick
    }
  }
}

isSick: true will pass true along to is_sick in def students.

There's no built-in database filtering in GraphQL (because GraphQL is backend-agnostic). The example you found in Hasura is because Hasura is coupled to a database, so it can make assumptions about how to filter things. $include isn't a dynmaic filter; instead, it statically ignores parts of the query based on the the value of if: ....

I hope that helps!

>All comments

Hi! If you want to filter data in your application, then you should:

  • Add arguments to the field you want to filter
  • Then, use those arguments to filter data

For example, you could add an argument like this:

  module Types
    class SchoolType < Types::BaseObject
      field :id, ID, null: false
      field :rating, Integer, null: true
      field :name, String, null: false
-     field :students, [StudentType], null: true
+     field :students, [StudentType], null: true do 
+       argument :is_sick, Boolean, required: false 
+     end 
    end
  end

Then, implement def students to perform filtering with the given is_sick value:

  module Types
    class SchoolType < Types::BaseObject
      field :id, ID, null: false
      field :rating, Integer, null: true
      field :name, String, null: false
      field :students, [StudentType], null: true do 
        argument :is_sick, Boolean, required: false 
      end 
+     
+     def students(is_sick: nil) 
+       students = object.students 
+       if !is_sick.nil? 
+         students = students.where(sick: is_sick) 
+       end 
+       students 
+     end 
    end
  end

Then, you can query the schema like this:

query SchoolsSickStudents {
  schools {
    id
    rating
    name
    students(isSick: true) {
      name
      sick
    }
  }
}

isSick: true will pass true along to is_sick in def students.

There's no built-in database filtering in GraphQL (because GraphQL is backend-agnostic). The example you found in Hasura is because Hasura is coupled to a database, so it can make assumptions about how to filter things. $include isn't a dynmaic filter; instead, it statically ignores parts of the query based on the the value of if: ....

I hope that helps!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

skanev picture skanev  路  3Comments

theodorton picture theodorton  路  3Comments

pareeohnos picture pareeohnos  路  3Comments

gastonmorixe picture gastonmorixe  路  3Comments

rylanc picture rylanc  路  3Comments