I have a model named Topic, that has a name as a field.
So say I have a term I'm searching for, apple.
If I do a
Topic.find_by_name("apple")
I get a record back with the name apple. That's good -- but how do I change find_by_name so that it can find "apple juice" as well as "apple" -- basically, find names that contain the original query or exactly match the original query?
Edit:
Thanks for all the response. I guess I should've been a little more clear earlier, but what if I want to find by a variable name (obviously I'm not going to want to find by the name "apple" everytime :) )?
How do I manipulate Topic.where to accommodate for this?
So something like...
#topic = Topic.where(......., #name)
I think something like this should work:
Topic.where("name like ?", "%apple%")
To accomodate for your edit:
Topic.where("name like ?", "%#{#search}%")
Basic string interpolation, you're using the value of #search inside the string %%, so you #search = "apple" then you end up with %apple%
With PostgreSQL you can also use match operators:
Topic.where("name ~* ?", #search)
Looks like in Rails 3 you would want to use the where:
Topic.where("name ILIKE ?", "%apple%")
Don't put string directly like that. Its called SQL injection. You should instead use .where:
Topic.where("name like '?%' ", params[:name])
Try
Topic.where("name like ?",'%apple%')
Topic.find(:all, :conditions => ["name like ?","%apple%"])
Related
I need to find a record from 2 parameters but I need one of them to be case insensitive. The current case sensitive line is
c = Course.find_by(subject_area: area, cat_number: cat)
But I need subject_area to be case insensitive. How would I achieve that?
It depends on the database, and you may need to pass in db-specific SQL for that (and not use find_by).
Are you using postrges? if so, this would normally work:
Course.where("LOWER(subject_area) = ? AND cat_number = ?", area.downcase, cat)
alternatively you could convert your subject_area to downcase every time you save a new one... then just use:
Course.find_by(subject_area: area.downcase, cat_number: cat)
I could be wrong, but don't currently know of any rails-native way of doing a case insensitive rails find_by
An alternative can be
c = Course.find_by("LOWER(subject_area)= ? AND cat_number = ?", area.downcase, cat)
If you are using postgres, you could do something like:
> c = Course.find_by('subject_area ILIKE ? AND cat_number LIKE ?', area, cat)
(ILIKE is used to do insensitive search)
If you need to have this attribute be case-insensitive then the right way is to ensure it's always one particular in your model
before_save { self.subject_area = subject_area.downcase }
You can migrate your existing data to be downcased as well.
Having done that your find by operation still needs to ensure that the area is downcased in query as suggested.
Course.find_by(subject_area: area.downcase, cat_number: cat)
Option 2:
I knew I was forgetting this. Let's say you dont want to go migration path. Just define a scope.
http://guides.rubyonrails.org/v4.1/active_record_querying.html#scopes
scope :area_nocase, (area) -> { where("LOWER(subject_area) = ?", area.downcase) }
then you can use it like
Course.area_nocase(area) or cat.course.area_nocase(area)
or chain it however you need to.
I am trying to use a wildcard or regular expression to give some leeway with user input in retrieving information from a database in a simple library catalog program, written in Ruby.
The code in question (which currently works if there is an exact match):
puts "Enter the title of the book"
title = gets.chomp
book = $db.execute("SELECT * FROM books WHERE title LIKE ?", title).first
puts %Q{Title:#{book['title']}
Author:#{book['auth_first']} #{book['auth_last']}
Country:#{book['country']}}
I am using SQLite 3. In the SQLite terminal I can enter:
SELECT * FROM books WHERE title LIKE 'Moby%'
or
SELECT * FROM books WHERE title LIKE "Moby%"
and get (assuming there's a proper entry):
Title: Moby-Dick
Author: Herman Melville
Country: USA
I can't figure out any corresponding way of doing this in my Ruby program.
Is it not possible to use the SQL % wildcard character in this context? If so, do I need to use a Ruby regular expression here? What is a good way of handling this?
(Even putting the ? in single quotes ('?') will cause it to no longer work in the program.)
Any help is greatly appreciated.
(Note: I am essentially just trying to modify the sample code from chapter 9 of Beginning Ruby (Peter Cooper).)
The pattern you give to SQL's LIKE is just a string with optional pattern characters. That means that you can build the pattern in Ruby:
$db.execute("SELECT * FROM books WHERE title LIKE ?", "%#{title}%")
or do the string work in SQL:
$db.execute("SELECT * FROM books WHERE title LIKE '%' || ? || '%'", title)
Note that the case sensitivity of LIKE is database dependent but SQLite's is case insensitive so you don't have to worry about that until you try to switch database. Different databases have different ways of dealing with this, some have a case insensitive LIKE, some have a separate ILIKE case insensitive version of LIKE, and some make you normalize the case yourself.
I wrote a little search query in rails:
.where("bezeichnung or nummer like ?", "%#{search}%")
It somehow wont work corectly, i dont get a error but it seems like it only makes a search query on the nummer field. Then i tried:
.where("bezeichnung like ?", "%#{search}%")
With the same search param i got a lot more recors. What did i wrong? Thanks
To match on two terms with an or using a single where call, you'll need to pass the search to both clauses, and separate them with the or:
.where("bezeichnung LIKE ? OR nummer LIKE ?", "%#{search}%", "%#{search}%")
Suddenly I've realized that while this works in groovy just like it is expeceted:
Sql.newInstance(connectionParams).rows("SELECT FROM ITEMS WHERE id = ?", [200])
this won't work
Sql.newInstance(connectionParams).rows("SELECT FROM ITEMS WHERE name LIKE '%?%'", ["some"])
All you can get is
Failed to execute: SELECT FROM ITEMS WHERE name LIKE '%?%' because:
The column index is out of range: 1, number of columns: 0.
My questions are:
Is it intentionally implemented this way? I've never needed to have a parametrized text search, so I'm not sure where this behaviour is typical or not.
How can I nevertheless safely parametrize statement with text search in it?
I believe you want to include the %'s in the parameter, like:
Sql.newInstance(connectionParams).rows("SELECT FROM ITEMS WHERE name LIKE ?", ["%some%"])
I'm fairly new and playing around with searching databases in Rails. I have a model and database that has a list of names under the 'name' attribute. I want to be able to enter search keywords into a single search field and this input can be one word or two words or more, depending on how specific a result the user wants.
Right now, I'm using something ugly as shown below, which will do a maximum of 3 search terms. Is there a way to make this dynamic for 'search_length' keywords? The find method is clearly repetitive, but I'm not sure how to automate it and haven't found any useful suggestions elsewhere online.
def self.search(search)
if search
search_length = search.split.length
find(:all, :conditions => ['name LIKE ? AND name LIKE ? AND name LIKE ?',
"%#{search.split[0]}%", "%#{search.split[1]}%",
"%#{search.split[search_length1]}%"])
else
find(:all)
end
end
Other than this, loving Rails so far.
Thanks Much,
Lev
The code from Łukasz Śliwa works great if you close the name variable with the other % sign.
The complete code from above working for me. Great post.
def self.search(search)
if search
search_length = search.split.length
find(:all, :conditions => [(['name LIKE ?'] * search_length).join(' AND ')] + search.split.map { |name| "%#{name}%" })
else
find(:all)
end
end
Use something like this:
find(:all, :conditions => [(['name LIKE ?'] * search_length).join(' AND ')] + search.split.map { |name| "%#{name}" })
I looks strange but, first generate search_length times string 'name LIKE ?':
['name LIKE ?'] * search_length
then you have array with some keys, so let's join all of them with ' AND ':
["name LIKE ? ", "name LIKE ? ", "name LIKE ? "].join(' AND ')
and finally merge with another array.
formatted_columns = format_column_names(Sub.column_names)
where(formatted_columns.map {|cn| "#{cn} like ?" }.join("or "), *(["%#{search}%"] * formatted_columns.size))
this takes care of all columns as well as the correct amount of fields