问题
Let's say I have a model Book with a field word_count, amongst potentially many other similar fields.
What is a good way for me to string together conditions in an "advanced search" of the database? In the above example, I'd have a search form with boxes for "word count between ___ and ___". If a user fills in the first box, then I want to return all books with word count greater than that value; likewise, if the user fills in the second box, then I want to return all books with word count less than that value. If both values are filled in, then I want to return word counts within that range.
Obviously if I do
Book.where(:word_count => <first value>..<second value>)
then this will break if only one of the fields was filled in. Is there any way to handle this problem elegantly? Keep in mind that there may be many similar search conditions, so I don't want to build separate queries for every possible combination.
Sorry if this question has been asked before, but searching the site hasn't yielded any useful results yet.
回答1:
How about something like:
@books = Book
@books = @books.where("word_count >= ?", values[0]) if values[0].present?
@books = @books.where("word_count <= ?", values[1]) if values[1].present?
ActiveRecord will chain the where clauses
The only problem is that if values[0] && values[1] the query would not return anything if values[0] was greater than values[1].
回答2:
For our advanced searching we create a filter object which encapsulates the activerecord queries into simple methods. It was originally based on this Thoughtbot post
A book filter could look something like this:
class BookFilter
def initialize
@relation = Book.scoped
end
def restrict(r)
minimum_word_count!(r[:first]) if r[:first].present?
maximum_word_count!(r[:second]) if r[:second].present?
recent! if r.try(:[], :recent) == '1'
@relation
end
protected
def recent!
where('created_at > ? ', 1.week.ago)
end
def minimum_word_count!(count)
where('word_count >= ? ', count)
end
def maximum_word_count!(count)
where('word_count <= ?', count)
end
def where(*a)
@relation = @relation.where(*a)
end
end
#to use
books = BookFilter.new.restrict(params)
回答3:
Take a look at the ransack gem, which is the successor to the meta_search gem, which still seems to have the better documentation.
If you do want to roll your own, there's nothing preventing you from chaining clauses using the same attribute:
scope = Book
scope = scope.where("word_count >= ?", params[:first]) if params[:first]
scope = scope.where("word_count <= ?", params[:last]) if params[:last]
But it's really not necessary to roll your own search, there are plenty of ready solutions available as in the gems above.
来源:https://stackoverflow.com/questions/9308820/rails-searching-with-multiple-conditions-if-values-are-not-empty