【问题标题】:Avoiding n+1 query bug in rails has many association避免 Rails 中的 n+1 查询错误有很多关联
【发布时间】:2015-10-24 14:18:09
【问题描述】:

这个特定主题有很多答案,但我找不到任何一个非常适合我。我有一个食谱应用程序,其中我需要所有对该食谱给予特定评级的用户(例如:对特定食谱给予评级 5 的用户列表)而没有 n+1 查询错误。我知道它可以使用包含选项来解决,但在属于参考中我不知道如何使用它。我正在使用 Rails 3.2.15。

以下是我的应用程序的模态级别描述

class Recipe < ActiveRecord::Base
  belongs_to :user, :foreign_key => 'creator_id'
  has_many :photos, :dependent => :destroy
  has_many :recipe_ingredients
  has_many :ingredients, :through => :recipe_ingredients
  has_many :ratings, :dependent => :destroy
end


class  Rating < ActiveRecord::Base
  belongs_to :user, :foreign_key => 'rater_id'
  belongs_to :recipe
end

class Ingredient < ActiveRecord::Base
  belongs_to :user, :foreign_key => 'creator_id'
  has_many :recipe_ingredients
  has_many :recipes, :through => :recipe_ingredients 
end

class User < ActiveRecord::Base
  has_many :recipes , foreign_key: "creator_id", class_name: "Recipe", :dependent => :destroy
  has_many :ingredients, foreign_key: "creator_id", class_name: "Ingredient", :dependent => :destroy
  has_many :ratings, foreign_key: "rater_id", class_name: "Rating", :dependent => :destroy
end

我检索用户的查询是

@rec = Recipe.find(params[:id])
ratings = @rec.ratings.where(:ratings => params[:ratings])

users = ratings.map {|rate| rate.user}

这引入了一个 n+1 查询错误,请问有什么正确的方法可以使用 rails 吗?

【问题讨论】:

    标签: ruby ruby-on-rails-3 postgresql activerecord


    【解决方案1】:

    修改后的查询:

    users = ratings.includes(:user).select('users.name')
    

    考虑到您在用户中有名称列,我添加了select('users.name')。您可以为视图使用任何您想要的列。

    【讨论】:

    • 谢谢!哥们帮了大忙。我正在尝试包含(:用户),关联对您输入的名称非常重要
    【解决方案2】:

    @VedprakashSingh 的回答有一个主要缺点是返回填充了来自 User 的数据的 Rating 实例。因此,突然之间,您将失去所有User 的方法。类型安全被抛到了窗外。

    您可以改为使用joins/merge 组合来获取一个模型的实例,并在另一个模型上添加条件。像这样:

    User.joins(:ratings).merge(
      # Here you can place a relation of ratings
      @rec.ratings.where(:ratings => params[:ratings])
      # Yes, it's your line, copy-pasted from your question
    ) # ...and back here we get users joined with these
    

    因此,您明确地以您想要Users 的事实开始查询。然后你加入表格,得到一个 huge 集合,每个评级都与它的user 相连(都还在数据库中!)。 merge 的内容然后将该集合中的评级过滤为您想要的。

    当心,因为我从问题中复制的行可能容易受到伪造参数的影响,因此它没有被过滤。你有ratings 表,所以如果params[:ratings] 原来是一个哈希(可以完成,它实际上是用户的输入),它将被视为允许用户发送的条件的哈希任何基于哈希的条件。

    yoursite.com/whatever?ratings[id]=12
    

    该查询字符串将导致 params[:ratings] 成为:

    {"id" => "12"} # Damn right, a Ruby hash
    

    Strong parameters 存在是有原因的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-26
      • 1970-01-01
      • 2013-07-22
      • 1970-01-01
      • 2018-01-28
      • 1970-01-01
      • 2021-02-17
      相关资源
      最近更新 更多