【发布时间】:2015-12-02 02:07:30
【问题描述】:
拥有模型:
class Movie < ActiveRecord::Base
has_many :comments
has_many :ratings
end
# self join to unable multilevel comments
class Comment < ActiveRecord::Base
belongs_to :upcomment, class_name: "Comment"
belongs_to :user
end
class Rating < ActiveRecord::Base
belongs_to :movie
belongs_to :user
end
class User < ActiveRecord::Base
has_many :ratings
has_many :comments
end
当我不想获得所有 cmets 及其回复和 cmets/回复作者时,我会进行以下查询:
@comments = Comment
.where('comments.movie_id = ? AND comments.upcomment_id IS NULL', params[:movie])
.includes([:user, :replies => :user])
Q1:但是我应该如何修改这个查询以包括每个评论和回复的用户评分?。
我的尝试
为了简单起见,现在我将跳过回复。我在 .includes 和 where 子句中添加了评分,如下所示:
@comments = Comment
.where('comments.movie_id = ? AND comments.upcomment_id IS NULL', params[:movie])
.includes([:user => :ratings])
.where(ratings: { movie_id: params[:movie] })
它有效,但它只返回作者有评级的那些 cmets。当我查看 Rails 生成的 SQL 时,我注意到 '"ratings"."movie_id" = ?'是(可能)在错误的地方:
SELECT [...]
LEFT OUTER JOIN "users" "users" ON "users"."id" = "comments"."user_id"
LEFT OUTER JOIN "ratings" "users_ratings" ON "ratings"."user_id" = "users"."id"
WHERE (comments.movie_id = '131' AND comments.upcomment_id IS NULL) AND "ratings"."movie_id" = 131
| So I changed and made a query in SQLite to check if it works
V
SELECT [...]
LEFT OUTER JOIN "users" ON "users"."id" = "comments"."user_id"
LEFT OUTER JOIN "ratings" ON "ratings"."user_id" = "users"."id" AND "ratings"."movie_id" = "comments"."movie_id"
WHERE (comments.movie_id = '131' AND comments.upcomment_id IS NULL)
它似乎有效,但是 Q2:如何在 Rails 中执行多个条件的 join(无需自己编写 join)?
由于我没有找到上述问题的答案,所以我自己编写了这些连接。我不得不添加 eager_load 以使其工作并最终得到以下查询:
@comments = Comment
.where('comments.movie_id = ? AND comments.upcomment_id IS NULL', params[:movie])
.joins('LEFT OUTER JOIN "comments" "replies_comments" ON "replies_comments"."upcomment_id" = "comments"."id"')
.joins('LEFT OUTER JOIN "users" "users_replies_comments" ON "users_replies_comments"."id" = "replies_comments"."user_id"')
.joins('LEFT OUTER JOIN "ratings" "ratings_users_replies_comments" ON "ratings_users_replies_comments"."user_id" = "users_replies_comm ents.id" AND "ratings_users_replies_comments"."movie_id" = "comments"."movie_id"')
.joins('LEFT OUTER JOIN "users" "users_comments" ON "users_comments"."id" = "comments"."user_id"')
.joins('LEFT OUTER JOIN "ratings" "ratings_users_comments" ON "ratings_users_comments"."user_id" = "users_comments"."id" AND "ratings_ users_comments"."movie_id" = "comments"."movie_id"')
.eager_load([:replies => { :user => :ratings }, :user => :ratings])
它似乎有效,但我认为它可能是性能杀手。所以我的最后一个想法是加载所有带有作者/回复的 cmets 并对其进行迭代,收集所有作者 ID 并使用“IN(id1,id2,...)”进行额外查询。然后将其作为单独的变量传递给视图。像这样的:
ids = @comments.map { |c| c.user.id }
@comments.each { |c| c.replies.map { |r| ids.push r.user.id } }
ids.uniq!
@ratings = Rating
.where('movie_id = ?', params[:movie])
.where('user_id IN(?)', ids)
或者也许我不应该简单地做任何事情并在需要时让 Rails 达到延迟负载等级?你怎么看?
【问题讨论】:
标签: ruby-on-rails ruby-on-rails-4 activerecord associations rails-activerecord