我需要一些帮助来实现预加载

I need some help implementing eager loading

最近有人告诉我急切加载及其提高性能的必要性。我已经设法从加载此页面中减少了一些查询,但我怀疑如果我可以正确地预先加载所需的记录,我可以 trim 显着减少它们。

此控制器需要加载以下所有内容以填充视图:

控制器:

def student_view
    @student = Student.includes(:objective_students).find(params[:student])
    @seminar = Seminar.includes(:objective_seminars).find(params[:id])
    @oss = @seminar.objective_seminars.includes(:objective).order(:priority)
    @objectives = @seminar.objectives.order(:name)
    objective_ids = @objectives.map(&:id)
    @student_scores = @student.objective_students.where(:objective_id => objective_ids)
    @ss = @student.seminar_students.find_by(:seminar => @seminar)
    @teacher = @seminar.user

    @teach_options = teach_options(@student, @seminar, 5)
    @learn_options = learn_options(@student, @seminar, 5)
end

下面的方法是发生大量重复查询的地方,我认为应该通过预先加载来消除这些查询。此方法为学生提供了六个选项,因此她可以选择一个 objective 来教她的 class 同伴。该方法首先查看学生得分在 75% 到 99% 之间的 objectives。在那个括号内,它们也按 "priority" 排序(从 objective_seminars 加入 table。这个值由老师设置。)如果还有更多空间,则该方法查看objective 学生得分为 100%,按优先级排序。 (learn_options 方法实际上与此方法相同,但括号编号不同。)

teach_options方法:

def teach_options(student, seminar, list_limit)
        teach_opt_array = []
        [[70,99],[100,100]].each do |n|
            @oss.each do |os|
                obj = os.objective
                this_score = @student_scores.find_by(:objective => obj)
                if this_score
                    this_points = this_score.points
                    teach_opt_array.push(obj) if (this_points >= n[0] && this_points <= n[1])
                end
            end
            break if teach_opt_array.length > list_limit
        end
        return teach_opt_array
    end

提前感谢您的任何见解!

@jeff - 关于您的问题,我看不出 @student_scores.find_by(:objective => obj) 以外的地方会有很多查询。 您的 @student_scores 对象已经是一个 ActiveRecord 关系,对吗?因此,您可以在此使用 .where().select{} 而无需再次访问数据库。 Select 会给你留下一个数组,而不是一个 AR 关系,所以要小心。

this_score = @student_scores.where(objectve: obj)
this_score = @student_scores.select{|score| score.objective == obj}

这些应该有用。

关于你的顶级控制器方法的一些其他建议 - 我没有看到任何守卫或防御编码,所以如果这些对象中的任何一个为零,你的 .order(:blah) 可能会出错。此外,如果它们 return nil,则依赖于它们数据的后续查询可能会出错。我会选择一些 try()s 或救援。

最后,只是吹毛求疵,但前两行有点难以阅读,因为您可能会错误地将参数解释为应用于包含以及主要对象:

@student = Student.includes(:objective_students).find(params[:student])
@seminar = Seminar.includes(:objective_seminars).find(params[:id])

我会将查找与您的主要对象放在一起,然后是包含:

@student = Student.find(params[:student]).includes(:objective_students)
@seminar = Seminar.find(params[:id]).includes(:objective_seminars)