Case insensitive with special special characters in Rails model - ruby-on-rails

I have the cars_tables and in its name column I have names with special characters, for example:
car_names = Car.pluck :name
=> ["Cárrozería", "Óther Cars", "Forede Lúis", "Ságara Mbobe"]
The values ​​are automatically parameterized making the special characters disappear
car_name_parameterize << ["Cárrozería", "Óther Cars", "Forede Lúis", "Ságara Mbobe"].map { |name| name.parameterize }.join(', ')
=> ["carrozeria", "other-cars", "forede-luis", "sagara-mbobe"]
and with the parameterized values ​​I would like to do a query but I can't since the names have special characters that prevent me from doing so
first_car_name = car_name_parameterize.first
=> carrozeria
Car.find_by('name ILIKE ?', "%first_car_name%")
=> nil ;; nil because the word **carrozeria** doesn't have í special character,
Car.find_by_name "carrozería"
=> #<Car:0x300312318 id: 1, name: "carrozería"...> ;; If it does the query without returning nil but it is because I consulted its name manually when placing "carrozería"
In short, I am looking to make the queries with the columns with the same name but with special characters (usually these characters usually have accents) recognized.
I am looking to make queries to the name of the cars table, canceling the special characters, such as the accent between the words for example
I have also tried the gsub method without success.
If you could help me I would be very happy and thank you for taking the time to read me.

You will need to use the unaccent extension (docs: https://www.postgresql.org/docs/current/unaccent.html).
Basically, install the extension:
CREATE EXTENSION unaccent;
And then you will be able to use the unaccent() function:
where("unaccent(name) LIKE ?", "%#{your_value}%")
Read more details and different alternatives in the following entry: Postgres accent insensitive LIKE search in Rails 3.1 on Heroku

Thanks to the link that the user #markets shared with me, I used the unaccent method to avoid the settlements and then the lower case and it worked for me.
cart_name = "carrozería"
Car.find_by("lower(unaccent(name)) LIKE ?", "%#{cart_name}%")

Related

Postgres LIKE in a rails application

I have a Postgres LIKE statement like this:
#favorites.find_each do |profiles|
#related_profiles = Profile.where('name LIKE ?', '%' + profiles.name + '%')
end
What I'm trying to do is loop through the favourites variable and find all profiles that contain some characters of the name.
For example a name "jasson jackson" should be found if the name contains "jackson" or "jasson"
The query you're looking for would be like:
Profile.where("name LIKE ?", "%#{profiles.name}%")
But note that your #related_profiles may not be properly assigned as the result would be the same as saying:
Profile.where("name LIKE ?", "%#{#favorites.last.name}%")
whereas, I doubt if that is what you need.
Also note that the it would be an ActiveRecord::Collection, an array like object.
A way to work around that is to initialize #related_profiles = [] and then at each point through your loop, you could do:
#related_profiles +=
Profile.where("name LIKE ?", "%#{profiles.name}%")
or another way is:
names = #favorites.map(&:name)
query = names.map{|name| "name LIKE '%#{name}%'"}.join(" OR ")
OR
query = #favorites.map{|favorite| "name LIKE '%#{favorite.name}%'" }.join(" OR ")
THEN
profiles = Profile.where(query)
UPDATE
Based on a comment from #joshrumbut, I decided to reimplement using the bind parameters.
However, code clarity is a bit lost, but here's a way it could be done:
names = #favorites.map(&:name)
query = names.map{|favorite| "name LIKE ?" }.join(" OR ")
profiles = Profile.where(query, *names.map{|name| ("%#{name}%")})
Based on the comment from #muistooshort, I removed the quotes from the first two queries and I think this approach looks a bit cleaner, as he suggested. From the docs
Profile.where('name like any(array[?])', names.map { |s| "%#{s}%" })
For better results with this type of search, You can try use FTS (Full text search).
Rails has a gem with this feature implemented:
https://github.com/Casecommons/pg_search
PgSearch builds named scopes that take advantage of PostgreSQL's full text search.
With this gem installed on your project, just use this statement for search:
PgSearch.multisearch("jasson")
Other option is Elasticsearch, with him you can index yours registers for a better text search.
Hope it helps :)
I'll try with Postgres patterns and "OR" operator - postgres doc
names = #favorites.map(&:name)
Profile.where('name LIKE %(?)%', names.join('|'))

rails query generates oci:error quoted string incorrectly terminated

I have the following code(very legacy) in the application:
results << User.all(
:select => "'#{entry.name}' AS user_name, '#{entry.lastname}, #{entry.firstname}' AS user_full_name, display_name",
:order => "display_name")
Which generates the following query I get the following oci error:
OCIError: ORA-01756: quoted string not properly terminated:
SELECT 'theupsstore2579' AS user_name, 'O'Brien, Perry' AS user_full_name, display_name FROM "Users" WHERE..
Is there a good way to fix my query?
Is using quote_string a good way to do it
Yes there is a good way. You should not ever use interpolated strings eg "string bit #{interpolated_bit}" in a sql query - this is very bad for security and leads to SQL injection attacks.
In this case - because one of the names includes a ' character (in the last-name O'Brien - and once that value is inserted into your SQL string, it treats it like it would any ' character and ends the string - even though there is more after the ' (namely the Brien bit)
That then causes a syntax error for your database.
However - using the interpolated strings this way also leaves you open to SQL injection - what if somebody typed in '; DROP TABLE users; (or equivalent) into their last name field? and your code happily put that into the SQL and ran it? Your way of using interpolated strings is NOT safe. But Rails provides alternatives that are safe - and they are what you should always use.
eg the built-in arel methods (where select order etc) and use the sanitised ? syntax so instead of
results << User.all(
:select => "'#{entry.name}' AS user_name, '#{entry.lastname}, #{entry.firstname}' AS user_full_name, display_name",
:order => "display_name")
you could try
results << User.select("'?' AS user_name, '?, ?' AS user_full_name, display_name", entry.name, entry.lastname, entry.firstname).order("display_name")
(though I question why you want to force all users to have the same name - did you actually want these to be conditions selecting only users with those names instead?)
I strongly recommend you read through all of the Rails Guides. In this case especially the one on how to use Active Record queries:
and also you might want to read up the security guide - specifically, in this case, the Rails guide SQL injection section

How do I handle uppercase and lowercase characters in a custom url?

I want to let users have links to their profiles using their registered usernames.
I store their username exactly how they give it.
I set up my routes to match /:name and then used find_by_name to get it. The problem I have is when you type in example.com/username it doesn't work the name: Username. (Note the uppercase/lowercase difference)
So my question is how can I ignore case in urls?
You can store the username downcased along with a display_name which is in the format they gave it to you. Then, downcase any input and match it with username and only use display_name for, well, display :)
If you wanted, you could even redirect to /DisPlAyName from /username after you look up the record.
Easiest way is to convert the username in the database and in rails to lower (or upper) case when you are doing the comparison.
User.where('lower(username) = ?', params[:name].downcase).first
Or if you are still using rails 2:
User.find(:first, :conditions => ['lower(username) = ?', params[:name].downcase])
One way to do this is to store a lowercase version of their username. When they type in
example.com/UsErNaMe or example.com/Username
downcase it and match it to the lowercase version in the database.
---OR---
Just make the user have a lowercase username everywhere. (Downcase it when its made)
---OR---
Have a constant type of username. So always have the first letter capitalized or something. Store it as lower case and upcase the first letter everytime you show it.

Stripping the first character of a string

i have
string = "$575.00 "
string.to_f
// => 0.0
string = "575.00 "
string.to_f
// => 575.0
the value coming in is in this format and i need to insert into a database field that is decimal any suggestions
"$575.00 "
We did this so often we wrote an extension to String called cost_to_f:
class String
def cost_to_f
self.delete('$,').to_f
end
end
We store such extensions in config/initializers/extensions/string.rb.
You can then simply call:
"$5,425.55".cost_to_f #=> 5425.55
If you are using this method rarely, the best bet is to simply create a function, since adding functions to core classes is not exactly something I would recommend lightly:
def cost_to_f(string)
string.delete('$,').to_f
end
If you need it in more than one class, you can always put it in a module, then include that module wherever you need it.
One more tidbit. You mentioned that you need to process this string when it is being written to the database. With ActiveRecord, the best way to do this is:
class Item < ActiveRecord::Base
def price=(p)
p = p.cost_to_f if p.is_a?(String)
write_attribute(:price, p)
end
end
EDIT: Updated to use String#delete!
So many answers... i'll try to summarize all that are available now, before give own answer.
1. string.gsub(/[\$,]/, '')
string.gsub!(/^\$/, '')
2. string[1..-1]
3. string.slice(0) # => "ome string"
4. s/^.//
Why (g)sub and regexp Just for deleting a character? String#tr is faster and shorter. String#delete is even better.
Good, fast, simple. Power of reverse indexing.
Hm... looks like it returns "S". Because it is an alias to String#[]
Perl? /me is cheking question tags...
And my advice is:
What if you have not dollar, but yena? Or what if you don't even have anything before numbers?
So i'll prefer:
string[/\d.+/]
This will crop leading non-decimal symbols, that prevent to_f to work well.
P.S.: By the way. It's known, that float is bad practice for storing money amounts.
Use Float or Decimal for Accounting Application Dollar Amount?
You could try something like this.
string = string[1..-1] if string.match(/^\$/)
Or this.
string.gsub!(/^\$/, '')
Remember to put that backslash in your Regexp, it also means "end of string."
you can use regex for that:
s/^.//
As laways, this is PCRE syntax.
In Ruby, you can use the sub() method of the string class to replace the string:
result = string.sub(/^./,"")
This should work.
[EDIT]
Ok, someone asked what's the gsub() is for:
gsub() acts like sub() but with the /g modifier in PCRE (for global replacement):
s/a/b/
in PCRE is
string.sub(/a/, "b")
and
s/a/b/g
is
string.gsub(/a/, "b")
in Ruby
What I'd use (instead of regular expressions) is simply the built-in slice! method in the String class. For example,
s = "Some string"
s.slice!(0) # Deletes and returns the 0th character from the string.
s # => "ome string"
Documentation here.

case insensitive search in rails - an example from Beginning Rails

Could some on help me to turn the following search into case - insensitive?
Here is the piece of code for earching "title" field in event module:
# Add each field to the conditions array
searchable_fields.each_pair do |field, value|
conditions << "#{field} LIKE ?"
values << "%#{value}%"
end
Here is the data I have entered:
Concert
■Posted by: bancova
■2010-03-14
■boston
test
the "Concert" is the title of this event.
now, when I entered "concert" (small c), I cannot get the event.
however, when I entered "Concert", or "oncert", or "cert"...I can get it.
Could some some friend explain the code and teach me how to make it case insensive?
thanks.
I'm unfamiliar with the tutorial you're using but it looks like it's a database problem, not a Ruby/Rails problem. The problem is that your database is case sensitive so 'Concert' matches because that's what's in the DB, but 'concert' doesn't because it's not an actual match with 'Concert'.
Anyway, the actual solution will depend on your database and how it's configured but lets assume it's MySQL, then your solution would look like this
searchable_fields.each_pair do |field, value|
conditions << "#{field} LIKE LOWER(?)"
values << "%#{value.downcase}%"
end
value.downcase will change the input string to all lowercase and the LOWER sql function will do the same on the database side. They should now match. IF you're using SqlLite or Postgres you'll need to look up their lowercase functions but the rest will still be the same.

Resources