Multiple word search in one field - ruby-on-rails

I have search functionality with this code in the 'search.rb' file:
votsphonebooks = votsphonebooks.where("address like ?", "%#{address}%") if address.present?
There are multiple fields, this is just one of them.
How can I successfully change this line into something like a map to include multiple words.
Eg. If they type in '123 Fake St' - it will look for exactly that, but I want it to search for '123', 'Fake', 'St'.

First thing you should do is split the address by spaces:
addresses = params[:address].split(" ")
Then what you need is a OR query, you could do it by using ARel.
t = VotsPhoneBook.arel_table # The class name is my guess
arel_query = addresses.reduce(nil) do |q, address|
q.nil? ? t[:address].matches("%#{address}%") : q.or(t[:address].matches("%#{address}%"))
end
results = Post.where(arel_query)

Try using REGEXP instead of LIKE:
address_arr = address.split(" ")
votsphonebooks = votsphonebooks.where('address REGEXP ?',address_arr.join('|')) unless address_arr.blank?

Related

.where.not with empty array

I have a problem trying to work with a NOT IN query (using Rails 4/Postgres, for reference) in an elegant way. I'm trying to get a list of all objects of a certain model that don't show up in a join table for a certain instance. It works , when you try a NOT IN query with an empty array, it throws an error because you can't look for NOT IN NULL.
The below code now works, but is there a better way than to use an unintuitive conditional to make a pseudo-null object?
def characters_selected
self.characters_tagged.pluck(:name)
end
def remaining_characters
characters = self.characters_selected
characters = ["SQL breaks if this is null"] if characters.empty?
# this query breaks on characters_selected == [] without the above line
Character.where("name NOT IN (?)", characters )
end
This is the ActiveRecord way:
def remaining_characters
characters = self.characters_selected
Character.where.not(:name => characters)
end
When characters.empty? the where clause becomes "WHERE (1=1)".

`Case/when` statement works some of the time

Ok I am creating a gem that is supposed to find tags #, #, or $ in user's posts. I am using a case when statement and it seems to work, only sometimes. For example I will have a string like #you and that works, but #cool does not work unless I add #cool #you. It seems the other when statements only work if the first when statement is true. The REGEX is just so it knows what to look for and I know those do work.
REGEXS = [Supertag::Tag::USERTAG_REGEX, Supertag::Tag::HASHTAG_REGEX, Supertag::Tag::MONEYTAG_REGEX]
def linkify_tags(taggable_content)
text = taggable_content.to_s
REGEXS.each do
case text
when text = text.gsub(Supertag::Tag::USERTAG_REGEX) {link_to($&, usertag_path($2), class: 'tag')}
when text = text.gsub(Supertag::Tag::HASHTAG_REGEX) {link_to($&, hashtag_path($2), class: 'tag')}
when text = text.gsub(Supertag::Tag::MONEYTAG_REGEX) {link_to($&, moneytag_path($2), class: 'tag')}
end
end
text.html_safe
end
For some reason you iterate over REGEXS, ignore the item in the iteration, then hard-code them again... you actually do text.gsub(Supertag::Tag::USERTAG_REGEX) ... 3 times - once for each REGEX in your list.
Also, you misuse the case when construct, I suggest you read more about it
You should either drop the each entirely, and use only the explicit constants, or refactor you code to work dynamically, maybe something like:
REGEXS = [[Supertag::Tag::USERTAG_REGEX, :usertag_path],
[Supertag::Tag::HASHTAG_REGEX, :hashtag_path],
[Supertag::Tag::MONEYTAG_REGEX, :moneytag_path]]
def linkify_tags(taggable_content)
text = taggable_content.to_s
REGEXS.each do |regex, path|
text = text.gsub(regex) {link_to($&, send(path, $2), class: 'tag')}
end
text.html_safe
end

How to find a keyword in a string

Users send in smses which must include a keyword. This keyword is then used to find a business.
The instruction is to use the keyword at the start of the sentence.
I know some users won't use the keyword at the beginning or will add tags (# # -) or punctuation (keyword.) to the keyword.
What is an efficient way to look for this keyword and for the business?
My attempt:
scrubbed_message = msg.gsub("\"", "").gsub("\'", "").gsub("#", "").gsub("-", "").gsub(",", "").gsub(".", "").gsub("#", "").split.join(" ")
tag = scrubbed_msg.split[0]
if #business = Business.where(tag: tag).first
log_message(#business)
else
scrubbed_msg.split.each do |w|
if #business = Business.where(tag: w).first
log_message(#business)
end
end
end
Instead of which characters you want to remove from the string, I suggest to use a whitelist approach specifying which characters you want to keep, for example alphanumeric characters:
sms = "#keyword and the rest"
clean_sms = sms.scan(/[\p{Alnum}]+/)
# => ["keyword", "and", "the", "rest"]
And then, if I got right what you are trying to do, to find the business you are looking for you could do something like this:
first_existing_tag = clean_sms.find do |tag|
Business.exists?(tag: tag)
end
#business = Business.where(tag: first_existing_tag).first
log_message(#business)
You can use Regexp match to filter all unnecessary characters out of the String, then use #reduce method on the Array git from splitted string to get the first occurience of a record with tag field matched to a keyword, in the exmaple: keyword, tag1, tag2:
msg = "key.w,ord tag-1'\n\"tag2"
# => "key.w,ord tag-1'\n\"tag2"
scrubbed = msg.gsub(/[#'"\-\.,#]/, "").split
# => ["keyword", "tag1", "tag2"]
#business = scrubbed.reduce(nil) do| sum, tag |
sum || Business.where(tag: tag).first
end
# => Record tag: keyword
# => Record tag: tag1 if on record with keyword found

Case insensitive and inclusive ActiveRecord queries?

I have views that print out a table of guides that have the word 'farming' in their titles.
def farming
t = Guide.arel_table
#guides = Guide.where(t[:title].matches('%farming'))
end
The problem is I want to show all guides that have at least any kind of spelling of the word 'farming' in it. So "Farming for Dummies" should show up in the search, too.
How do I do this?
In order to match Farming for Dummies you need to use farming%, i.e. % should be after the word farming.
Try the following to return all records with title including the word farming anywhere in title:
def farming
t = Guide.arel_table
#guides = Guide.where(t[:title].matches('%farming%'))
end

How to include a custom SQL function

I'm using find_by_sql to execute an SQL query.
I would like to be able to use Soundex and Levenshtein, but in order to use Levenshtein I need to include the function as a file.
This is my code so far:
info = params[:email].split('#')
name = info[0]
domain = info[1]
levenshtein = File.open("./lib/assets/mysql-function-levenshtein.sql")
results = Domain.find_by_sql(
"" + levenshtein + "
SELECT *
FROM domains
WHERE domain = '" + domain + "'"
)
I have no idea if simply including it in the query is even valid.
What's the best implementation?
By the way the file I'm trying to include is this:
https://github.com/vyper/levenshtein-sql
First off, I think you'd be better off just defining that function in your database with a migration, so you wouldn't have to define it again for every query where you want to use it:
class AddLevenShteinFunctionToDatabase < ActiveRecord::Migration
  def up
levenshtein = File.read("/path/to/levenshtein.sql")
    execute levenshtein
  end
def down
# maybe put some code here to delete the function
end
end
With this done, you could also add a scope to your Domain model for doing these kind of queries:
scope :levenshtein, lambda {|s1, s2| select("levenshein(#{s1}, #{s2})") }
With this, you should be able to write your queries something like this:
results = Domain.levenshtein("LEONARDO", "LEONARDU").where(:domain => domain)

Resources