I have a model named Awardunit
. Awardunit
have many Awardleaders
. One Award unit can have one or many Award leaders.
If I get all the record or search and get a collection of records to a variable named awardunits
how can I count the number of Awardleaders
in all the units in this collection?
Here's what I did :
@leaders = 0
@awardunits.each do |unit|
@leaders = @leaders + unit.awardleaders.size
end
Again to count the disabled leaders I use this :
@disabledleaders = 0
@awardunits.each do |unit|
@disabledleaders = @disabledleaders + unit.awardleaders.where(disabled: true).size
end
If I use this, it will have to go through all the records every time the page loads. Isn't there a better way of doing this?
You can make counting the associations cheap by adding a counter cache:
class AwardUnit << ActiveRecord::Base
has_many :award_units
end
class AwardLeader << ActiveRecord::Base
belongs_to :award_unit, counter_cache: true
end
Now add a new column called award_leaders_count
to your AwardUnit
table in a new migration:
def change
add_column :award_units, :award_leaders_count, :integer, default: 0
AwardUnit.all.each do |unit|
AwardUnit.reset_counters(unit.id, :award_leaders)
end
end
Rails will now automatically cache the number of award_leaders for every AwardUnit
and @my_award_unit.award_leaders.count
will give you the count without running another database query.
By default, Rails counter_cache
only works for all award_leaders. If you need to count only those award_leaders that have a condition, you will have to add your own counter_cache:
class AwardUnit << ActiveRecord::Base
has_many :award_units
end
class AwardLeader << ActiveRecord::Base
belongs_to :award_unit
scope :disabled, -> { where(disabled: true) }
after_save :update_counter_cache
after_destroy :update_counter_cache
def update_counter_cache
award_unit.update_attribute(:disabled_award_units_count, award_unit.award_leaders.disabled.count)
end
end
migration:
def change
add_column :award_units, :disabled_award_leaders_count, :integer, default: 0
AwardUnit.all.each do |unit|
unit.update_attribute(:disabled_award_units_count, unit.award_leaders.disabled.count)
end
end
Now, when you have an array of AwardUnit
s, getting their combined count of disabled award leaders is as simple as
@award_units = AwardUnit.limit(5).to_a # or a similar query
@award_units.inject(0){|sum,unit| sum + unit.disabled_award_leaders_count }
You can eager load Awardleaders
when fetching Awardunit
s, so you don't have to execute a count query for every Awardunit
, like this:
@awardunits = Awardunit.includes(:awardleaders).where('awardleaders.disabled = ?', true) # the rest of the query
Or, you can query the count directly like this:
@leaders = Awardleader.where('awardunit_id IN (?)', @awardunits.map(&:id)).where(:disabled => true).count
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With