Hi,
I just started looking at JSONAPI::Resources and I have some questions regarding filtering:
1) Is there any way to filter resource by using "like" searches? For example find all posts whose title contains a word "bird". It appears that there is no standard way to do it, at least I didn't find any.
2) If I want to use some complicated default filter in an "index" action for example, I was trying to use serializers - but I have no way how to easily serialize array of my records, what I mean is this:
render json:
JSONAPI::ResourceSerializer.new(Api::V1::PostResource).serialize_to_hash(Api::V1::PostResource.new(Post.all))
This of course doesn't work - how can I easily convert a Relation or an array of records into an array of Resources? It looks like I have to iterate over each record and concert it mannualy.
Thank you for the answer.
1) You can override the apply_filter method on the resource. This allows you to use custom where clauses.
2) This shouldn't be needed (see 1 above). But if you want to do this you will need to create an Array of Resources separately. So loop through Post.all and instantiate and collect one resource per post. Pass that to the Serializer. This should work:
Post.collect do |post|
PostResource.new(post, context)
end
or
Post.collect do { |post| PostResource.new(post, context) }
Answering 1)
I think your question is less about the gem itself and more about the JSON API spec which the gem implements. I would posit that the Filter section of the JSON API Spec has the answer: that it's "agnostic about the [filtering] strategies supported by a server". So, you're likely correct: the spec doesn't require a standard methodology.
In developing KnowPlace, we (@allthesignals and I) decided to implement a simple search via the filter[search] parameter. The link highlights the apply_filter override mentioned by @lgebhardt.
Thank you for the quick response, I have missed the apply_filter funcionality, yes that is sufficient for all my needs. Thank you.
@PavelJ If that fixed it for you, would you mind closing the issue? Thanks! :)
If someone else wants to filter resources by using "like" searches, this is what I did:
class ProductResource < JSONAPI::Resource
attribute :name
filter :name
def self.apply_filter(records, filter, value, options = {})
strategy = _allowed_filters.fetch(filter.to_sym, Hash.new)[:apply]
if strategy
if strategy.is_a?(Symbol) || strategy.is_a?(String)
send(strategy, records, value, options)
else
strategy.call(records, value, options)
end
else
value_regex = value.join('|')
records.where("#{filter} REGEXP '#{value_regex}'")
end
end
end
I think it would be nice to use filters with predicates (like ransack does)
something like the following example
class BaseResource < JSONAPI::Resource
abstract
def self.ransack_filter(name, opts = {})
opts[:apply] = ->(records, value, _options) do
records.ransack({name => value}).result
# or without ransack:
# records.public_send(name, value)
end
filter name, opts
end
def ransack_filters(*names)
options = names.extract_options!
names.each { |name| ransack_filter(name, options) }
end
class ProductResource < BaseResource
attribute :name
ransack_filters :name_equals, :name_contains
end
class Product < ActiveRecord::Base
# without ransack:
# scope :name_equals, ->(value) { where(name: value) }
# scope :name_contains, ->(value) { where('name LIKE ?', "%#{value}%") }
end
Most helpful comment
I think it would be nice to use filters with predicates (like ransack does)
something like the following example