Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] Advanced filtering - using "like" etc. - is it possible? #460

Closed
PavelJ opened this issue Sep 25, 2015 · 6 comments
Closed

[Question] Advanced filtering - using "like" etc. - is it possible? #460

PavelJ opened this issue Sep 25, 2015 · 6 comments

Comments

@PavelJ
Copy link

PavelJ commented Sep 25, 2015

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.

@lgebhardt
Copy link
Member

  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) }

@beechnut
Copy link
Contributor

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.

@PavelJ
Copy link
Author

PavelJ commented Sep 25, 2015

Thank you for the quick response, I have missed the apply_filter funcionality, yes that is sufficient for all my needs. Thank you.

@beechnut
Copy link
Contributor

@PavelJ If that fixed it for you, would you mind closing the issue? Thanks! :)

@meydjer
Copy link

meydjer commented Oct 31, 2016

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

@senid231
Copy link
Contributor

senid231 commented Nov 1, 2016

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants