Search/ filter method for all attributes in my index table - ruby-on-rails

I'm trying to write a row for my index table that filters my objects regarding a specific value of a specific column. What I have until now is this:
pimps_controller.rb:
def index
#pimps = Pimp.search(params[:search])
end
pimp.rb:
def self.search( search)
if search
where('title LIKE ?', "%#{search}%")
else
scoped
end
end
A part of view:
<%= text_field_tag :search, params[:search] %>
That filters after the objects title only so I tried to alter it to make it functional for different search fields that can filter after different attributes. I want to pass a second parameter value if someone fires the search function to make sure it triggers for the right attributes. That's what I've tried:
pimps_controller.rb
#pimps = Pimp.search(params[:search_column],params[:search])
pimp.rb:
def self.search(search_column, search)
if search
col = "%#{search_column}"
s = "%#{search}%"
where(col 'LIKE ?', s)
else
scoped
end
end
The view:
<%= text_field_tag :search, params[:search], params[:search_column => title] %>
But it's not working. I get an error message for passing the both parameters in one search field I guess. How would you do it?

Here's a simple tutorial on how to do it:
https://we.riseup.net/rails/simple-search-tutorial
In the model, you will have to add the fields with or condition to the query.
def self.search(search)
search_condition = "%" + search + "%"
find(:all, :conditions => ['title LIKE ? OR description LIKE ?', search_condition, search_condition])
end
If you want to define the field to search in the params you can use string interpolation with simple quotes:
%q(text contains "#{search.query}")

You need 2 text fields, one for the column, one for the value:
# view
<%= text_field_tag :search_value, params[:search_value] %>
<%= text_field_tag :search_column, params[:search_column] %>
# controller
#pimps = Pimp.search(params[:search_column], params[:search_value])
# Pimp model
def self.search(search_column, search_value)
if search_value.present? && search_column.present?
column = self.column_names.include?(search_column.to_s) ? search_column : 'title'
value = "%#{search_value}%"
where("#{self.table_name}.#{column} LIKE ?", value)
else
scoped
end
end
The problem about this method is that if you don't type the exact name of the column, it will search the value in the column title. I think you should use a select_tag, listing all searchable columns of the model:
# view
<%= select_tag :search_column, options_for_select(Pimp.column_names.map { |col| [col, col] }, params[:search_column]) %>
This view code will display a select tag with the available columns of the Pimp model. You can easily limit the searchable columns by defining a class method on Pimp:
# Pimp model
def searchable_columns
self.column_names - ['id', 'created_at', 'updated_at']
end
# view
<%= select_tag :search_column, options_for_select(Pimp.searchable_columns.map { |col| [col, col] }, params[:search_column]) %>

Related

Multi find search in ruby with date

I have created a multi find search, where I need to filter records by date / category / title. Searching by a category and/or title works, however, when date is typed it doesn't change anything (the results is the same like there was no date typed). I have no idea what else I could do to fix it, I am just a beginner in Ruby. Any idea?
Model:
def self.multi_find(cat_id, search, date_search)
search_condition = "%" + search + "%"
#date_condition = date_search
# test if cat_id is not blank
if not cat_id.blank?
# assign the value of cat_id to a ‘scope’ named :cat
scope :cat, -> { where('category_id = ?', cat_id) }
# using the ‘scope’ cat find where a search string is like a title or an author’s name
self.cat.where("title LIKE ? or event_date = ?", search_condition, date_search.to_date)
else
# find where a search string is like a title or an author’s name
self.where("title LIKE ? or event_date = ?", search_condition, date_search.to_date)
end
end
Controller:
def multi_find
# call an Event class method, using two parameters; a category unique identifier and a search string (author or title)
events = Event.multi_find(params[:cat_id], params[:search_string], params[:event_date_search])
# use Kaminari pagination ...
#events = Kaminari.paginate_array(events.order :title).page(params[:page]).per(6)
# if no products have been found
if #events.empty?
# display a notice
flash.now[:alert] = "No events found - so displaying all events"
# then display all products
#events = Event.order(:title).page(params[:page]).per(6)
end
# use the index view
render :action => "index"
end
The console outputs the SQL Query
Event Load (0.0ms) SELECT "events".* FROM "events" WHERE (category_id = '1') AND (title LIKE '%%' or event_date = '2018-02-14') ORDER BY "events"."title" ASC
View file:
<%= form_tag my_path, :method=>'post', :multipart => true do %>
<%= select_tag ('cat_id'),
options_from_collection_for_select(#categories, :id, :cat_name, 0 ),
:prompt => "Select a Category" %>
<div class="datepicker">
<% #event_date_format %>
<%= text_field_tag :event_date_search %>
</div>
<!-- Key word:-->
<%= text_field_tag :search_string %>
<%= submit_tag 'Search' %>
<% end %>
It's because you have an or in your sql statement. However you should also clean up your code a bit.
def self.multi_find(cat_id, search, date_search)
result = self.all
result = result.where(category_id: cat_id) if cat.id.present?
result = result.where('title LIKE ?', "%#{search}%") if search.present?
result = result.where(event_date: date_search) if date_search.present?
result
end

How to query the activerecord based on the enum status?

I am trying implement a search/filter action on a model Production based on a column status. The column status is of integer type. Later for the purpose of readability I used enum datatype on status column as follows.
class Production < ApplicationRecord
enum status:{
Preproduction:1,
Postproduction: 2,
Completed:3
}
end
Then I started to work on a search/filter functionality to fetch the record based on the status given by the user.
productions_controller
def filter
if params[:filter]
#productions = Production.where('productions.status like ?', "%#{params[:filter]}%")
else
#productions = Production.all
end
end
view
<%= form_tag [:filter, :productions], :method => 'get' do %>
<p>
<%= text_field_tag :filter, params[:filter] %>
<%= submit_tag "Filter", :status => nil %>
</p>
<% end %>
Now I am able to query the record properly only if I enter the integer values like 1 2 or 3 in the text field. When I enter the status like Preproduction like I assigned, I am not getting the result. I am getting a blank page. How can I fix this ? How can I make it to accept the string and query successfully ?
You can do this...
#productions = Production.where('productions.status like ?', "%#{Production.statuses[params[:filter]]}%")
Enums have a pluralized class method, so enum status in Production has a hash
Production.statuses which looks like your status hash but with the symbols changed into strings.

ActiveRecords search with two params

I have a table named measurements and a model named measure.
I have a method one_day_measurements_index in the controller.
Here is the code:
def one_day_measurements_index
#record = Measurement.last
if #record.blank?
#scheduled_on = Date.today
else
#scheduled_on = params[:date] || #record.scheduled_on
end
if #record.blank?
flash[:danger] = "No Measurements Calculated."
redirect_to measurements_path
else
if #scheduled_on.blank?
#scheduled_on = params[:date] || Date.today
end
#page = params[:page] || 1
#per_page = params[:per_page] || WillPaginate.per_page
if params[:search]
#measurements = Measurement
.where("scheduled_on = '#{#scheduled_on}'")
.search(params[:search])
.paginate(:per_page => #per_page, :page => params[:page])
.order("get_inst_status(instrument_id), instrument_id")
else
#measurements = Measurement
.where("scheduled_on = '#{#scheduled_on}'")
.paginate(:per_page => #per_page, :page => params[:page])
.order("get_inst_status(instrument_id), instrument_id")
end
end
measurements_hash #measurements
end
I can use search entering a string, the search method in the model is:
private
def self.search(search)
where('
upper(reading_type) like upper(:search)
or upper(daily_sequence) like upper(:search)
or upper(reading_frequency) like upper(:search)
or upper(reading_period) like upper(:search)
or upper(reading_period) like upper(:search)
or upper(measured_by) like upper(:search)
or upper(measured_with) like upper(:search)
or upper(crew) like upper(:search)
or get_inst_id(instrument_id) like upper(:search)
or get_inst_group_id(instrument_id) like upper(:search)
or upper(get_inst_status(instrument_id)) like upper(:search)
', search: "%#{search}%")
end
In the one_day_measurements_index.html.erb I am using this search tag:
<%= form_tag one_day_measurements_index_path, :method => 'get', class: "search" do %>
<%= text_field_tag(:search, params[:search], options = {:placeholder => "Search", :class => "search_field"})%>
<% end %>
And the path in the routes.rb is:
match 'one_day_measurements_index', to: 'measurements#one_day_measurements_index', via: 'get'
Here is the problem.
The page rendered show on top of the rows the tag for search lets say by a string.
Below the listed rows I have a tag to select with date picker the date since I want to see only the records for that date.
Here it is:
<%= form_tag one_day_measurements_index_path, :method => 'get' do %>
<%= text_field_tag :date, params[:date], options = {:placeholder => "Date (yyyy-mm-dd)", :class => 'datetimepicker'} %>
<%= submit_tag "Show Measurements for Date", class: "btn btn-xs btn-primary"%>
<% end %>
So in a nutshell I have to search tags for a string lets say an instrument type and the date for which I want to see those records. Practically I want to see all the records for instruments of type MPBX on the date of '2015-02-25'. The entire table has records for two more dates after that, '2015-02-26' and '2015-02-27'.
When I am entering the date '2015-02-25' I can see only the records from that date, which is correct. But when I am entering after that in the other search tag the string MPBX, the rendered page shown is for the date of '2015-02-27', the last date recorded in the table. Also no rows are shown since MPBX existed only on the date of '2015-02-25'.
Don't know how to solve this. Any clues?
Okay,
based on your comments, I've first of all refactored your controller code into a bit more readable structure. I hope I've not broken your logic by doing this:
def one_day_measurements_index
unless Measurements.last
#scheduled_on = Date.Today
flash[:danger] = “No Measurements Calculated.”
render(action: "one_day_measurements_index") and return
end
#scheduled_on = params[:date] || #record.scheduled_on
#page = params[:page] || 1
#per_page = params[:per_page] || WillPaginate.per_page
# Now we actually search on date first.
#measurements = Measurement.where("scheduled_on = '#{#scheduled_on}'")
# if the search string is provided, we perform a narrower search on
# the records of the matching date.
if params[:search]
#measurements.where('
upper(reading_type) like upper(:search)
or upper(daily_sequence) like upper(:search)
or upper(reading_frequency) like upper(:search)
or upper(reading_period) like upper(:search)
or upper(reading_period) like upper(:search)
or upper(measured_by) like upper(:search)
or upper(measured_with) like upper(:search)
or upper(crew) like upper(:search)
or get_inst_id(instrument_id) like upper(:search)
or get_inst_group_id(instrument_id) like upper(:search)
or upper(get_inst_status(instrument_id)) like upper(:search)
', search: “%#{params[:search]}%”)
end
# We finish by paginating and ordering our result.
#measurements.paginate(:per_page => #per_page, :page => params[:page])
#measurements.order("get_inst_status(instrument_id), instrument_id")
end
If I made no mistakes, the code should do the following:
Takes the last Measurement in your table.
If not found, then we set the #scheduled_on and show the page.
If the date is provided, we set that as criteria + pagination values
then we actually search using our criteria
I think the problem lies in that you performed a redirect_to which does not store the variables you have defined, basically resetting your query every time.
Let me know how this turns out, and I can make edits based on your feedback.
EDIT
I'm not 100% sure if this approach will, since I do not know enough about your application. It could potentially be that this results in a not working page based on the information in your DB, but unfortunately I do not have enough info to catch all cases.

Ransack search not working if there is 'space' in search term

I am using ransack for search in my rails 3.2 application using postgres as database.
I have a Invoice model and every invoice belongs_to a buyer. Below is my search form in index page.
views/invoices/index.html.erb
<%= search_form_for #search do |f| %>
<%= f.text_field :buyer_name_cont %>
<%= f.submit "Search"%>
<% end %>
And here is my controller code.
controllers/invoices_controller.rb
def index
#search = Invoice.search(params[:q])
#invoices=#search.result(:distinct => true).paginate(:page => params[:page], :per_page => GlobalConstants::PER_PAGE )
respond_to do |format|
format.html # index.html.erb
format.json { render json: #invoices }
end
end
Let's say a invoice is there of a buyer having name "Bat Man".
If I search "Bat", I get the invoice in results.
Again if I search "Man", I get the invoice in results.
But if I search "Bat Man", I don't get the invoice in results.
I know it might be something trivial but I am not able to resolve.
Update
When I tried the sql query formed directly in database using pgAdmin, I realized that in database there were multiple spaces in the buyer name, something like "Bat.space.space.space.Man".
Can something be done so that "Bat.space.Man" search also finds "Bat.space.space.space.Man" in results?
You could sanitize your data. For instance with regexp_replace(). Run in the database once:
UPDATE invoice
SET buyer = regexp_replace(buyer, '\s\s+', ' ', 'g')
WHERE buyer <> regexp_replace(buyer, '\s\s+', ' ', 'g');
And sanitize new inserts & updates likewise.
\s .. class shorthand for "white space" (including tab or weird spaces).
The 4th parameter 'g' is for "globally", needed to replace all instances, not just the first.
Ransack not support cont search for multi terms, I solved the requirement my customized way. the details as following:
Add scope to your model:
scope :like_search, ->(column, value) {
keywords = value.to_s.split.map{ |k| "%#{k}%" }
where(Array.new(keywords.size, "#{column} ILIKE ?").join(' AND '), *keywords)
}
in your view. instead of using f.text_field :buyer_name_cont provided by ransack, use normal field helper text_field_tag :buyer_name, params[:buyer_name]
then restrict your ransack in scope:
scope = Invoice.like_search(:name , params[:buyer_name])
#q = scope.ransack(params[:q])

Searching multiple fields with Ransack

I'm using Ransack to allow advanced searching on my users. Currently, the users have first_name, middle_name, and last_name columns. Here is my code:
.field
= f.attribute_fields do |a|
= a.attribute_select
...
How can I have a custom one called 'Name', that lets the user search through all three of the columns mentioned above?
Note
I would still like to keep the remaining attributes as options as well, such as email, phone number, etc. Please keep this in mind when determining an answer.
I would suggest to provide a dedicated column for this search. While that might create redundant data, it is way easier to search than doing some SQL magic on the existing columns.
You can easily automate the setting of this field:
before_save :set_full_name
def set_full_name
self.full_name = [first_name, middle_name, last_name].reject(&:blank?).join(" ")
end
Then you can use the normal ransack methods to search this field.
Use this to search multiple fields:
= f.text_field(: first_name_or_middle_name_or_last_name_cont)
This will generate a query like this:
where first_name like '%q%' or middle_name like '%q%' or last_name like'%q%'
when you fill in a q as search parameter
Another approach is to search every attribute of the model, excluding only the fields that you don't want to search. To do this, you could create a custom helper method that builds the label and search field name expected by Ransack's search method. The following method (located in a helper) returns a concatenation of all attributes that you wish to search in a way that Ransack expects:
def most_attributes_cont
most_attributes = []
attributes_to_exclude = [
"id",
"created_at",
"updated_at"
]
ModelName.column_names.each do |column_name|
most_attributes << column_name unless column_name.in?(attributes_to_exclude)
end
most_attributes.join("_or_") + "_cont"
end
Then, just add the method call to your search form:
<%= search_form_for #q do |f| %>
<%= f.label most_attributes_cont %>
<%= f.search_field most_attributes_cont %>
<%= f.submit %>
<% end %>

Resources