Need help optimizing some Rails 2.3 ActiveRecord code - ruby-on-rails

(Hi Dr. Nick!)
I'm trying to tighten things up for our app admin, and in a few places we have some pretty skeezy code.
For example, we have Markets, which contain Deals. In several places, we do something like this:
#markets = Market.find(:all, :select => ['name, id'])
#deals = Deal.find(:all, :select => ['subject, discount_price, start_time, end_time'], :conditions => ['start_time >= ? AND end_time <= ?', date1 date2])
Then in the corresponding view, we do something like this:
#markets.each do |m|
=m.name
end
#deals.sort!{ |a,b| a.market.name <=> b.market.name }
#deals.each do |d|
=d.subject
=d.market.name
end
This runs a stupid amount of queries: one to get the market names and ids, then another to get all the deal info, and then for each deal (of which there are thousands), we run yet another query to retrieve the market name, which we already have!
Tell me there is a way to get everything I need with just one query, since it's all related anyway, or at least to clean this up so it's not such a nightmare.
Thanks

You can write like this way ..
#deals_with_market_name = Deal.find(:all, :include => :market,
:select => ['subject, discount_price, start_time, end_time,market.name as market_name'],
:conditions => ['start_time >= ? AND end_time <= ?', date1 date2],
:order => "market.name")
And in view ...
#deals.each do |a|
=a.subject
=a.market_name
end
Try it...

If you use :include => :market when searching the deals you won't run a query to retrieve the market name for each deal. It'll be eager loaded.
#deals = Deal.find(:all, :include => :market)
Hope it helps.

Related

Rails 3.1 - quick rewrite on deprecated query needed

I have this:
Product.find(:all, :conditions => ['release_date >=? AND release_date <=?', #start, #start + #weeks.weeks], :order => "initial_stock DESC")
I understand conditions is now deprecated. This works fine locally but when I upload to heroku the order doesn't work, so probably best I rewite right? Problem is each thing i've tried throws an error. Can anyone help?
Thanks!
This should do it:
Product.where("release_date >= ? AND release_date <= ?", #start, #start + #weeks.weeks).order("initial_stock DESC")
If this is used across the app I generally like to create a scope on the model for it. I haven't tested this code but here's what I would do:
# scope on Product.rb
scope :by_release_date, lambda { |date| where("release_date BETWEEN ? AND ?", date.beginning_of_day, date.end_of_day) }
# query anywhere in app
Product.by_release_date('2012-06-11 00:00:00').order('initial_stock DESC')

Find and display nearest date in RoR

I am new to ruby on rails and I'm not sure where to start with this. I have a model for users, and one for projects. Users have many projects, and projects have one user. There is an end_date column in the projects table (as well as a name column).
What I want to do is find the project with the nearest end_date and display it's name and end date on the user's show page.
I tried putting this code in the projects controller, but I do not know if it is working, because I don't know how to access it and display the project name in the view.
def next_deadline(after = DateTime.now, limit = 1)
find(:all, :conditions => ['end_date > ?', after], :limit => limit)
end
Any help would be appreciated. Let me know if more information is needed.
As #Dan mentioned, you do need the :order clause to get the first one, but you should add it to your query and not replace the :conditions (otherwise you'll get the project with the earliest end_date irrespective of your after argument). The way you're defining this method is a bit off though. It should be defined in your Project model (and definitely not the controller) as a class method, or, what I think is a better approach, as a scope. In Rails < 3 (which it seems that you're using):
class Project < ActiveRecord::Base
named_scope :next_deadline, Proc.new { |after = DateTime.now, limit = 1| {:conditions => ['end_date > ?', after], :order => "end_date ASC", :limit => limit} }
...
end
Or in Rails >= 3:
class Project < ActiveRecord::Base
scope :next_deadline, Proc.new { |after = DateTime.now, limit = 1| where('end_date > ?', after).order("end_date ASC").limit(limit) }
...
end
Also, you can always test this kind of code using the Rails console: script/console in Rails < 3, rails c in Rails >= 3.
#projects = Project.find_by_sql("SELECT projects.* FROM projects
JOIN users ON users.id = projects.user_id AND projects.user_id = " + #user.id.to_s + "
WHERE projects.end_date > now()
ORDER BY projects.end_date ASC
LIMIT " + limit)
or
#projects = Project.where(:user_id => #user.id)
.where("end_date > ?", DateTime.now)
.order("end_date ASC")
You want to use :order, not :conditions.
Model.find(:all , :order => "end_date ASC")
Then the first result will be the item with the closest end_date
As Dan said, the condition you wrote won't get the nearest end date, but the dates that are greater than today, or the date passed in as a parameter.
In your User model you could write
def next_deadline_project
self.projects.first
end
as long as you give projects a default scope that orders records by end_date
In order to show information on the view you must set it in an instance variable in the User's controller show method. Instance variables are passed to views and you can access them to display the data.
#project = next_deadline_project
And in your show.html.erb you can use something like:
<%= #project.name %> - <%= #project.end_date %>

Active Record Query Using Associated Model in Find Clause

I'm having a blonde moment and probably a brain freeze.
In my rails3 app, I have users and tasks. My users have many tasks...
I have due and overdue tasks as follows:
#due = Task.find(:all, :conditions => ["dueddate >= ? AND AND status = ?", Date.today, false], :include => :taskcategories, :order => "dueddate asc")
What I want to do in my tasks view, is list the users with due tasks...
For some reason, I can't get my head around it. I have tried this, but it's not working:
#task = Task.all
#user = User.find(:all, :conditions => ["#task.dueddate <= ? AND
#task.status = ?", Date.today + 7.days, false])
I'm sure this is easy, can anyone help me!!?
I guess this should work
updated
User.joins(:tasks)
.where("tasks.dueddate <= ? AND tasks.status = ?", Date.today + 7.days, false).group(:id)
This should work with SQLite and MySQL. However, PostgreSQL requires that you supply all the columns of the table. If it's a small table, you could simply type the names. Or you could add this method to the model:
def self.column_list
self.column_names.collect { |c| "#{self.to_s.pluralize.downcase}.#{c}"}.join(",")
end
and change .group(:id) to .group(User.column_list)

Elegant Summing/Grouping/Etc in Rails

I have a number of objects which are associated together, and I'd like to layout some dashboards to show them off. For the sake of argument:
Publishing House - has many books
Book - has one author and is from one, and goes through many states
Publishing House Author - Wrote many
books
I'd like to get a dashboard that said:
How many books a publishing house put
out this month?
How many books an
author wrote this month?
What state (in progress, published) each of the books are in?
To start with, I'm thinking some very simple code:
#all_books = Books.find(:all, :joins => [:author, :publishing_house], :select => "books.*, authors.name, publishing_houses.name", :conditions => ["books.created_at > ?", #date])
Then I proceed to go through each of the sub elements I want and total them up into new arrays - like:
#ph_stats = {}
#all_books.map {|book| #ph_stats[book.publishing_house_id] = (#ph_stats[book.publishing_house_id] || 0) + 1 }
This doesn't feel very rails like - thoughts?
I think your best bet is to chain named scopes together so you can do things like:
#books = Books.published.this_month
http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html#M001683
http://m.onkey.org/2010/1/22/active-record-query-interface
You should really be thinking of the SQL required to write such a query, as such, the following queries should work in all databases:
Number of books by publishing house
PublishingHouse.all(:joins => :book, :select => "books.publishing_house_id, publishing_houses.name, count(*) as total", :group => "1,2")
Number of books an author wrote this month
If you are going to move this into a scope - you WILL need to put this in a lambda
Author.all(:joins => :books, :select => "books.author_id, author.name, count(*) as total", :group => "1,2", :conditions => ["books.pub_date between ? and ?", Date.today.beginning_of_month, Date.today.end_of_month])
this is due to the use of Date.today, alternatively - you could use now()::date (postgres specific) and construct dates based on that.
Books of a particular state
Not quite sure this is right wrt your datamodel
Book.all(:joins => :state, :select => "states.name, count(*) as total", :group => "1")
All done through the magic of SQL.

Association Problem

I am having a problem with an association:
Battalion
:has_many soldiers
Soldiers
:has_many primaries
I need to do this
#bseniorleads=(#user.battalion.soldiers.find(:all, :conditions => ["seniorleader = ?", "Yes"]))
then
#seniorspouse=(#bseniorleads.primaries.find(:all, :conditions => ["relationship = ?", "Spouse"]
This gives me an undefined method for primaries, I assume because the bseniorleads is an array?
Basically I don't know how to do this they right way but I need to be able to query a group from one model that meets a condition and then take that result and find the people from another model that belong to them. Any ideas?
Thanks in advance.
You should be able to do something like this (assuming you only needed the #bseniorleads instance variable in the second query):
#senior_spouse = #user.battalion.soldiers.find(
:all,
:select => 'primaries.*',
:joins => [:primaries],
:conditions => ["seniorleader = ? and primaries.relationship = ?", "Yes", "Spouse"]
)
I haven't checked that yet, but I think it should get you pretty close.
You might want to check out these two rails guides, which certainly helped me better understand ActiveRecord associations and querying:
ActiveRecord Querying
ActiveRecord Associations

Resources