Search with multiple columns in Rails - ruby-on-rails

While using the command:
#items = Item.find(:all,:order => 'name', :conditions => ["name LIKE ?", "%#{params[:key]}%"])
This works perfectly fine, but the search is only based on just a column. How do i make it search other columns, like description, category?

I am assuming you are using Rails 2, since you are using ActiveRecord 2 style syntax. You can just add the other columns as additional conditions, like this:
key = "%#{params[:key]}%"
#items = Item.find(:all, :conditions => [
'name LIKE ? OR description LIKE ? OR category LIKE ?',
key, key, key
], :order => 'name')
For reference, here's how you would do it in Rails 3:
key = "%#{params[:key]}%"
#items = Item.where('name LIKE ? OR description LIKE ? OR category LIKE ?', key, key, key).order(:name)

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 = "%#{params[:key]}%"
#items = Item.where('name LIKE :search OR description LIKE :search OR category LIKE :search', search: key).order(:name)
It will give you same result.
Thanks

Related

Rails activerecord LIKE AND clause error

Here is an activerecord query i'm trying to use in rails
q = "Manchester"
b = "John Smith"
Model.find(:all, :conditions => ["city ? AND name like ?", q, b])
but i get this error in rails console
ActiveRecord::StatementInvalid: SQLite3::SQLException: near "'Manchester'": syntax error: SELECT "model".* FROM "model" WHERE (city 'Manchester' AND name like 'John Smith')
Please help!
You missed LIKE for city.
Model.where('city LIKE ? AND name LIKE ?', "%#{q}%", "%#{b}%");
You can also use this syntax which is a lot more readable than trying to figure out which ? goes with which variable. I mean if you have 1 or 2 it's fine, but once you have more it gets pretty ugly.
Model.where("city LIKE :city AND name LIKE :name", { city: "%#{q}%", name: "%#{b}%" })
The placeholders and hash key can be anything you like as long as they match (don't use :city and then hamster: in the hash key for example).
The nice thing about this is that you can also use one variable for multiple searches:
where("user LIKE :term OR email LIKE :term OR friends LIKE :term", { term: "%#{params[:term]}%"})
Try this:
Model.find(:all, :conditions => ["city = ? AND name like ?", q, b])

Rails: Sorting Objects From Different Models

I've populated a hash with two different models. I then try to sort them like so:
#search_results = User.find(:all, :conditions => ['name LIKE ?', "%#{params[:query]}%"])
#search_results += Book.find(:all, :conditions => ['title LIKE ?', "%#{params[:query]}%"])
#search_results.sort! { |a,b| a.impressions_count <=> b.impressions_count }
This throws the following error:
comparison of User with Book failed
Both users and books have an integer-based impressions_count. Why can't I sort via this attribute? What other options do I have?
I faced a similar problem recently and ended up writing some custom sql because all other ways returned an array. Pretty sure its not a good idea to use the sort method since it will always be more efficient to sort in SQL than ruby, especially when the data set gets large
#combined_results = User.find_by_sql(["SELECT title, id, impressions_count, NULL as some_attribute_of_book
FROM user
WHERE title LIKE ?
UNION SELECT title, id, impressions_count, some_attribute_of_book FROM book
WHERE title LIKE ?
ORDER BY impressions_count", params[:query], params[:query]])
The above is completely untested code, more of an example than anything

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.

Rails 3: Trying to understand join query

I have a User class and a GroupUser class. I'm trying to do a search by name of the users. I tried following what I read on the joins, but I have something wrong. Also I need to change my name portion of the query to a like instead of an equals
Here is the query I had initially built.
#users = GroupUser.joins(:users).where(:group_id => params[:group_id]).where(:users => {:name => params[:q]})
Try this:
#users = User.where("name ilike ? and id in (select distinct user_id from groups_users where group_id = ?)", "%#{params[:q]}%", params[:group_id])

rails find - condition on array of db-fields

I am stuck and I guess it is a syntax thing I just don't get:
It's a one(category)_to_many(product) relationship.
In my category model I have the columns pick1, pick2, pick3, pick4, pick5.
Each of those holds the id of a product.
In my category_controller I want to retrieve those in a find:
#productpicks = Product.find(:all, :conditions =>
['online = ? and category_id IN (?)', true,
[#category.pick1, #category.pick2, #category.pick3, #category.pick4, #category.pick5]])
... and iterate over them in the view like this:
do something
But there is nothing to be found in that array ...
Does anyone have an idea what I am doing wrong?
Thanks for helping!
Val
Shouldn't it be:
#productpicks = Product.find(
:all,
:conditions => [
'online = ? and id IN (?)',
true, [
#category.pick1,
#category.pick2,
#category.pick3,
#category.pick4,
#category.pick5
]
]
)
Replacing category_id with id in the where clause?
As pick1-5 hold product ids, and you are trying to find those specific products.

Resources