Using where search clause after select - ruby-on-rails

I'm new to rails and I'm adding some updates to already existing app. I need to add filtering by search word and I'm trying to use where with ILIKE to do that but due to the original code is written. the original code uses Model.select to get the records and when i try to chain where with the ILIKE clause to that it still returns the whole array not filtered.
date_format = get_js_date_format_by_id(#account.date_format_id)
sort_format = "#{params[:sort_item] || "date"} #{params[:sort_order] || "asc"}"
#expenses = Expense.select("expenses.*, to_char(expenses.date, '#{date_format}') as formatted_date,
to_char(
CASE
WHEN expense_categories.is_calculated = true then expenses.amount * expense_categories.unit_cost
ELSE expenses.amount
END, 'FM999999990.00') as total,
CASE
WHEN expense_categories.is_calculated = true then expense_categories.unit_name
ELSE null
END as unit_name,
'' as images_list,
users.name as user_name, expense_categories.name as category_name, projects.name as project_name, clients.name as client_name")
.joins(:user, :project, :expense_category, :client)
if params[:query]
#expenses.includes(:user).where('users.name ILIKE :query', { query: "%#{params[:query]}%"}).references(:users)
end
#expenses.where(conditions)
.order(sort_format)
if params[:page]
#expenses = #expenses.page(params[:page]).per(params[:per_page])
end
#expenses
I'm adding the filtering part starting at if params[:query] the rest is the original code. any help would be appreciated to pin pointing the problem.
thanks!

The problem is that you don't modify the current #expenses scope with:
if params[:query]
#expenses.includes(:user).where('users.name ILIKE :query', { query: "%#{params[:query]}%"}).references(:users)
end
This line returns the new scope you want, but you are not saving it anywhere. Following code should work:
if params[:query]
#expenses = #expenses.includes(:user).where('users.name ILIKE :query', { query: "%#{params[:query]}%"}).references(:users)
end
Note: same thing applies for the sorting under the params[:query] condition.

Related

Ignore uppercase, downcase and accent in search

I have a search system with filter here. This system work like a charm but I have some problem with downcase / uppercase and accent.
For example if I search "marée" I have result but if I search "MAREE" or "Marée" or "maree". I don't have result.
I want to fix that. How I can fix this ? Thank you.
my controller
def resultnohome
if params[:query].blank?
redirect_to action: :index and return
else
#campings = Camping.searchi(params[:query], params[:handicap], params[:animaux], params[:television], params[:plage], params[:etang], params[:lac])
if params[:query] == "aube"
#pub = Camping.find_by_id(1)
else
end
end
end
My model
def self.searchi(query, handicap, animaux, television, plage, etang, lac)
return scoped unless query.present?
result = left_outer_joins(:caracteristiquetests, :situations).where('nomdep LIKE ? OR name LIKE ? OR nomregion LIKE ? OR commune LIKE?', "%#{query}%", "%#{query}%", "%#{query}%", "%#{query}%")
result = result.where('handicap LIKE ?', "%#{handicap}%") if handicap
result = result.where('animaux LIKE ?', "%#{animaux}%") if animaux
result = result.where('television LIKE ?', "%#{television}%") if television
result = result.where('plage LIKE ?', "%#{plage}%") if plage
result = result.where('etang LIKE ?', "%#{etang}%") if etang
result = result.where('lac LIKE ?', "%#{lac}%") if lac
return result
end
If you insist on using SQLite then you don't have many good options. The most common suggestion is to have extra columns in your database, that are normalized values so if your plage column contains "Marée" then you also have a column plage_ascii that contains "maree"
you need to create the additional columns with migrations, then you would have a before_save action in your model...
before_save :create_normalized_strings
def create_normalized_strings
self.handicap_ascii = handicap.downcase.mb_chars.normalize(:kd).gsub(/[^x00-\x7F]/n, '').to_s
self.animaux_ascii = animaux.downcase.mb_chars.normalize(:kd).gsub(/[^x00-\x7F]/n, '').to_s
# etc etc
end
Then in your search do...
if handicap
test_handicap = handicap.downcase.mb_chars.normalize(:kd).gsub(/[^x00-\x7F]/n, '').to_s
result = result.where('handicap_ascii LIKE ?', "%#{handicap}%")
end
It's not great, as it basically forces you to duplicate data in your database into extra columns. If you can consider more sophisticated databases other than SQLite then you'd be better off... personally I wouldn't ever use SQLite in a production environment.

Searchkick: Searching multiple specific fields in the same query

i'm trying to make an advanced search in my rails app but i'm having some problems with empty params
class Product< ActiveRecord::Base
searchkick
when i fill the search and size fields everyting works, but if i leave size field blank nothing show up in the results...
probably i'm doing something stupid
i made it work with a bunch of IFs:
def index
if params[:search].present?
if params[:size].present?
#products = Product.search params[:search], where: {size: params[:size]}
else
#products = Product.search params[:search]
end
else
if params[:size].present?
#products = Product.search "*", where: {size: params[:size]}
else
#products = Product.search "*"
end
end
but probably thats not the best approuch, having in mind that i will search in at least 5 other fields...
Search, Size, Brand, Color, Store.state, Price, Rating etc...
sorry for my english, i hope you guys understand my question and get able to help me..
My be smth:
...
search_condition = params[:search] || '*'
where_conditions = params.slice(:size, :brand, :color, ...)
#products = if where_conditions.any?
Product.search search_condition, where: where_conditions
else
Product.search search_condition
end
Found a solution where
Setting up Facets in Elasticsearch with Searchkick gem in Rails 4.1
query = params[:query].presence || "*"
conditions = {}
conditions[:state] = params[:state] if params[:state].present?
conditions[:city] = params[:city] if params[:city].present?
movies = Movie.search query, where: conditions

Rails 4, Postgresql - Custom query incorrectly constructed inside controller, correctly in console

I'm trying to write a query that will match last names that at least start with the submitted characters. No matter how I construct the query though, whether I use ILIKE, LIKE, ~*, or ##, the query that's constructed when the method is called from the controller changes the query to a simple = operator.
The full method:
def self.search(params)
if params.empty?
return Evaluation.none
end
results = nil
if params.include?(:appointment_start_date) && params.include?(:appointment_end_date)
results = Evaluation.where(appointment_time: params.delete(:appointment_start_date)..params.delete(:appointment_end_date))
end
params.each do |key, value|
case key
when :l_name
results = (results || Evaluation).where("to_tsquery('english', '#{value}:*') ## to_tsvector('english', l_name)")
when :ssn
results = (results || Evaluation).joins(:candidate).where(candidates: { ssn: value })
else
results = (results || Evaluation).where("#{key} = ?", value)
end
end
results.includes(:evaluation_type, :agency, :psychologist)
end
But the pertinent piece is:
.where("to_tsquery('english', '#{value}:*') ## to_tsvector('english', l_name)")
If I call the method from the Rails console, the expected SQL query is generated and the correct records are returned. Now here's the controller action:
def search
authorize! :read, Evaluation
#evaluations = Evaluation.search(evaluation_search_params).paginate(page: params[:page]).decorate
end
Let's say the user submits 'kel' for the search. For some flippin' reason, the generated query is
SELECT "evaluations".* FROM "evaluations" WHERE (l_name = 'kel') ORDER BY "evaluations"."appointment_time" ASC LIMIT 30 OFFSET 0
(There's a default_scope on Evaluation, hence the ORDER BY, so that's not part of the mystery.)
I've tried removing the pagination and the Draper decoration to no avail, and both of those also work fine in the console. So, my question is, why the heck is the generated query different when called from the controller? I'm seriously at my wit's end with this.
Are you param keys strings instead of symbols? That might be affecting which branch of the case statement is selected. Try debugging and stepping through the code if you are unsure.
You may just need to change to string values:
params.each do |key, value|
case key
when "l_name"
results = (results || Evaluation).where("to_tsquery('english', '#{value}:*') ## to_tsvector('english', l_name)")
when "ssn"
results = (results || Evaluation).joins(:candidate).where(candidates: { ssn: value })
else
results = (results || Evaluation).where("#{key} = ?", value)
end
end

Ruby on Rails: Search on has_many association with inner join and other conditions

I've been searching for this for a long time now, and I can't get to find the answer anywhere.
What I have is a basic search function to find houses, everything works, and till so far the main piece for getting houses looks something like this (the conditions are for example):
#houses = House.all(
:conditions => ["houses.province_id = ? AND open_houses.date > ? AND houses.surface = ?", "4", "Tue, 08 Feb 2011", "125"],
:select => 'distinct houses.*',
:joins => "INNER JOIN open_houses ON open_houses.house_id = houses.id" )
Now, I have this has_many association for the specifications of a house (like balcony, swimming pool etc)..
For this I have the standard setup. A table for the spec-names, and a table with the house_id and the specification_id.
But now, I need to add these to the search function. So someone can find a house with Swimming pool AND a balcony.
I'm sure there is a solution, but I just don't know where to put it in the code, and how.. Google just get's me to pages like it, but not to pages explaining exactly this.
This is what my params look like:
Parameters: {"dateto"=>"", "commit"=>"ZOEKEN!", "pricefrom"=>"", "location"=>"", "province_id"=>"4", "rooms"=>"", "datefrom"=>"", "surface"=>"125", "utf8"=>"✓", "priceto"=>"", "filters"=>{"by_specifications"=>["2", "5", "10"]}, "house_category_id"=>""}
Hope someone can help, if something is unclear please let me know.
Thanks
EDIT:
Oke, I've got it to work! Thanks very much!
There's only one little problem: A house shows up if either one of the specs exists for the house.. So it's an OR-OR query, but what I want is AND-AND..
So if a user searches for balcony and garage, the house must only show up if both of these exist for the house..
What I did now is this: for each specification searched, a query is being made.. (code is below)
I'm wondering, is this the right way to go?
Because it works, but I get double matches.. ( I filter them using "uniq" )
The problem with uniq is that I can't get "will_paginate" to work with it..
This is my final code:
def index
ActiveRecord::Base.include_root_in_json = false
# I'm creating conditions with a function I made, PM me for the code..
conditions = createConditions( createParameters( ) );
query = House.includes( :open_houses ).where( conditions )
unless params[:specifications].blank?
query = query.joins( :house_specifications )
query = query.group( 'open_houses.id' )
query = query.where( 'house_specifications.specification_id' => params[:specifications] )
query = query.order( 'count(open_houses.id) DESC' )
# query = query.having( 'count(open_houses.id) = ?', [params[:specifications].length.to_s] )
end
query = query.order( (params[:sort].blank?)? "open_houses.date ASC, open_houses.from ASC" : params[:sort] )
if params[:view] == "list"
page = params[:page] unless params[:page].blank?
#houses = query.all.uniq.paginate( :page => page || "1", :per_page => 5 )
else
#houses = query.all.uniq
end
respond_to do |format|
format.html
format.js
end
end
Thanks for the help, really appreciate it!
Try this (Rails 3):
House.joins(:houses_specifications).
where('houses_specifications.specification_id' => params[:by_specifications]).
where(...).all
You can have a bunch of if's and case's to add filters, group_by's, limits etc before you run that final .all on the query.
house_query = House.where(:pink => true)
unless params[:by_specifications].blank?
house_query = house_query.joins(:houses_specifications).
where('houses_specifications.specification_id' => params[:by_specifications])
end
...
#houses = house_query.all
Edit:
New and improved version that doesn't query directly on the join table for readability, group_by for distinctness and does an intersect to get houses with all specs.
query = House.joins(:open_houses).where(conditions)
unless params[:specifications].blank?
query = query.joins(:specifications).
group('houses.id').
where(:specifications => params[:specifications]).
having('count(*)=?', params[:specifications].count)
end
if params[:view] == "list"
#houses = query.all.paginate(:page => params[:page])
else
#houses = query.all
end
You can change the "having" to an "order" to get those with the most matching specs first.

Optional mix of filter parameters in a search the Rails way

I've got a simple list page with a couple of search filters status which is a simple enumeration and a test query which I want to compare against both the title and description field of my model.
In my controller, I want to do something like this:
def index
conditions = {}
conditions[:status] = params[:status] if params[:status] and !params[:status].empty?
conditions[???] = ["(descr = ? or title = ?)", params[:q], params[:q]] if params[:q] and !params[:q].empty?
#items = Item.find(:all, :conditions => conditions)
end
Unfortunately, it doesn't look like I can mix the two types of conditions (the hash and the paramatized version). Is there a "Rails Way" of doing this or do I simply have to do something awful like this:
has_status = params[:status] and !params[:status].empty?
has_text = params[:q] and !params[:q].empty?
if has_status and !has_text
# build paramatized condition with just the status
elsif has_text and !has_status
# build paramatized condition with just the text query
elsif has_text and has_status
# build paramatized condition with both
else
# build paramatized condition with neither
end
I'm migrating from Hibernate and Criteria so forgive me if I'm not thinking of this correctly...
Environment: Rails 2.3.4
You can mix hash and array conditions using scopes:
hash_conditions = {}
# build hash_conditions
items_scope = Item.scoped(:conditions => hash_conditions)
unless params[:q].blank?
items_scope = items_scope.scoped(:conditions => ["(descr = ? or title = ?)", params[:q], params[:q]])
end
...
items = items_scope.all
So you can mix and match any types of conditions, and the query will be executed only when you do items_scope.all
a=[],b=[]
unless params[:status].blank?
a << "status = ?"
b << params[:status]
end
unless params[:q].blank?
a << "(descr = ? or title = ?)"
b << params[:q] << params[:q]
end
#items = Item.all( :conditions => [a.join(" AND "), b] )
A better search on my part turned up something called "named scopes" which looks like is exactly what I'm looking for. I'm about to see if it will work with the will_paginate gem....
Reference:
http://edgerails.info/articles/what-s-new-in-edge-rails/2010/02/23/the-skinny-on-scopes-formerly-named-scope/

Resources