I am attempting to do a search in Rails 3 like so. I have a database object like so with an attribute like so:
@food.fruit = "Apples, Pears, Plums"
and I have a search that u开发者_如何学JAVAses params from a checkbox, like so:
params[:search] = ["Apples", "Oranges", "Bananas", "Grapefruit"]
Since my @food
object above has "Apples" and the search array has "Apples" I'd like for this search to be successful. However I am having trouble having this work properly.
My initial thought was to do something like
@results = Food.where("fruit LIKE ?", "%params[:search]%")
or
@results = Food.where("fruit IN ?", params[:search])
But the first only works if the params[:search]
contains ONLY the @food.fruit
elements and no others. The second doesn't work at all.
My last ditch resort is to do something like
@results = Array.new
params[:search].each do |search|
@results << Food.where("fruit LIKE ?", search)
end
but I'd rather not do that if I don't have to. Anyone have any advice?
What you're looking for is some SQL like this:
WHERE LOWER(fruit) LIKE '%apples%'
OR LOWER(fruit) LIKE '%oranges%'
OR LOWER(fruit) LIKE '%bananas%'
OR LOWER(fruit) LIKE '%grapefruit%'
Note that LIKE
is not necessarily case insensitive so pushing everything to lower case (or upper case) is generally a good idea.
That's simple enough but saying x.where(...).where(...)...
connects the conditions with AND
when you want OR
. One way is to build the first argument to where
as a string by pasting together the right number of "LOWER(fruit) LIKE ?"
strings to match the number of elements in params[:search]
:
@results = Food.where(
(["LOWER(fruit) LIKE ?"] * params[:search].length).join(' OR '),
*(params[:search].map { |s| '%' + s.downcase + '%' })
)
Essentially you're doing 5 separate searches. While making 5 separate SQL queries might not be the answer, you could always join the array, like:
scope :search_fruit, lambda {|*fruits|
where fruits.flatten.map {|fruit| arel_table[:fruit].matches("%#{fruit}%") }.inject(&:or)
}
and
Food.search_fruit(params[:fruit])
精彩评论