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
Related
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
I have such AR code:
#types = Type.find(:all,
:conditions => { :TYP_MOD_ID => params[:models],
:TYP_PCON_START => params[:year] },
:order => "TYP_HP_FROM")
but how can i do that i see TYP_PCON_START without first 2 symbols and compare?
for example :TYP_PCON_START.to_s[4...6]
Also i need to compare not =, but >= > condition. How to do this?
Use where instead of find. Also i didn't understand what you mean by how can i do that i see TYP_PCON_START without first 2 symbols and compare?.
#types = Type.where("TYP_MOD_ID = ? AND substring(TYP_PCON_START FROM 4 to 6) >= ?",
params[:models], params[:year]).order("TYP_HP_FROM")
I am using Rails 3 and I need to do a select where the primary key of the records is IN a resulting previous select. You can do this easily using straight SQL using an IN. Here is the obviously incorrect way I have done what I need. What's the Rails way to do this well:
#task = Link.find(params[:id])
clients = Client.where('task_id = ?',#task.id).select('DISTINCT(company_id)')
company_list = []
clients.each do |client|
company_ids << client.company_id
end
#companies = Company.where(:id => company_ids)
As others have mentioned I'd use join in this case. The syntax for using "in" is also very simple though e.g.
company_ids = [1,2,3,4]
#companies = Company.where("id in (?)", company_ids)
Update
Actually it's even simpler than that now (I think rails 3+), you can do
company_ids = [1,2,3,4]
#companies = Company.where(id: company_ids)
This does not answer your question about "select IN using where clauses", but I think your whole script can be rewritten in one line using joins. This should give the same result as your snippet above:
#companies = Company.joins(:clients).where(:clients => {:task_id => params[:id]})
I believe this will do what you are asking for:
#task = Link.find(params[:id])
#companies = Company.where(:id => Client.where(:task_id => #task.id).select('distinct company_id').map(&:company_id))
You can view the sql by tacking .to_sql on the end in the console.
The join syntax in mischa's answer is probably more readable though.
I think the issue might be that you have company_list and company_ids. I would expect company_ids in the iterator to return something like:
NameError: undefined local variable or method `company_ids'
I think I might write this like:
#task = Link.find(params[:id])
clients = Client.where(:task_id => #task.id).select('distinct company_id')
#companies = Company.where(:id => clients.map(&:company_id))
You can simply use find, as find by id accepts a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]), see: http://apidock.com/rails/ActiveRecord/Base/find/class
#companies = Company.find(company_ids)
I have a class called Deal.
Deal has vote_scores.
I would like to see how many vote_scores are in Deal that are greater than 2.
My guess :
for vote_scores > 2 in Deal
count
end
Doesn't really work :D
Edit:
I tried everyone's ideas. But note that :
Deal.vote_scores
Doesn't work because vote_scores is not an attribute of Deal, but rather an attribute of one of its Deals. So if I did this :
Deal.find(1).vote_scores
would return a #.
vote_scores is instantiated within the haml here:
.deal_summary{:id => "deal_#{deal_view.id}"}
.score
= deal_view.vote_scores
in the model here:
def vote_scores
self.votes.inject(0){|sum, vote| sum + vote.value}
end
If you just want to know how many, the more efficient code will be:
Deal.count(:conditions => ["vote_scores > ?", 2])
This will be faster since the counting is done in sql rather than in ruby.
EDIT
Okay, we can try this:
Deal.find(:all).select {|e| e.vote_scores > 2}.count
This will return total number of deal object that has vote_scores > 2
Hopefully that is what you want to do.
Deal.find(:all, :conditions => ["vote_scores > ?", 2]).length
deal = Deal.first #or whatever... Deal.find(10)
deal.votes.count :conditions => ['value > ?', 2]
for all Votes
Vote.count(:conditions => ['value > ?', 2'])
Say I have model 'Car' and controller 'cars', and a method 'display'.
I have multiple attributes like:
in_production, year, make
I can easily do something like this to find cars that match all the parameters passed:
def display
#cars = Car.find(:all, :conditions => { :in_production => #{params[:in_production]}, :year => #{params[:year]}, :make => #{params[:make]} })`
end
So what I'm doing is coding hard links in the menu, so if I wanted to find all Nissan cars from 2009 that were in production, I would pass those values as parameters in my link.
On another page I want to show every car from 2009 that is in_production, only two params instead of three. What's the best way to dynamically alter the conditions so it will work with one, two, or three params, whilst using the same action?
Any ideas?
First of all, using
:conditions => "in_production = '#{params[:in_production]}' AND year = '#{params[:year]}' AND make = '#{params[:make]}'"
is vulnerable to SQL injection. You need to escape the user provided parameters before using them in database conditions.
Something like this should let you add conditions more dynamically depending on whether or not the parameters exist. I did not test it, so I may edit it shortly...
def display
conditions = []
conditions << [ "in_production = ?", params[:in_production] ] if params[:in_production].present?
conditions << [ "year = ?", params[:year] ] if params[:year].present?
conditions << [ "make = ?", params[:make] ] if params[:make].present?
#cars = Car.all(:conditions => conditions )
end
Certainly escape the params and ensure that you only query against fields you want to be exposed. Beyond that, you could use what is built into Rails:
Car.find_all_by_in_production_and_year_and_make(in_production, year, make)
Hand-rolling the conditions may allow for additional logic to be applied (search by year only if the year is between x and y, etc). Using the rails finders (which in turn use method_missing) keeps the API clean and flexible without having to stare at direct SQL conditions.
You could construct a Car#search method that takes the entire params hash as input, where the params are sanitized and stripped of non-exposed fields, and construct the Car#find_all_by* method call using the param names themselves. Adding new conditions to search by is then as simple as passing them in the params.
You might check out searchlogic. It uses some method missing magic to construct named_scopes that would do what you want.
http://github.com/binarylogic/searchlogic
I use SmartTuple for stuff like this. Simple, powerful, designed specifically for the task.
#cars = Car.all(:conditions => (SmartTuple.new(" AND ") +
({:in_production => params[:in_production]} if params[:in_production].present?) +
({:year => params[:year]} if params[:year].present?) +
({:make => params[:make]} if params[:make].present?)
).compile)
or
#cars = Car.all(:conditions => [SmartTuple.new(" AND "),
({:in_production => params[:in_production]} if params[:in_production].present?),
({:year => params[:year]} if params[:year].present?),
({:make => params[:make]} if params[:make].present?),
].sum.compile)
or
keys = [:in_production, :year, :make]
#cars = Car.all(:conditions => (SmartTuple.new(" AND ").add_each(keys) do |k|
{k => params[k]} if params[k].present?
end).compile)
Pick the one you like the most. :)