【问题标题】:Rails4, getting all (multi-level) comments with their authors and authors ratingRails4,获取所有(多级)评论及其作者和作者评级
【发布时间】: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


    【解决方案1】:

    所以,收视率与 cmets 不同,对吧?为什么不?使评论也充当(可选)评级并删除评级表和模型。这需要为评论添加评分,并使评论正文可选。那么你只需要为一部电影加载所有的 cmets。

    不用担心预加载所有线程子 cmets 的查询。当您拥有几百万个 cmets 时,您会担心这一点。您应该将最顶部的“adam/eve”评论的 id 添加到每个孩子、孙子等,这样您就可以在一个查询中快速加载所有后代。您还可以添加movie_id,这样您就可以将其放入索引中并快速加载电影的每条评论,无论评论有多深。

    此外,在创建或更新评分时,将电影的总评分缓存在电影表中。然后,您可以显示带有评分的电影列表,而无需为每个请求执行 COUNT。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-18
      • 2016-01-09
      • 1970-01-01
      • 2011-01-26
      • 2018-01-13
      • 2022-12-09
      • 2017-10-08
      相关资源
      最近更新 更多