有模特:
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
当我不想得到他们的回复和评论/回复作者的所有评论我做以下查询:
@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] })
它有效,但只返回作者评分的那些评论。当我查看Rails生成的SQL时,我注意到了“收视率”。“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中执行多个条件的连接(不自己编写连接)?
由于我没有找到上述问题的答案,我自己写了这些联接。我不得不添加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])
它似乎有效,但我认为它可能是性能杀手。所以我的最后一个想法是用作者/回复加载所有注释并迭代它,收集所有作者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)
或者mayby我不应该简单地做任何事情并让Rails在需要时加入懒惰的负载?你怎么看?
答案 0 :(得分:0)
所以,评分与评论不一样吧?为什么不?使评论也作为(可选)评级,并删除评级表和模型。这需要添加评分分数以进行评论并使评论正文可选。然后你只需要加载电影的所有评论。
不要担心要预加载所有线程子注释的查询。当你有几百万条评论时,你会担心这一点。您应该为每个孩子,孙子等添加最顶级“adam / eve”评论的ID,以便您可以在一个查询中快速加载所有后代。您还可以添加movie_id,以便将其放入索引并快速加载电影的每个评论,无论多深。
此外,在创建或更新评级时,将电影的总评分分数缓存在电影表中。然后,您可以显示包含评分的电影列表,而不会为每个请求执行COUNT。