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
Related
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}%")
I have a materials model which has a search form. The search action looks a bit like this:
def search
conditions = {}
conditions[:version] = 'master'
conditions[:status] = 'shared'
conditions[:targ_lang] = params[:targ_lang] unless params[:targ_lang].blank?
#results = Material.find(:all, :conditions => conditions)
end
I have added the acts-as-taggable gem and it works fine to save the tags but I'm having trouble adding it to the search form. The documentation states that to find Materials with the tags you can use this code:
Material.tagged_with(["awesome", "cool"], :match_all => true)
But I don't know how to add this condition to the conditions.
Update
#results = Material.where(conditions) && Material.tagged_with(params[:tag_list])
This works provided tags are used but it doesn't work if the tag list is blank so I need a condition as with the other conditions above that the Material.tagged_with ... part is only necessary if the field is not empty.
Update 2 - Bad Solution
This works but it's not very elegant is it?
if params[:tag_list].blank?
#results = Material.where(conditions)
else
#results = Material.tagged_with(params[:tag_list]).where(conditions)
end
This code won't work for you?
Material.where(conditions).tagged_with(['awesome', 'cool'], :match_all => true)
Or the inverse order:
Material.tagged_with(['awesome', 'cool'], :match_all => true).where(conditions)
UPDATE
Reading the docs on the act-as-taggable-one on github, there is a option named :any. Maybe you can try to use it. I don't have a project i could do some testing, but maybe a code like:
Material.tagged_with(['awesome', 'cool', '', nil], :any => true).where(conditions)
Give it a try.
I have a search on my website, and today I discovered that if a post has for example a description like "implicación en el proyecto" and I try to find this post using the word "implicacion" without the accent, I don't get any results, but if I try with "implicación" using the letter o with the accent, I found the post.
How can I fix this search, I need to find the post even if the user use the word without the accent.
This is the actual search on my model. I'm using postgresql for production.
find(:all, :conditions => ['title LIKE ? OR title LIKE ? OR description LIKE ? OR description LIKE ?', "%#{search}%", "%#{search.capitalize}%", "%#{search}%", "%#{search.capitalize}%"] )
Thanks in advance for your help
EDIT
private
def pg_strip_accents(field)
"<<EOS
translate(
LOWER(#{field}),
'âãäåāăąÁÂÃÄÅĀĂĄèééêëēĕėęěĒĔĖĘĚìíîïìĩīĭÌÍÎÏÌĨĪĬóôõöōŏőÒÓÔÕÖŌŎŐùúûüũūŭůÙÚÛÜŨŪŬŮ',
'aaaaaaaaaaaaaaaeeeeeeeeeeeeeeeiiiiiiiiiiiiiiiiooooooooooooooouuuuuuuuuuuuuuuu'
)
EOS"
end
def self.search(search)
if search.present?
where("#{pg_strip_accents('title')} LIKE :search OR #{pg_strip_accents('description')} LIKE :search",
:search => "%#{search.downcase}%")
else
find(:all)
end
end
You can use the postgresql translate function to do this as shown here. Also, you can halve the amount of search terms if you convert both sides to upper or lowercase before testing them:
where(<<EOS
translate(
LOWER(title),
'âãäåāăąÁÂÃÄÅĀĂĄèééêëēĕėęěĒĔĖĘĚìíîïìĩīĭÌÍÎÏÌĨĪĬóôõöōŏőÒÓÔÕÖŌŎŐùúûüũūŭůÙÚÛÜŨŪŬŮ',
'aaaaaaaaaaaaaaaeeeeeeeeeeeeeeeiiiiiiiiiiiiiiiiooooooooooooooouuuuuuuuuuuuuuuu'
)
LIKE :search OR
translate(
LOWER(description),
'âãäåāăąÁÂÃÄÅĀĂĄèééêëēĕėęěĒĔĖĘĚìíîïìĩīĭÌÍÎÏÌĨĪĬóôõöōŏőÒÓÔÕÖŌŎŐùúûüũūŭůÙÚÛÜŨŪŬŮ',
'aaaaaaaaaaaaaaaeeeeeeeeeeeeeeeiiiiiiiiiiiiiiiiooooooooooooooouuuuuuuuuuuuuuuu'
)
LIKE :search
EOS }, :search => "%#{search.downcase}%")
As shown in the link, you can create a dedicated function to make this easier to reuse. Or you can use ruby code to do that for you:
def pg_strip_accents(field)
<<EOS
translate(
LOWER(#{field}),
'âãäåāăąÁÂÃÄÅĀĂĄèééêëēĕėęěĒĔĖĘĚìíîïìĩīĭÌÍÎÏÌĨĪĬóôõöōŏőÒÓÔÕÖŌŎŐùúûüũūŭůÙÚÛÜŨŪŬŮ',
'aaaaaaaaaaaaaaaeeeeeeeeeeeeeeeiiiiiiiiiiiiiiiiooooooooooooooouuuuuuuuuuuuuuuu'
)
EOS
end
where("#{pg_strip_accents('title')} LIKE :search OR #{pg_strip_accents('description')} LIKE :search",
:search => "%#{search.downcase}%")
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%"])
I'm trying to query an OR WHERE into an acts-as-taggable-on query, like so...
Business.tagged_with(params[:query], :any => true)
But I'd also like to perform at the same time an or_where like this...
Business.tagged_with(params[:query], :any => true).or_where('name LIKE ?', "%#{params[:query]}%")
This obviously doesn't work as there is no or_where method but would someone know how to perform this correctly?
In short, I'm trying to find a match against any tags OR business name. Thanks.
You can OR two queries together by using the | operator like so:
Business.tagged_with(params[:query], :any => true) | Business.where('name LIKE ?', "%#{params[:query]}%")
Note that this will eager load the results, so you can't apply any more conditions after this, like ordering. It will return an array with all of the matching results in it, excluding duplicates.