我怎么能重构这个?并且减少对服务器的调用?

时间:2017-03-23 12:30:10

标签: ruby-on-rails ruby activerecord refactoring

我有这种方法。它的工作是返回一份已经毕业于开发人员的学员名单。用户过滤他们在开发人员中寻找的技能。该列表将与列表顶部的更相关的开发人员一起返回

这是控制器方法:

  def filter
    @skills = Skill.all
    @developers = []
    unless params[:ids].nil?
      params[:ids].each do |skill|
        skill = @skills.find(skill)
        skill.trainees.each do |developer|
          @developers << developer
        end
      end
    end
    if @developers.empty?
      @developers = Trainee.developers.all
    else
      @developers = @developers.group_by {|x| x}.map {|k, v| [k, v.count]}
      @developers.sort_by!(&:last).reverse!
      @developers.map! do |developer|
        developer[0]
      end
    end
    respond_to do |format|
      format.js {}
    end
  end

目前,同样的方法是按照我想要的次数命中服务器。由于我单击11个按钮进行过滤,因此显示下面的服务器日志。可以是1或5或20,具体取决于用户要查找的内容

Processing by SkillsController#filter as */*
  Parameters: {"ids"=>["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"]}
  Skill Load (0.6ms)  SELECT  `skills`.* FROM `skills` WHERE `skills`.`id` = 1 LIMIT 1
  Trainee Load (0.6ms)  SELECT `trainees`.* FROM `trainees` INNER JOIN `mastered_skills` ON `trainees`.`id` = `mastered_skills`.`trainee_id` WHERE `mastered_skills`.`skill_id` = 1
  Skill Load (0.4ms)  SELECT  `skills`.* FROM `skills` WHERE `skills`.`id` = 2 LIMIT 1
  Trainee Load (0.6ms)  SELECT `trainees`.* FROM `trainees` INNER JOIN `mastered_skills` ON `trainees`.`id` = `mastered_skills`.`trainee_id` WHERE `mastered_skills`.`skill_id` = 2
  Skill Load (0.3ms)  SELECT  `skills`.* FROM `skills` WHERE `skills`.`id` = 3 LIMIT 1
  Trainee Load (1.3ms)  SELECT `trainees`.* FROM `trainees` INNER JOIN `mastered_skills` ON `trainees`.`id` = `mastered_skills`.`trainee_id` WHERE `mastered_skills`.`skill_id` = 3
  Skill Load (6.1ms)  SELECT  `skills`.* FROM `skills` WHERE `skills`.`id` = 4 LIMIT 1
  Trainee Load (0.6ms)  SELECT `trainees`.* FROM `trainees` INNER JOIN `mastered_skills` ON `trainees`.`id` = `mastered_skills`.`trainee_id` WHERE `mastered_skills`.`skill_id` = 4
  Skill Load (0.3ms)  SELECT  `skills`.* FROM `skills` WHERE `skills`.`id` = 5 LIMIT 1
  Trainee Load (0.9ms)  SELECT `trainees`.* FROM `trainees` INNER JOIN `mastered_skills` ON `trainees`.`id` = `mastered_skills`.`trainee_id` WHERE `mastered_skills`.`skill_id` = 5
  Skill Load (2.6ms)  SELECT  `skills`.* FROM `skills` WHERE `skills`.`id` = 6 LIMIT 1
  Trainee Load (2.0ms)  SELECT `trainees`.* FROM `trainees` INNER JOIN `mastered_skills` ON `trainees`.`id` = `mastered_skills`.`trainee_id` WHERE `mastered_skills`.`skill_id` = 6
  Skill Load (4.2ms)  SELECT  `skills`.* FROM `skills` WHERE `skills`.`id` = 7 LIMIT 1
  Trainee Load (0.9ms)  SELECT `trainees`.* FROM `trainees` INNER JOIN `mastered_skills` ON `trainees`.`id` = `mastered_skills`.`trainee_id` WHERE `mastered_skills`.`skill_id` = 7
  Skill Load (1.2ms)  SELECT  `skills`.* FROM `skills` WHERE `skills`.`id` = 8 LIMIT 1
  Trainee Load (3.0ms)  SELECT `trainees`.* FROM `trainees` INNER JOIN `mastered_skills` ON `trainees`.`id` = `mastered_skills`.`trainee_id` WHERE `mastered_skills`.`skill_id` = 8
  Skill Load (0.3ms)  SELECT  `skills`.* FROM `skills` WHERE `skills`.`id` = 9 LIMIT 1
  Trainee Load (0.8ms)  SELECT `trainees`.* FROM `trainees` INNER JOIN `mastered_skills` ON `trainees`.`id` = `mastered_skills`.`trainee_id` WHERE `mastered_skills`.`skill_id` = 9
  Skill Load (2.1ms)  SELECT  `skills`.* FROM `skills` WHERE `skills`.`id` = 10 LIMIT 1
  Trainee Load (0.6ms)  SELECT `trainees`.* FROM `trainees` INNER JOIN `mastered_skills` ON `trainees`.`id` = `mastered_skills`.`trainee_id` WHERE `mastered_skills`.`skill_id` = 10
  Skill Load (0.3ms)  SELECT  `skills`.* FROM `skills` WHERE `skills`.`id` = 11 LIMIT 1
  Trainee Load (0.8ms)  SELECT `trainees`.* FROM `trainees` INNER JOIN `mastered_skills` ON `trainees`.`id` = `mastered_skills`.`trainee_id` WHERE `mastered_skills`.`skill_id` = 11
  Rendering skills/filter.js.haml
  Skill Load (0.7ms)  SELECT `skills`.* FROM `skills` INNER JOIN `mastered_skills` ON `skills`.`id` = `mastered_skills`.`skill_id` WHERE `mastered_skills`.`trainee_id` = 5
  Skill Load (0.5ms)  SELECT `skills`.* FROM `skills` INNER JOIN `mastered_skills` ON `skills`.`id` = `mastered_skills`.`skill_id` WHERE `mastered_skills`.`trainee_id` = 2728
  Skill Load (0.6ms)  SELECT `skills`.* FROM `skills` INNER JOIN `mastered_skills` ON `skills`.`id` = `mastered_skills`.`skill_id` WHERE `mastered_skills`.`trainee_id` = 10

5 个答案:

答案 0 :(得分:1)

优化这部分:

@skills = Skill.all
@developers = []
unless params[:ids].nil?
  params[:ids].each do |skill|
    skill = @skills.find(skill)
    skill.trainees.each do |developer|
      @developers << developer
    end
  end
end

要:

@developers = Skill.where(id: params[:ids]).joins(:trainees).select('trainees.*')

答案 1 :(得分:1)

首先@skills可以定义为: @skills = params[:ids].nil? ? Skill.all : Skills.where(id: params[:ids])

然后你面临n+1问题。为避免这种情况,您必须使用eager loading associations(ELA)或加入。

要使用ELA返回受训者对象,您可以使用mapflatten

接下来你有一个很好的部分:

@developers = @developers.group_by {|x| x}.map {|k, v| [k, v.count]}
@developers.sort_by!(&:last).reverse!

sort_by(&:last).reverse效果很快。

这部分:

@developers.map! do |developer|
  developer[0]
end

可以更改为:

@developers.map(&:first)

与之前的sort_by联合起来:

@developers = @developers.sort_by(&:last).reverse.map(&:first)

希望它有所帮助。

答案 2 :(得分:0)

所描述的问题看起来像N+1 problem。渴望加载关联可能会有所帮助:http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

答案 3 :(得分:0)

如果开发人员通过学员获得许多技能:

class Developer
  has_many :trainees
  has_many :skills, through: :trainees
end

然后你应该能够做到:

@developers = Developer.where(skills: {id: params[:ids]})

或者,您可以使用以下方法减少一次加入:

@developers = Developer.where(trainees: {skill_id: params[:ids]})

答案 4 :(得分:0)

使用来自@idej的大部分解决方案,我现在已经将我的代码变为更好的状态。我认为它可以更简化,但我很高兴。欢迎任何更多的答案

  def filter
    @skills = params[:ids].nil? ? Skill.all : Skill.where(id: params[:ids])
    @developers = @skills.inject([]) do |result, skill|
      result << skill.trainees
    end
    @developers = @developers.flatten.group_by {|x| x}.map {|k, v| [k, v.count]}
    @developers.sort_by!(&:last).reverse!.map!(&:first)
    respond_to do |format|
      format.js
    end
  end