Active Record Query 'where' to ignore spaces - ruby-on-rails

How do I get all the records from db that match given string without spaces?
For example:
db record: "Hello beautiful world!"
given string: name = "Hello beau tifulworld !"
Is it possible to make it as Active Record Query?

You can search by name without spaces. Something like this (for postgres):
Model.where("replace(name, ' ', '') = replace(?, ' ', '')", 'Hello beau tifulworld !')

What comes to my mind is that you can search the records by sorted names from both sides.
I mean, first in a new created array with the names and ids of the records you sort the record names, by something like
record.name.chars.sort.join and you also sort your
matching_word = "Hello beautiful world!"
to get !Habdeefilllloortuuw"
Then you can get the ids of the records that match this helper name.
In next step you can get these records from the database.
Good luck!

Related

Rails query to substitute field and query

could someone please help me to write a query to substitute hyphens in rails DB field with space?
For eg:
If I have a field called 'name' in a table User having a value 'asdc-sd bc', and want to remove special characters like '-' and replace it with space to match with a given name 'asdc sd bc'.
I tried using lower to convert the name to lowercase but am not able to find out how to substitute the hyphens with space. Please help!
You can use SQL REPLACE function to replace - with spaces(' ').
For example, if you are querying on name column of User model, you can write your query like below:
query_str = 'asdc sd bc'
User.where("REPLACE(name, '-', ' ') = ?", query_str)

Query in a string column for one of the value in an array like multiple OR (using full text search)

In a rails 4 app, in one model I have a column containing multiple ids as a string with comma separated values.
"123,4568,12"
I have a "search" engine that I use to retrieve the records with one or many values using the full text search of postgresql I can do something like this which is very useful:
records = MyModel.where("my_models.col_name ## ?", ["12","234"])
This return all the records that have both 12 and 234 in the targeted column. The array comes from a form with a multiple select.
Now I'm trying to make a query that will find all the records that have either 12 or 234 in there string.
I was hopping to be able to do something like:
records = MyModel.where("my_models.col_name IN (?)", ["12","234"])
But it's not working.
Should I iterate through all the values in the array to build a query with multiple OR ? Is there something more appropriate to do this?
EDIT / TL;DR
#BoraMa answer is a good way to achieve this.
To find all the records containing one or more ids referenced in the request use:
records = MyModel.where("my_models.col_name ## to_tsquery(?)", ["12","234"].join('|'))
You need the to_tsquery(?) and the join with a single pipe |to do a OR like query.
To find all the records containing exactly all the ids in the query use:
records = MyModel.where("my_models.col_name ## ?", ["12","234"])
And of course replace ["12","234"] with something like params[:params_from_my_form]
Postgres documentation for full text search
If you already started to use the fulltext search in Postgres in the first place,I'd try to leverage it again. I think you can use a fulltext OR query which can be constructed like this:
records = MyModel.where("my_models.col_name ## to_tsquery(?)", ["12","234"].join(" | "));
This uses the | operator for ORing fulltext queries in Postgres. I have not tested this and maybe you'll need to do to_tsvector('my_models.col_name') for this to work.
See the documentation for more info.
Suppose your ids are :
a="1,2,3,4"
You can simply use:
ModelName.find(a)
This will give you all the record of that model whose id is present in a.
I just think a super simple solution, we just sort the ids in saving callback of MyModel, then the query must be easier:
class MyModel < ActiveRecord::Base
before_save :sort_ids_in_col_name, if: :col_name_changed?
private
def sort_ids_in_col_name
self.col_name = self.col_name.to_s.split(',').sort.join(',')
end
end
Then the query will be easy:
ids = ["12","234"]
records = MyModel.where(col_name: ids.sort.join(',')

Ruby On Rails Dynamic Where Search

I am using postgres for the db.
Service Address Controller contains this line
#service_addresses = ServiceAddress.where("customer_id =?" , params[:customer_id]).search(params[:search])
Service Address Model Method:
def self.search(query)
where("street LIKE ? OR city LIKE ?", "%#{query.to_s}%","%{query.to_s}%")
end
In my view I have a search bar that sends params
service_addresses?utf8=✓&search=123+Echo+Dr+New+York
Lets assume I have two columns on my view Street & City
If I search 123 Echo Dr. New York it will NOT return a record where Street = 123 Echo Dr. and the City = New York
However, if I simply search 123 Echo Dr. or 123 it will return all records that have that in the Street or City column. Similairly if I search New York it will return records that contain New York
I tried parsing out the search param into an array (successfully) and then using a loop to essentially build a string of "%#{parsed_query[i]}%" However when I tried to pass the string of binds to my where statement, but I got an error stating I have the wrong number of binds. It was treating my string variable as one bind.
Not able to use Data Tables gem!
Thank you SOF community.
Iterate over the tokens parsed from the query string to build up your query with ActiveRecord. Note that this will result in a SQL query many AND conditions.
#service_addresses = ServiceAddress.where("customer_id =?" , params[:customer_id])
parsed_query.each do |token|
#service_addresses = #service_addresses.search(token)
end
Update for case-insensitivity
To ignore case you can convert all strings to either uppercase or lowercase.
# Service Address Model Method:
def self.search(query)
where("upper(street) LIKE ? OR upper(city) LIKE ?", "%#{query.to_s.upcase}%","%{query.to_s.upcase}%")
end
Alternative
You may wish to look into document matching (full-text search). Postgres has some nice support for this. The topic is too much to cover here, but there are many resources online. A good place to start is the Postgres documentation
If I understand you problem, try this:
def self.search(keyword)
keyword_search = "%#{keyword.downcase}%"
where('lower(city) LIKE :search OR lower(street) LIKE :search', search: keyword_search)
end

Order by last name

I am trying to set the default scope for my User model. Each user has one name column. The problem is that I would like to order users by the first letter of their last name. This would be the start of the last word from the name. For example, a users name may be "Kevin John Smith", I would like to order by Smith. I currently have default_scope order('name ASC'), but this sorts by the first letter of the first name. How would I convert this to sort by the first letter of the last name? Thanks!
Try this:
User.select("users.*, SUBSTRING_INDEX(users.name, ' ', -1) AS lastname").limit(10).order('lastname ASC')
SUBSTRING_INDEX is one of the mySQL string functions.
For PostgreSQL split_part should work:
User.select("users.*, split_part(users.name, ' ', 3) AS lastname").limit(10).order('lastname ASC')
I am not sure, though try with -1 too:
User.select("users.*, split_part(users.name, ' ', -1) AS lastname").limit(10).order('lastname ASC')
Because, the latter one will ensure that the last string after split is used which will cover the cases where user has just first name and last name.
Make sure you use unscoped, which returns a scope for this class without taking into account the default_scope.

String array in database. match and return values

I have the following data in a column letters in a mysql database. I saved them in varchar:
letters
["a","b"]
["a","b","d"]
["a","d"]
["d","c","e"]
["e","c","f"]
["c","f"]
["f","e"]
I am trying to match some elements. When I have params[:lttrs] as"a", I want to return:
["a","b"]
["a","b","d"]
["a","d"]
When I have params[:lttrs] as "c,e", I want to return:
["d","c","e"]
["e","c","f"]
My attempt is to retrieve all the rows and then match each of them with include?('a'), but with that, I can only do one element at a time. Is that the approach?
You can use the LIKE operator with wild card matching. This solution will be faster than using ruby. But it will be slow if you have a large table. If you provide more details about the reason for your data design we will be able to suggest alternative approaches.
like_params = params[:lttrs].split(",").map{|letter| "%#{letter}%"}
like = like_params.map{ "LIKE ?"}.join(" OR ")
Model.where(like, like_params)
You could try doing something like:
query = "letters "
r = 1
letters_arr = params[:lttrs].split(",")
letters_arr.each do |l|
if r == 1:
query << " like '%#{l}%'"
else
query << " or like '%#{l}%'"
end
end
letters_found = WhateverModel.where(query)
Obviously you need to make sure that the params are handled more safely than that, but that should get you on your way.

Resources