I'm trying to build a basic search where only the entire exact search term shows results. Currently it is showing results based on individual words.
Here's the code from the model:
def search
find(:all, :conditions => ['term' == "%#{search}%"])
end
Sorry in advance. I'm very new to rails!
Thank you.
Remove the % from "%#{search}%" so it's "#{search}".
% is a wildcard that matches every result containing the word. So "%tea%" for example would match tear, nestea, and steam, when that's not what you want.
This should yield an exact match:
def search
find(:all, :conditions => ['term' == "#{search}"])
end
Your code doesn't work for several reasons.
You do not pass any value to that method. Therefore search will always be nil.
The ['term' == "%#{search}%"] condition doesn't make much sense because - as I said before - search is undefined and therefore the condition will is the same as ['term' == "%%"]. The string term is not equal to %% therefore the whole condition is basically: [false].
Rails 5.0 uses a different syntax for queries. The syntax you used is very old and doesn't work anymore.
I would do something like this:
# in your model
scope :search, -> (q) {
q.present? ? where("column_name LIKE :query", query: "%#{q}%") :none
}
# in your controller
def set_index
#b = Best.search(params[:search]).order(:cached_weighted_score => :desc)
end
Related
I have this tentative search function, however, it is limited to search one chunk of keyword only.
def self.search(search)
search_condition = "%" + search + "%"
active.where("lower(title) LIKE ?", search_condition.downcase)
end
E.g. I have this title: "Peter Paul Mary"
If I search "peter Mary", it doesn't show.
I found this code useful in this 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
Unfortunately, it's in older rails.
So, how do I translate this into rails 4?
Update:
I've changed to something like this:
def self.search(str)
search = str.split.map{|w| "(lower(title) LIKE ? )"}.join(" OR ")
values = str.split.map{|w| "%#{w.downcase}%"}.map(&:inspect).join(', ')
.where("#{search}", values)
end
But it raises this error:
ActiveRecord::PreparedStatementInvalid (wrong number of bind variables (1 for 2) in: (lower(title) LIKE ? ) OR (lower(title) LIKE ? )):
Please advise.
If you happen to be using Postgres with your app, then you can easily take advantage of PG's Full Text Search capabilities using the pg_search gem.
You can also plug into frameworks like Solr or ElasticSearch to give you this functionality, but they will increase you development effort.
PG and MySQL both also have pattern matching functions that would allow you to search based on a regex string from the search values.
So I have a search box working in my application, however it only returns a result if the search matches exactly what is submitted, as opposed to something like it. Heres my code for the search method;
def self.search(search)
if search
where(:title => ["title LIKE ?", "#{search}"])
else
all
end
end
The "title LIKE ?" doesn't seem to be returning results which are like the query, only ones which are exactly the same.
What am I missing here?
Try this
where(["title LIKE ?", "%#{search}%"])
Here is another way if you want to avoid string queries (using arel):
where(arel_table[:title].matches("%#{search}%"))
I would like my search to allow for multiple inputs. I have a scope in my model:
scope :by_description, lambda { |description| where('description LIKE ?', "%#{description}%") unless description.nil? }
Currently if I search for "abc, efg", it will look for that exact string. How can I modify my scope to allow "abc, efg" to search for any records that have either "abc" OR "efg" in the description field?
Something like this (based on other answers):
scope :by_description, ->(desc=nil) {
if desc.blank?
all
else
terms = desc.split(/\s*,\s*/).map { |t| t.strip }.map { |t| "%#{t}%" }
where( ( ["#{table_name}.description like ?"] * terms.count).join(' or '), *terms )
end
}
Try this:
scope :by_description, lambda { |description|
description.split(",").
map(&:strip).
inject(self) { |memo, term|
memo.where("description like ?", term)
}
unless description.nil?
}
What this does:
takes the incoming string and splits it into an array of comma-separated strings
calls strip on each string to remove leading and trailing spaces
uses inject to chain together a collection of where clauses, one for each search term in the array
I haven't actually tried so it may require some adjustment. But the approach is sound. The key bit is that you need multiple WHERE clauses to get the "and" behavior you're looking for.
This is based off Todd's answer:
scope :by_description, ->(desc=nil) {
if desc.present?
desc.split(",").map(&:strip).inject(self) do |memo, term|
memo.where("#{table_name}.description LIKE ?", "%#{term}%")
end
else
all # or none, if you want no results returned
end
}
Logic wise essentially the same, but I used the stabby syntax, and ensured that the scope would always return something chainable. I also like to be safe and add the table_name to manually built queries, to prevent ambiguous table errors (when 2 or more tables have a description field).
Thanks in advance for your help. I'm following the example I found here (Rails Find when some params will be blank) and trying to put together a bunch of conditions for a search form. This is for a Rails 2.3 legacy application. The below works for me, but I'm not sure how to do anything other than "=". For example, how can I make the programs_offered_category condition be a LIKE statement? I tried doing
majorcategories = params[:majorcategories]
Above the conditions statement and adding
conditions['programs_offered_category LIKE ?', "%#{majorcategories}%"]
but I get "wrong number of arguments (2 for 1)". Also, how can I do greater than and less than signs in this setup? Thanks!
search_controller.rb
conditions = {}
conditions[:city] = params[:city] unless params[:city].blank?
conditions[:state] = params[:state] unless params[:state].blank?
conditions[:geo_region] = params[:geo_region] unless params[:geo_region].blank?
conditions[:size_category] = params[:size_category] unless params[:size_category].blank?
conditions[:programs_offered_category] = params[:majorcategories]
#location_matches = Masterlocation.find(:all, :conditions => conditions, :order => 'nickname ASC').paginate(:page => params[:page], :per_page => 20)
I would suggest to use regular expression as follow
conditions['programs_offered_category'].map {|k,v| (k =~ /majorcategories/) ? v : nil}
It will return array of results if there is more than one matches otherwise single value
What's the best way to include a LIKE clause in a Rails query i.e. something along the lines of (the completely incorrect):
Question.where(:content => 'LIKE %farming%')
You'd use the syntax:
Question.where("content LIKE ?" , "%#{farming}%")
If this is Rails 3 you can use Arel's matches. This has the advantage of being database agnostic. For example:
Question.where(Question.arel_table[:content].matches("%#{string}%"))
This is somewhat clunky, but easily extracted to scopes, e.g.:
class Question
def self.match_scope_condition(col, query)
arel_table[col].matches("%#{query}%")
end
scope :matching, lambda {|*args|
col, opts = args.shift, args.extract_options!
op = opts[:operator] || :or
where args.flatten.map {|query| match_scope_condition(col, query) }.inject(&op)
}
scope :matching_content, lambda {|*query|
matching(:content, *query)
}
end
Question.matching_content('farming', 'dancing') # farming or dancing
Question.matching_content('farming', 'dancing', :operator => :and) # farming and dancing
Question.matching(:other_column, 'farming', 'dancing') # same thing for a different col
Of course to join with "AND" you could just chain the scopes.
Edit: +1 to metawhere and squeel though (haven't tried latter but it looks cool) They both add this type of functionality and much more.
If you want really sexy conditions and and don't have problems with external dependencies, I highly recommend MetaWhere and it's successor Squeel:
# MetaWhere
Question.where(:content.like => '%farming%')
# MetaWhere with operators overloaded
Question.where(:content =~ '%farming%')
# Squeel
Question.where { :content.matches => '%farming%' }
# Squeel with operators overloaded
Question.where { :content =~ '%farming%' }