避免 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
因此,您明确地以想要 User
s 的事实开始查询。然后你加入 tables,得到一个 巨大的 集合,每个评分与其 user
相结合(都在数据库中!)。 merge
的内容然后将该组中的评分过滤为您想要的评分。
当心,因为我从问题中复制的行可能容易受到伪造参数的攻击,所以它没有被过滤。你有 ratings
table,所以如果 params[:ratings]
结果是一个散列( 可以 完成,它实际上是用户的输入)它将是被视为条件哈希,允许用户发送任何基于哈希的条件。
yoursite.com/whatever?ratings[id]=12
该查询字符串将导致 params[:ratings]
为:
{"id" => "12"} # Damn right, a Ruby hash
Strong parameters 存在是有原因的。
这个特定主题有很多答案,但我找不到最适合我的答案。我有一个食谱应用程序,我需要所有对该食谱给予特定评级的用户(例如:对特定食谱给予评级 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
因此,您明确地以想要 User
s 的事实开始查询。然后你加入 tables,得到一个 巨大的 集合,每个评分与其 user
相结合(都在数据库中!)。 merge
的内容然后将该组中的评分过滤为您想要的评分。
当心,因为我从问题中复制的行可能容易受到伪造参数的攻击,所以它没有被过滤。你有 ratings
table,所以如果 params[:ratings]
结果是一个散列( 可以 完成,它实际上是用户的输入)它将是被视为条件哈希,允许用户发送任何基于哈希的条件。
yoursite.com/whatever?ratings[id]=12
该查询字符串将导致 params[:ratings]
为:
{"id" => "12"} # Damn right, a Ruby hash
Strong parameters 存在是有原因的。