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}%")
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 this code for query:
$repository = $em->getRepository('AcmeCrawlerBundle:Trainings');
$query = $repository->createQueryBuilder('p')
->where('p.title LIKE :word')
->orWhere('p.discription LIKE :word')
->setParameter('word', $word)
->getQuery();
$trainings = $query->getResult();
The problem is: even if matches exist, they not found by this query. I used this code to see full sql:
print_r(array(
'sql' => $query->getSQL(),
'parameters' => $query->getParameters(),
));
And what I've got:
FROM Trainings t0_ WHERE t0_.title LIKE ? OR t0_.discription LIKE ? [parameters] => Array ( [word] => Spoken )
(last part of query)
Tell me please what to change?
You forgot the % signs around the word:
->setParameter('word', '%'.$word.'%')
Below are some additional steps you can take to further sanitise input data.
You should escape the term that you insert between the percentage signs:
->setParameter('word', '%'.addcslashes($word, '%_').'%')
The percentage sign '%' and the symbol underscore '_' are interpreted as wildcards by LIKE. If they're not escaped properly, an attacker might construct arbitrarily complex queries that can cause a denial of service attack. Also, it might be possible for the attacker to get search results he is not supposed to get. A more detailed description of attack scenarios can be found here: https://stackoverflow.com/a/7893670/623685
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 am building an app where I'd like to use a simple search to search through the object's title, AND tags (using acts_as_taggable_on) in one search
I am able to construct either, but not both, and am at my wits end trying to figure it out.
To search using tags, I use:
#post = Post.tagged_with(params[:search])
To search the object, I use:
#post = Post.search(params[:search])
And I wrote a method called search in the Post model, as follows:
def self.search(search)
if search
where('question LIKE ?', "%#{search}%")
else
scoped
end
end
Any idea how to combine these two queries? Any attempts so far have been unsuccessful mainly because there isn't a "tag" column in my Post model, etc.
I figured it out and testing it in my solution, I wrote the joins myself so it isn't as clean as I would like so if anybody has a better solution, I'd still love to see it. Here's what I came up with:
q = "%#{search}%"
Post.select('distinct posts.*').joins("LEFT JOIN taggings on posts.id = taggings.taggable_id")
.joins("LEFT JOIN tags on tags.id = taggings.tag_id")
.where('title LIKE ? OR tags.name LIKE ?', q, q )
This is working as expected in my tests. I am testing with three records. One has the search term in the title, another has just the tag and the third has both the title and the tag, so it shouldn't return duplicates.
Hope this helps.
I found this:
Search form with acts_as_taggable_on (Rails 3)
this might work, but I am also paging my results with the kaminari gem so when I use this approach, I get the following error:
undefined method `page' for []:Array
So this won't work in my situation. I would love to find a solution with a single query to handle the whole thing.
As a followup to Andrew's code, I used this search method in my model -- just adding case-insensitivity and making the post "title" query a bit more explicit since my model actually contained a "name" attribute which was ambiguous.
def self.search(search)
if search.present?
q = "%#{search}%"
Post.select('distinct posts.*').joins("LEFT JOIN taggings on posts.id = taggings.taggable_id")
.joins("LEFT JOIN tags on tags.id = taggings.tag_id")
.where('lower(posts.title) LIKE lower(?) OR lower(tags.name) LIKE lower(?)', q, q )
else
all
end
end
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