Rails3: Searching for Users by Second nad First name? - ruby-on-rails

I want to write a simple search method in my User model where it checks agisnt the Second name and first name and returns matching users. I have this at the moment but throws an error:
def self.search(search)
if search
where("first_name like ? or second_name like ?", "%#{search}%")
else
all
end
end
the error is: wrong number of bind variables (1 for 2) in: first_name like ? or second_name like ?
How can i fix this?
Thanks

You have two ? which means the where method is expecting two arguments:
def self.search(search)
if search
where("first_name like ? or second_name like ?", "%#{search}%", "%#{search}%")
else
all
end
end
I'm not sure if you can streamline those likes to use one argument instead of the duplicate two, but you could clean it up a little:
def self.search(search)
if search
q = "%#{search}%"
where("first_name like ? or second_name like ?", q, q)
else
all
end
end

You can use
where("first name like :name or second name like :name", :name => "%foo%")

Related

Error in using WHERE for search function in Ruby on Rails

I made a controller to search something ,
but the result was weird:
My code:
def create
#word = searching_params[:word]
#searching = current_user.searchings.build(word: #word)
flash[:notice] = "New searching is performed!" if #searching.save
#users = User.where("firstname LIKE ? OR lastname LIKE ?", "%#{#word}%", "%#{#word}%")
#posts = Post.where("body LIKE ?", "%#{#word}%")
render :index
end
So, when i searched for a name: Mose Collins,
o, se, ose could get the result,
but m, c, co and others would give me nothing.
LIKE performs a case sensitive match. If you want to perform a case insensitive match in a somewhat polyglot fashion you can use the LOWER() SQL function:
#users = User.where("LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ?", "%#{#word.downcase}%", "%#{#word.downcase}%")
Postgres has a ILIKE function which is case insensitive:
#users = User.where("firstname ILIKE ? OR lastname ILIKE ?", "%#{#word.downcase}%", "%#{#word.downcase}%")
You can also use Arel to construct it instead of a SQL string:
class User < ApplicationRecord
has_many :favorite_jobs
def self.search(term)
where(
arel_table[:firstname].matches("#{name}").or(
arel_table[:lastname].matches("#{name}")
)
)
end
end
This approach is more portable and espcially shines if you want to built the query programatically.

Using multiple LIKE statements in an ActiveRecord Query

I am fairly new to Rails. I have been builidng a search bar that goes through all of my products. I got it to work when I am only searching through either the name of the product or the description. But I would like the search term to be compared to both and the product to be displayed if either the name matches the search term or it matches the description.
This is my code at the moment:
if params[:q]
search = params[:q]
#products = Product.where("name LIKE ? OR description LIKE ?", "%#{search}%")
else
#products = Product.all
end
Right now I am getting an error: "Wrong Number of Bind Variable"
I have been trying to google for a solution but I havn't gotten lucky. I would reallz appreciate if someone could help me! Thanks so much.
If your variables in query have the same value, you can use named key:
#products = Product.where(
"name LIKE :search OR description LIKE :search", search: "%#{search}%"
)
If they different:
#products = Product.where(
"name LIKE :first_search OR description LIKE :second_search",
first_search: "%#{f_search}%", second_search: "%#{s_search}%"
)
or just use question marks:
#products = Product.where(
"name LIKE (?) OR description LIKE (?)", "%#{f_search}%", %#{s_search}%"
)
You need to provide search key twice. One for each ?
search_key = "%#{search}%"
#products = Product.where("name LIKE ? OR description LIKE ?", search_key, search_key)

Postgres Rails 4 search query id or multiple columns

I am working in an app with a basic search form with Heroku, but I can't get my sql query to work properly with PostgreSQL, even though this query worked with MySQL. By the way, I tried to paste the logs from Heroku, but it only says that when you search something it renders 500.html.
Here's my model OrdemDeServico with the search action:
def self.search(search)
if search
joins(:cliente).where("clientes.nome LIKE ? OR veiculo LIKE ? OR placa LIKE ? OR ordem_de_servicos.id = ?", "%#{search}%", "%#{search}%", "%#{search}%", "#{search}")
else
where(nil)
end
end
I just installed PostgreSQL locally, and it returned this error when searching:
`PG::InvalidTextRepresentation: ERROR: invalid input syntax for integer: "Augusto"
LINE 1: ... placa LIKE '%Augusto%' OR ordem_de_servicos.id = 'Augusto')
query:
SELECT "ordem_de_servicos".* FROM "ordem_de_servicos" INNER JOIN "clientes" ON "clientes"."id" = "ordem_de_servicos"."cliente_id" WHERE (clientes.nome LIKE '%Augusto%' OR veiculo LIKE '%Augusto%' OR placa LIKE '%Augusto%' OR ordem_de_servicos.id = 'Augusto') ORDER BY prazo LIMIT 5 OFFSET 0
I finally worked it out a solution. Those who have the same problem here's my code for the model:
def self.search(search)
if search
where("id = ?", search)
joins(:cliente).where("clientes.nome ilike :q or veiculo ilike :q or placa ilike :q", q: "%#{search}%")
else
where(nil)
end
end

Rails ActiveRecord - Search on Multiple Attributes

I'm implementing a simple search function that should check for a string in either the username, last_name and first_name. I've seen this ActiveRecord method on an old RailsCast:
http://railscasts.com/episodes/37-simple-search-form
find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
But how do I make it so that it searches for the keyword in name, last_name and first name and returns the record if the one of the fields matched the term?
I'm also wondering if the code on the RailsCast is prone to SQL injections?
Thanks a lot!
I assumed your model name is Model - just replace it with your real model name when you do the actual query:
Model.where("name LIKE ? OR last_name LIKE ? OR first_name LIKE ?", "%#{search}%","%#{search}%","%#{search}%")
About your worries about SQL injections - both of code snippets are immune to SQL injections. As long as you do not directly embed strings into your WHERE clause you are fine. An example for injection-prone code would be:
Model.where("name LIKE '#{params[:name]}'")
Although the selected answer will work, I noticed that it breaks if you try to type a search "Raul Riera" because it will fail on both cases, because Raul Riera is not either my first name or my last name.. is my first and last name... I solved it by doing
Model.where("lower(first_name || ' ' || last_name) LIKE ?", "%#{search.downcase}%")
With Arel, you can avoid writing the SQL manually with something like this:
Model.where(
%i(name first_name last_name)
.map { |field| Model.arel_table[field].matches("%#{query}%")}
.inject(:or)
)
This would be particularly useful if the list of fields to match against was dynamic.
A more generic solution for searching in all fields of the model would be like this
def search_in_all_fields model, text
model.where(
model.column_names
.map {|field| "#{field} like '%#{text}%'" }
.join(" or ")
)
end
Or better as a scope in the model itself
class Model < ActiveRecord::Base
scope :search_in_all_fields, ->(text){
where(
column_names
.map {|field| "#{field} like '%#{text}%'" }
.join(" or ")
)
}
end
You would just need to call it like this
Model.search_in_all_fields "test"
Before you start.., no, sql injection would probably not work here but still better and shorter
class Model < ActiveRecord::Base
scope :search_all_fields, ->(text){
where("#{column_names.join(' || ')} like ?", "%#{text}%")
}
end
The best way to do this is:
Model.where("attr_a ILIKE :query OR attr_b ILIKE :query", query: "%#{query}%")

Search multiple columns - Rails

I am currently writing a search method for my rails applications, and at the moment it works fine. I have the following in my game.rb:
def self.search(search)
if search
find(:all, :conditions => ['game_name LIKE ? OR genre LIKE ? OR console LIKE ?', "%#{search}%", "#{search}", "#{search}"])
else
find(:all)
end
end
Now that searches fine, but my problem is that if there is a record in game_name that has the word 'playstation' in it, it will finish the search there. It only returns that record, rather than all games that have 'playstation' stored in console. Now I understand this is because I have 'OR' in my conditions, but I don't know an alternative. 'AND' requires all the conditions to match or none return at all. What is an alternative I can use to AND and OR? Help would be much appreciated.
If there is a solution that has separate search boxes and entries, then that would be fine, I don't necessarily require the search to find it all based on one search form.
If I understand your question correctly, your SQL looks good to me for what you are trying to do. An OR clause will return all records that match in column1, column2, or column3. It doesn't stop at the first match. I do see an issue with your parameters in that the first you are using LIKE with % but in the second two you aren't, maybe that is where your issue is coming from.
Should this be your find (% around second and third search)?
find(:all, :conditions => ['game_name LIKE ? OR genre LIKE ? OR console LIKE ?', "%#{search}%", "%#{search}%", "%#{search}%"])
or better use DRY version (above will not work for Rails 4.2+):
Item.where('game_name LIKE :search OR genre LIKE :search OR console LIKE :search', search: "%#{search}%")
What if you have 15 columns to search then you will repeat key 15 times. Instead of repeating key 15 times in query you can write like this:
key = "%#{search}%"
#items = Item.where('game_name LIKE :search OR genre LIKE :search OR console LIKE :search', search: key).order(:name)
It will give you same result.
Thanks
I think this is a little bit of a cleaner solution. This allows you to add/remove columns more easily.
key = "%#{search}%"
columns = %w{game_name genre console}
#items = Item.where(
columns
.map {|c| "#{c} like :search" }
.join(' OR '),
search: key
)
A more generic solution for searching in all fields of the model would be like this
def search_in_all_fields model, text
model.where(
model.column_names
.map {|field| "#{field} like '%#{text}%'" }
.join(" or ")
)
end
Or better as a scope in the model itself
class Model < ActiveRecord::Base
scope :search_in_all_fields, ->(text){
where(
column_names
.map {|field| "#{field} like '%#{text}%'" }
.join(" or ")
)
}
end
You would just need to call it like this
Model.search_in_all_fields "test"
Before you start.., no, sql injection would probably not work here but still better and shorter
class Model < ActiveRecord::Base
scope :search_all_fields, ->(text){
where("#{column_names.join(' || ')} like ?", "%#{text}%")
}
end
I think this is a more efficient solution if you want to search an array of columns as I do.
First and most importantly you can add a private function to your model that creates a query template:
def self.multiple_columns_like_query(array)
array.reduce('') { |memo, x| #
unless memo == '' #
memo += ' or ' # This is the
end #
memo += "#{x} like :q" # core part
} #
end
Than you can use the function in your search function:
def self.search(query)
if fields = self.searched_fields && query
where multiple_like_query(fields), q: "%#{query}%"
end
end
Here you should also define self.searched_fields as an array of field names.

Resources