Rails - How to not evaluate a field in a Ransack search form

5.3k Views Asked by At

I am using the Ransack Gem in Rails 3.1. Everything works as expected. However, I have a search form that will look up a person using a Soundex column that I am having an issue with.

In my form I have a "given_name_soundex_cont" field, that a user will enter the name "Joe". My controller than converts "joe" to a soundex code, and then Ransack looks for a match in the "given_name_soundex" column.

All matching records are returned and seems fine, except that the field where the user entered the word "joe" now shows the soundex value instead (of course because I changed the param).

So I figured I could just add a "given_name_cont" field and hide the "given_name_soundex_cont" field, but now Ransack wants to include "given_name_cont" in the query, which I don't want.

Does anyone know how I can include fields in a Ransack search form without Ransack actually evaluating them. That way I can specify which fields are ot be evaluated and which are not.

If it helps this is what I have in my controller:

def index

  if !params[:q].blank?  # nil.blank? and [].blank? are true
    search_params = params[:q]
    search_params[:given_name_soundex_cont] = convert_to_soundex(search_params[:given_name_soundex_cont])
    @q = Person.search(search_params)
    @results = @q.result.limit(50)
  else
    @q = Person.search(params[:q])
    @results = []
  end

end


private 
    def convert_to_soundex(text)
      Text::Soundex.soundex(text.to_s)
    end

And in my view:

<%= search_form_for @q do |f| %>
  <label>Given Name:</label>
  <%= f.text_field :given_name_soundex_cont %>

  <%= f.submit %>
<% end %>
3

There are 3 best solutions below

0
On

I wanted to achieve something similar, that is, replace the whitespaces in customer input by the SQL wildcard %, so that the search field "Foo Bar" would match "Foo Bar", but also "Foomster Q. Bar" and "Foo Glolubar". I also wanted to add the wildcard to the beginning and end of the actual search parameter, so that "Fiefoo Newton Barrister" would also be a match.

Here's how. The relevant parts of app/controllers/customers_controller.rb:

class CustomersController < ApplicationController
  # GET /customers
  # GET /customers.json
  def index
    @search = Customer.search(params[:q])
    @customers = @search.result
    ...
    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @customers }
    end
  end
  ...
end

The relevant parts of the view app/views/customers/index.html.erb (the span4, span8 and btn btn-large btn-primary markup is provided by Twitter's Bootstrap framework):

<% require 'action_view' %>
<div class="row">
...
  <div class="span4"> <!-- The search criteria -->
    ...
    <%= search_form_for @search do |f| %> <!-- this is a Ransack-provided form -->
      <div class="field">
        <%= f.label :customer_name_whitespaces_match_anything, "Name is or contains:" %>
        <%= f.text_field :customer_name_whitespaces_match_anything, class: "my-textbox" %>
        ...
        <%= f.label :customer_address_whitespaces_match_anything, "Address is or contains:" %>
        <%= f.text_field :customer_address_whitespaces_match_anything, class: "my-textbox" %>
        ...
      </div>
      <br/>
      <div class="actions">
        <%= f.submit "Search", class: "btn btn-large btn-primary" %>
      </div>
    <% end %>
  </div>
  <div class="span8"> <!-- The search results -->
    ...
    <table border="1" cellpadding="5">
      <tr>
        <th><%= sort_link(@search, :customer_name, "Customer name") %></th>
        ...
        <th><%= sort_link(@search, :customer_address, "Address") %></th>
        ...
      </tr>
      <% @customers.each do |c| %>
        <tr>
          <td><%= link_to c.customer_name, customer_path(c, search: @search) %></td>
          ...
          <td><%= c.customer_address %></td>
          ...
        </tr>
      <% end %>
    </table>
  </div>
...
</div>

You'll notice the search predicates customer_name_whitespaces_match_anything and customer_address_whitespaces_match_anything. These refer to a custom search predicate that I defined in file config/initializers/ransack.rb. Its contents are:

Ransack.configure do |config|
  config.add_predicate 'whitespaces_match_anything',
  :arel_predicate => 'matches', # so we can use the SQL wildcard "%"
  # Format the incoming value: replace spaces by the SQL wildcard "%".
  :formatter => proc {|v| "%"+v.gsub(" ","%")+"%"}
end

Now the search term is munged by the custom predicate before it's given to SQL query, but after the search, the search form's input fields still show the search term the user input originally.

(In the search results, I have a link from the customer's name to the individual customer, which will cause the view app/views/customers/show.html.erb to be loaded.)

0
On

You could re-use params[:q][:given_name_soundex] in your view to override the value passed to #ransack/#search.

0
On

Create a ransacker that formats the parameter. I've not tried this, but I think it will work (via https://github.com/ernie/ransack/issues/36).

ransacker :given_name_soundex, :formatter => proc {|v| Text::Soundex.soundex(v)} do
  parent.table[:given_name_soundex]
end