Rails Eager为现有对象加载has_many关联

时间:2017-01-18 11:49:52

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

我对rails& amp;我遇到了这个性能问题,我将不胜感激。

我有一个用户模型&每个用户has_many UserScores关联。我正在准备一个显示不同用户统计信息的仪表板,包括基于特定条件的user_scores计数。以下是代码片段:

def dashboard
@users = Array.new
users = User.order('created_at ASC')
users.each do |u|
    user = {}
    user[:id] = u.id
    user[:name] = u.nickname
    user[:email] = u.email
    user[:matches] = u.user_scores.count
    user[:jokers_used] = u.user_scores.where(:joker => true).length
    user[:jokers] = u.joker
    user[:bonus] = u.user_scores.where(:bonus => 1).length
  user[:joined] = u.created_at.strftime("%y/%m/%d")
    if user[:matches] > 0
        user[:last_activity] = u.user_scores.order('updated_at DESC').first.updated_at.strftime("%y/%m/%d")
    else
        user[:last_activity] = u.updated_at.strftime("%y/%m/%d")
    end
    @users << user
end
@user_count = @users.count

我看到的问题是对每个用户重复UserScore db查询以获取不同的计数。

有没有办法避免那些多次查询?

N.B。我不确定我为视图准备数据的方法是否是最佳方式,因此任何有关该方法的建议或提示都将受到高度赞赏。

由于

3 个答案:

答案 0 :(得分:1)

使用include方法来急切加载你有很多关联。您可以在此处理解此概念:https://www.youtube.com/watch?v=s2EPVMqOsTQ

答案 1 :(得分:1)

您需要急切加载users_scores以减少多个查询。 @ Slava.K就如何消除它提供了很好的解释。 添加includes(:user_scores)用于查询用户,并使用ruby的方法在从DB通过查询获取数据后使用集合。

请参阅以下代码以了解:

users = User.includes(:user_scores).order('created_at ASC')

users.each do |u|
  ....

  user[:matches] = u.user_scores.length
  user[:jokers_used] = u.user_scopes.select{ |score|  score.joker == true }.length
  user[:jokers] = u.joker
  user[:bonus] = u.user_scores.select{ |score|  score.bonus == 1 }.length
  ....
end

此外,您准备回应的方式并不干净灵活。相反,您应该重写as_json方法来准备json,它可以正确地使用视图。默认情况下为模型定义as_json方法。您可以从官方文档http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html中了解更多相关信息,或访问article on preparing clean json response,其中我解释了正确深入覆盖as_json。

答案 2 :(得分:0)

首先,在您的查询中引用user_scores关联:

users = User.includes(:user_scores).order('created_at ASC')

跟踪铁路文档协会渴望加载:http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

另请注意,即使您的关联已经预加载,where也会对数据库进行新查询。因此,而不是

u.user_scores.where(:joker => true).length
u.user_scores.where(:bonus => 1).length

尝试:

u.user_scores.count { |us| us.joker }
u.user_scores.count { |us| us.bonus == 1 }

您可能还必须以某种方式重写.user_scores.order('updated_at DESC').first.updated_at.strftime("%y/%m/%d")