Is there an easy way to get ransack like filters?
I currently use something like this
class Types::RansackPredicateType < GraphQL::Schema::InputObject
argument :eq, String, "Equals", required: false
argument :cont, String, "Contains", required: false
argument :nil, Boolean, "Is Null", required: false
end
class Types::RansackUserType < GraphQL::Schema::InputObject
User.ransackable_attributes.each do |attr|
argument attr, Types::RansackPredicateType, required: false
end
argument :m, String, "and, or", required: false
argument :g, [Types::RansackUserType], required: false
end
md5-b03279ab841bf182554e09ed355e5fbf
which then allows queries like
md5-3733f12fc7ac7363184c8c48cf87daa2
```graphql
{user(q: {g: [{m: "or", email: {cont: "@example.com"}, name: {eq: "test"}}, {confirmed_at: {nil: false}}]}) {id}}
the types should probably be stricter e.g. :m could be an enum and RansackPredicateType could be split into several types so that integer_column: { cont: "a" } would be disallowed. i think it should also be possible to add other nested RansackXTypes for associated records.
(side questian: is there a way to get rid of the q: without essentially duplicating RansackUserType?)
or is there a completely different approach to filtering which dosn't require writing hundreds of filters?
That would be really cool, but I'm not sure this gem should support this. It looks like this would be ideal gem/extension that hooks into this gem. What do you think?
Btw, have you seen https://github.com/opencrud/opencrud? I know some folks on the GraphQL Slack's #ruby channel were talking about building something to support this syntax, maybe interesting for you?
I have not but it's indeed what i have been searching for.
You are probably right that it belongs in an extension so i'll close this issue.
I found myself looking for this as well, and came up with a solution that is pretty simple:
module GraphQL
module RansackSupport
def ransack(base, **args)
base.ransack(build_ransack_query(base, **args)).result
end
def build_ransack_query(base, **args)
atts = base.ransackable_attributes
sort = args.delete(:sort)
ransack_params = args.reduce({}) do |memo, (k,v)|
memo[atts.include?(k.to_s) ? :"#{k}_eq" : k] = v
memo
end
if sort
ransack_params[:s] = "#{sort[:column]} #{sort[:direction]}"
end
ransack_params
end
end
end
Include the module into your base type, then you can resolve fields like so:
field :posts, [Types::Post], null: false do
argument :id, ID, required: false
argument :title_cont, String, required: false
end
def posts(**args)
# you can also do ransack(self.object.posts, **args)
ransack(Post, **args)
end
I solved problem using this:
class Types::CompanyFilterType < Types::BaseInputObject
Company.ransackable_attributes.each do |attr|
Ransack.predicates.keys.map do |predicate|
argument "#{attr}_#{predicate}".to_sym, String, required: false, camelize: false
end
end
end
and
module Resolvers
class CompaniesResolver < BaseResolver
type [Types::CompanyType], null: false
argument :filter, Types::CompanyFilterType, required: false
def resolve(filter:)
Company.ransack(filter.to_hash).result
end
end
end
Basically, Types::CompanyFilterType create a bunch predicates as argument for all attributes
irb(main):001:0> Types::CompanyFilterType.arguments.keys
=> ["id_eq", "id_eq_any", "id_eq_all", "id_not_eq", "id_not_eq_any", "id_not_eq_all", "id_matches", "id_matches_any", "id_matches_all", "id_does_not_match", "id_does_not_match_any", "id_does_not_match_all", "id_lt", "id_lt_any", "id_lt_all", "id_lteq", "id_lteq_any", "id_lteq_all", "id_gt", "id_gt_any", "id_gt_all", "id_gteq", "id_gteq_any", "id_gteq_all", "id_in", "id_in_any", "id_in_all", "id_not_in", "id_not_in_any", "id_not_in_all", "id_cont", "id_cont_any", "id_cont_all", "id_not_cont", "id_not_cont_any", "id_not_cont_all", "id_i_cont", "id_i_cont_any", "id_i_cont_all", "id_not_i_cont", "id_not_i_cont_any", "id_not_i_cont_all", "id_start", "id_start_any", "id_start_all", "id_not_start", "id_not_start_any", "id_not_start_all", "id_end", "id_end_any", "id_end_all", "id_not_end", "id_not_end_any", "id_not_end_all", "id_true", "id_not_true", "id_false", "id_not_false", "id_present", "id_blank", "id_null", "id_not_null", "canonical_name_eq", "canonical_name_eq_any", "canonical_name_eq_all", "canonical_name_not_eq", "canonical_name_not_eq_any", "canonical_name_not_eq_all", "canonical_name_matches", ..."]
Most helpful comment
I found myself looking for this as well, and came up with a solution that is pretty simple:
Include the module into your base type, then you can resolve fields like so: