This might not be an exact answer to your question, but because I googled around for the correct way to do OR searches in Ransack myself, and didn't find a good answer, but did manage to solve the problem myself, I thought I'd share the solution.
In an application I'm working on, there's a search page that takes as parameters the various fields of model Customer
(linked to DB table customers
), and then lists a table of those customers match the search result. The customers have three different phone number fields, and the search page previously had a different search field for each type of numbers. I wanted to combine them into one field that would conveniently search within all the numbers.
The database had these three field definitions (in these snippets, I've only changed some identifier names):
mysql> desc customers;
+--------------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------------+------------------+------+-----+---------+----------------+
...
| customer_phone_a | varchar(50) | YES | | NULL | |
| customer_phone_b | varchar(50) | YES | | NULL | |
| customer_phone_c | varchar(50) | YES | | NULL | |
+--------------------------+------------------+------+-----+---------+----------------+
Previously, the search page (file app/views/customers/index.html.erb
) contained this:
<%= search_form_for @search do |f| %> <!-- this is a Ransack-provided form -->
<div class="field">
...
<%= f.label :customer_phone_a_spaces_match_anything, "Phone number A is or contains:" %>
<%= f.text_field :customer_phone_a_spaces_match_anything %>
<%= f.label :customer_phone_b_spaces_match_anything, "Phone number B is or contains:" %>
<%= f.text_field :customer_phone_b_spaces_match_anything %>
<%= f.label :customer_phone_c_spaces_match_anything, "Phone number C is or contains:" %>
<%= f.text_field :customer_phone_c_spaces_match_anything %>
...
</div>
<div class="actions">
<%= f.submit "Search", class: "btn btn-large btn-primary" %>
</div>
<% end %> <!-- search_form_for -->
(This is beside the present point, but the contents of file config/initializers/ransack.rb
are:
Ransack.configure do |config|
config.add_predicate 'spaces_match_anything',
:arel_predicate => 'matches', # so we can use the SQL wildcard "%"
# Format the incoming value: add the SQL wildcard character "%" to the beginning and the end of
# the string, replace spaces by "%", and replace multiple occurrences of "%" by a single "%".
:formatter => proc {|v| ("%"+v.gsub(" ", "%")+"%").squeeze("%")}
end
This means that in addition to Ransack's default eq
, cont
, etc., I can use my custom predicate spaces_match_anything
in searches. The predicate does what it says.)
Anyway, taking inspiration from the same example you did, I added the following ransacker to model app/models/customer.rb
:
ransacker :all_phones do |parent|
Arel::Nodes::InfixOperation.new('||',
Arel::Nodes::InfixOperation.new('||',
Arel::Nodes::InfixOperation.new('||', parent.table[:customer_phone_a]||"", ' '),
parent.table[:customer_phone_b]||"", ' '),
parent.table[:customer_phone_c]||"")
end
Finally, I replaced the three phone number search fields in the search page by this:
<%= f.label :customer_phone_a_or_customer_phone_b_or_customer_phone_c_cont, "Phone number is or contains:" %>
<%= f.text_field :customer_phone_a_or_customer_phone_b_or_customer_phone_c_cont %>
A number that a user inputs in this search field will now match any of the three numbers of a customer. (Notice the guard against null number, ||""
, in the ransacker.)
This is tested to work with Ruby v1.9.3 and Rails v3.2.8. The input parameter will not match the end of one number and the beginning of the next one, not even if a space is input in the right place, and not even if in the search field code, I replace _cont
by _spaces_match_anything
.