Lucas Andrade
Lucas Andrade

Reputation: 4600

Rails how to use where method for search or return all?

I am trying to do a search with multiple attributes for Address at my Rails API.

I want to search by state, city and/or street. But user doesn't need to send all attributes, he can search only by city if he wants.

So I need something like this: if the condition exists search by condition or return all results of this condition.

Example:

search request: street = 'some street', city = '', state = ''

How can I use rails where method to return all if some condition is nil?

I was trying something like this, but I know that ||:all doesn't work, it's just to illustrate what I have in mind.:

def get_address
  address = Adress.where(
    state: params[:state] || :all,
    city: params[:city] || :all,
    street: params[:street] || :all)
end

It's possible to do something like that? Or maybe there is a better way to do it?

Upvotes: 1

Views: 1511

Answers (3)

max
max

Reputation: 102222

This is a more elegant solution using some simple hash manipulation:

def filter_addesses(scope = Adress.all)
  # slice takes only the keys we want
  # compact removes nil values
  filters = params.permit(:state, :city, :street).to_h.compact
  scope = scope.where(filters) if filters.any?
  scope
end

Upvotes: 2

danielricecodes
danielricecodes

Reputation: 3586

I highly recommend using the Searchlight gem. It solves precisely the problem you're describing. Instead of cluttering up your controllers, pass your search params to a Searchlight class. This will DRY up your code and keep your controllers skinny too. You'll not only solve your problem, but you'll have more maintainable code too. Win-win!

So in your case, you'd make an AddressSearch class:

class AddressSearch < Searchlight::Search

  # This is the starting point for any chaining we do, and it's what
  # will be returned if no search options are passed.
  # In this case, it's an ActiveRecord model.
  def base_query
    Address.all # or `.scoped` for ActiveRecord 3
  end

  # A search method.
  def search_state
    query.where(state: options[:state])
  end

  # Another search method.
  def search_city
    query.where(city: options[:city])
  end
  # Another search method.
  def search_street
    query.where(street: options[:street])
  end
end

Then in your controller you just need to search by passing in your search params into the class above:

AddressSearch.new(params).results

One nice thing about this gem is that any extraneous parameters will be scrubbed automatically by Searchlight. Only the State, City, and Street params will be used.

Upvotes: 1

matthewd
matthewd

Reputation: 4435

Once you're passing a column to where, there isn't an option that means "on second thought don't filter by this". Instead, you can construct the relation progressively:

def get_address
  addresses = Address.all
  addresses = addresses.where(state: params[:state]) if params[:state]
  addresses = addresses.where(city: params[:city]) if params[:city]
  addresses = addresses.where(street: params[:street]) if params[:street]
  addresses
end

Upvotes: 2

Related Questions