在Rails中,如何计算集合中关联记录的数量?

时间:2013-12-18 09:21:33

标签: ruby-on-rails ruby ruby-on-rails-4

我有一个名为Awardunit的模型。 Awardunit有许多Awardleaders。一个奖项单位可以有一个或多个奖项领导者。

如果我获取所有记录或搜索并获取一个名为awardunits的变量的记录集合,我如何计算此集合中所有单位中Awardleaders的数量?

这就是我的所作所为:

 @leaders = 0
 @awardunits.each do |unit|
      @leaders = @leaders + unit.awardleaders.size
 end

再次计算残疾领导者,我用这个:

  @disabledleaders = 0
  @awardunits.each do |unit|
     @disabledleaders = @disabledleaders + unit.awardleaders.where(disabled: true).size
  end

如果我使用它,每次页面加载时都必须经过所有记录。有没有更好的方法呢?

3 个答案:

答案 0 :(得分:8)

您可以通过添加计数器缓存来便宜地计算关联:

class AwardUnit << ActiveRecord::Base
  has_many :award_units
end

class AwardLeader << ActiveRecord::Base
  belongs_to :award_unit, counter_cache: true
end

现在在新迁移中向award_leaders_count表添加一个名为AwardUnit的新列:

  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现在会自动缓存每个AwardUnit的award_leaders数量,@my_award_unit.award_leaders.count将为您提供计数,而无需运行其他数据库查询。

默认情况下,Rails counter_cache仅适用于所有award_leaders。如果您只需要计算那些有条件的award_leader,您将需要添加自己的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

迁移:

  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

现在,当你有一组AwardUnit时,获得残疾奖励领导人的总数就像

一样简单
@award_units = AwardUnit.limit(5).to_a # or a similar query
@award_units.inject(0){|sum,unit| sum + unit.disabled_award_leaders_count }

答案 1 :(得分:2)

您可以在抓取Awardleaders时急切地加载Awardunit,这样您就不必为每个Awardunit执行计数查询,如下所示:

@awardunits = Awardunit.includes(:awardleaders).where('awardleaders.disabled = ?', true) # the rest of the query

或者,您可以直接查询计数:

@leaders = Awardleader.where('awardunit_id IN (?)', @awardunits.map(&:id)).where(:disabled => true).count

答案 2 :(得分:1)

这将只运行一次数据库查询

Awardunit.includes(:awardleaders).map {|award_unit| award_unit.awardleaders.size}.inject(0){|sum,item| sum + item }