query with include, but only in certain condition - ruby-on-rails

Is it possible to make a Model query in rails with an include statement, but to have the "include" only if another condition occurs?
I have this code:
#families = Family.find(:all,
:conditions => family_conditions,
:select => 'families.id,families.name',
:include => families_include_array)
So the include should be there only if the families_include_array is not null.

You should add your query in an if statement
if families_include_array.is_nil?
#families = Family.find(:all,:conditions => family_conditions, :select => 'families.id,families.name')
else
#families = Family.find(:all,:conditions => family_conditions, :select => 'families.id,families.name', :include => families_include_array)
end
Or you can create your SQL request as string and send it to SQL with ActiveRecord::Base.connection.execute
UPDATE
Or you can try to override the find method
def self.find(*args)
records = super
# Manipulate records
end

Related

Rails 2.3.8 Compound condition

I have a rails query that I would like to run. The only problem that I am having is the query structure.
Essentially the query looks like this
queryList = model.find(:all, :conditions => [id = "id"])
#returns a query list
#here is the issue
compound = otherModel.find(:first, :select => "an_id",
:conditions => ["some_other_id=? and an_id=?, some_other_id, an_id])
Where an_id is actually a list of ids in the query list. How can I write that in rails to basically associate a single id to a list that may contain ids...
If you use a hash as conditions, the operation is an AND.
obj = Model.first(:conditions => {:field1 => "val1", :field2 => "val2"})

How to make use of ruby procs to dynamically execute a database query

I have a model called Bucket which has this instance method
Bucket#populate_students_academicwise(mentor)
def populate_students_academicwise(mentor)
student_ids = Mark.find(:all,
:joins => self.student_current_klass,
:conditions => ["marks.subject_id = ? AND st.klass_id = ? AND marks.klass_id = ? AND u.account_enabled IS TRUE AND sub.active IS TRUE AND k.active IS TRUE", mentor.subject_id, mentor.klass_id, mentor.klass_id],
:group => "marks.student_id",
:having => ["ROUND(AVG(marks_obtained)/AVG(marks_total)*100) BETWEEN ? AND ?", min_range, max_range]).collect(&:student_id)
self.assign_students(student_ids)
end
Now, this query returns a set of students whose academic performance is between a range of values
ROUND(AVG(marks_obtained)/AVG(marks_total)*100) BETWEEN ? AND ?)
I call this method from a bucket instance like this bucket.populate_students_academicwise(mentor)
I'd like to negate that query, meaning, return the set of students whose academic performance is NOT between a range of values. All I can think of is to create another method that runs negative of the above query. So I have another method Bucket#negate_populate_students_academicwise(mentor)
def negate_populate_students_academicwise(mentor)
Mark.find(:all,
:joins => self.student_current_klass,
:conditions => ["marks.subject_id = ? AND st.klass_id = ? AND marks.klass_id = ? AND u.account_enabled IS TRUE AND sub.active IS TRUE AND k.active IS TRUE", mentor.subject_id, mentor.klass_id, mentor.klass_id],
:group => "marks.student_id",
:having => ["ROUND(AVG(marks_obtained)/AVG(marks_total)*100) NOT BETWEEN ? AND ?", min_range, max_range]).collect(&:student_id)
end
Now, this query returns a set of students whose academic performance is NOT between a range of values ROUND(AVG(marks_obtained)/AVG(marks_total)*100) NOT BETWEEN ? AND ?
How can I call the first method Bucket#populate_students_academicwise(mentor) with a negate method appended to it bucket.populate_students_academicwise(mentor).negate that would call a proc to negate the query?
So you want to basically have both functionalities and still have them in a DRY way?
I would say you should use a default parameter.
bucket.populate_students_academicwise(mentor) # dont negate
bucket.populate_students_academicwise(mentor,false) # negate
Within you method
def populate_students_academicwise(mentor,in_range = true)
student_ids = Mark.find(:all,
:joins => self.student_current_klass,
:conditions => ["marks.subject_id = ? AND st.klass_id = ? AND marks.klass_id = ? AND u.account_enabled IS TRUE AND sub.active IS TRUE AND k.active IS TRUE", mentor.subject_id, mentor.klass_id, mentor.klass_id],
:group => "marks.student_id",
:having => ["ROUND(AVG(marks_obtained)/AVG(marks_total)*100) #{'NOT' if !in_range} BETWEEN ? AND ?", min_range, max_range]).collect(&:student_id)
self.assign_students(student_ids)
end
With that little bit of query manipulation you get your NOT (or no NOT) depending on the parameter and dont need to worry about proc here.

Rails 2.3.x - lazy db querying

I would like to construct a query in ActiveRecord based on GET params and using named_scope. I thought I'd chain some scopes and add conditions based on GET param availability, but I fear this will overwork the db, sending a new query on each query portion added:
# in model
named_scope :sorted, :order => 'title, author'
named_scope :archived, :conditions => { :is_archived => true }
named_scope :by_date, lambda { |date| { :conditions => [ 'updated_at = ?', date ] } }
# in controller / helper
#articles = Article.sorted.all
#articles = #articles.by_date(params[:date]) if params[:date]
#articles = #articles.archived if params[:archived] == '1'
Another option I've thought of is building a method chaining string that'll then be sent to the object using Object#send, but that seems a bit dirty and somewhat problematic when the named_scope receives arguments (like by_date). I realize I can construct a query string to use with :conditions => ... in ActiveRecord::Base#find, but I thought I'd try first with named_scope to see if it's possible to do lazy querying with the latter. Any suggestions on how to do this using the named_scope and not having the database bombarded by queries? thanks.
You can make lambda more smarter
# in model
named_scope :sorted, :order => 'title, author'
named_scope :archived, lambda { |is_archived| (is_archived == 1) ? {:conditions => {:is_archived => true}} : {}}
named_scope :by_date, lambda { |date| date.nil? ? {} : { :conditions => [ 'updated_at = ?', date ]}}
# in controller / helper
#articles = Article.by_date(params[:date]).archived(params[:archived]).sorted

Conditional "or" in Thinking Sphinx search

Using RoR 2.3.8.
Here's my controller code:
class CitiesController < ApplicationController
def show
#city = City.find(params[:id])
#shops = Shop.search #city.name, {
:conditions => {:country => #city.country && (:city => #city.name || :state => #city.state)},
:page => params[:page],
:per_page => 100
}
end
end
The :conditions => {:country => #city.country && (:city => #city.name || :state => #city.state)} obviously doesn't work because I am just trying to explain what I wanna achieve.
:city and :state will be columns from Spots table, not Cities table. I want results to return either one of them fulfills the condition. But have no clue how to do it.
Thanks.
Tass has got it right - with your TS search call, it should look something like this:
def show
#city = City.find(params[:id])
#shops = Shop.search "#{#city.name} #country #{#city.country} (#city #{#city.name} | #state #{#city.state})",
:match_mode => :extended,
:page => params[:page],
:per_page => 100
}
end
You'll note I've set the match mode - Thinking Sphinx will do this automatically if you're using the :conditions argument - but when constructing the query manually, you need to set it yourself.
Place your raw search to Sphinx - you should find the correct method in the TS docu. A reference for raw search. You probably want something like
"#city_country #{#city.country} (#city_name #{#city.name} | #city_state #{#city.state})"
(I'm not sure how TS names the indexes. Check that.)

Rails - Active Record :conditions overrides :select

I have a fairly large model and I want to retrieve only a select set of fields for each record in order to keep the JSON string I am building small.
Using :select with find works great but my key goal is to use conditional logic with an associated model. Is the only way to do this really with a lamda in a named scope? I'm dreading that perhaps unnecessarily but I'd like to understand if there is a way to make the :select work with a condition.
This works:
#sites = Site.find :all, :select => 'id,foo,bar'
When I try this:
#sites = Site.find :all, :select => 'id,foo,bar', :include => [:relatedmodel],
:conditions => ["relatedmodel.type in (?)", params[:filters]]
The condition works but each record includes all of the Site attributes which makes my JSON string way way too large.
Thanks for any pointers!
The to_json call supports :except and :only options to exclude/include model fields during serialization.
#sites.to_json(:only => [:name, :foo, :bar])
Call above serializes the Site objects with fields name and location.
#sites.to_json(:only => [:name, :location],
:include => { :relatedmodel => {
:only => [:description]
}
}
)
Call above serializes the Site objects with fields name, and location and contained RelatedModel objects with description field.

Resources