Rails - eagerly loads the number of related posts, but not the posts themselves
I have a page that requires an age to render. Half the time (3 seconds) is spent calling .find, which has a bunch of associations loaded. All I really need is the number of related records in each case displayed in the table: I myself don't need the records themselves. Is there a way to just upload an invoice? Here's a simplified example:
@subjects = Subject.find(:all, :include => [:questions])
In my table, for each row (i.e., each object), I simply show the values โโof the topic fields and the number of related questions for each object. Can I optimize the above search call according to these requirements?
I was thinking about using a group box, but my full call involved several different associations with some second order associations, so I don't think the group will work.
@subjects = Subject.find(:all, :include => [{:questions => :tags}, {:quizzes => :tags}], :order => "subjects.name")
: tags in this case are second-order associations via tags. Here are my associations in case it is not clear what is happening.
Subject
has_many :questions
has_many :quizzes
Question
belongs_to :subject
has_many :taggings
has_many :tags, :through => :taggings
Quiz
belongs_to :subject
has_many :taggings
has_many :tags, :through => :taggings
Grateful for any advice - max
a source to share
I find it best used :counter_cache
in association belongs_to
.
class Subject < ActiveRecord::Base
has_many :questions
end
class Question < ActiveRecord::Base
belongs_to :subject, :counter_cache => true
end
To use counter_cache
, you also need to add a column questions_count
to the table subjects
.
From railsapi.com
:
: counter_cache
Caches the number of owned objects in the associated class using of increment_counter and decrement_counter. The cache counter is incremented when an object of this class is created and decremented when its destroyed [...]
a source to share
You can use a cache counter for this purpose. I maintain a counter in the album model that keeps track of how many photos are associated with it. My process looks something like this (I believe I found this method on a blog somewhere, so I'm not taking responsibility for the source code):
In Model Pictures:
after_save :update_counter_caches
after_destroy :update_counter_caches
def update_counter_caches
self.albums.each { |a| a.update_count } unless self.albums.empty?
end
In the album model:
def update_count
update_attribute(:photographs_count, self.photographs.length)
end
The migration you need for albums:
class AddCounterCacheColumnToModels < ActiveRecord::Migration
def self.up
add_column :albums, :photographs_count, :integer, :default => 0
end
def self.down
remove_column :albums, :photographs_count
end
end
If I didn't understand your question, this should be a pretty neat way to achieve what you want. It works well in my current project. :)
EDIT: As a side note, the reason I am using this setting and not the default: counter_cache method is because I need to support multiple counters for multiple associations on the same model. As far as I know, you cannot achieve this with: counter_cache.
a source to share
The fastest way I think is:
@subjects = Subject.count(:joins => :questions, :select => 'DISTINCT(questions.id)')
And I'm not sure about a more complex query (not tested):
@subjects = Subject.count(:joins => [{:questions => :tags}, {:quizzes => :tags}], :select => 'DISTINCT(tags.id)'
a source to share