避免 rails 中的 n+1 查询错误有很多关联

Avoiding n+1 query bug in rails has many association

这个特定主题有很多答案,但我找不到最适合我的答案。我有一个食谱应用程序,我需要所有对该食谱给予特定评级的用户(例如:对特定食谱给予评级 5 的用户列表)而没有 n+1 查询错误。我知道它可以使用 includes 选项解决,但属于参考我不知道如何使用它。我正在使用 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?

修改后的查询:

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

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

@VedprakashSingh 的回答有一个主要缺点,即返回填充了来自 User 的数据的 Rating 实例。所以,突然之间,您就没有 User 的所有方法了。类型安全被抛出 window.

您可以改为使用 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 的事实开始查询。然后你加入 tables,得到一个 巨大的 集合,每个评分与其 user 相结合(都在数据库中!)。 merge 的内容然后将该组中的评分过滤为您想要的评分。

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

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

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

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

Strong parameters 存在是有原因的。