ruby-on-rails Rails方式(查询)获取用户没有阅读的帖子或有新回复的帖子

ccrfmcuu  于 2023-05-02  发布在  Ruby
关注(0)|答案(5)|浏览(101)

我有一个论坛。每个用户都有帖子和回复。每个帖子都有回复。每当一个用户查看一个帖子时,我都会在Views表中创建一条记录(或者用最后一个reply_id更新它)。假设一个帖子还没有回复,那么我只保存user_id和post_id。如果有对帖子的回复,那么我保存user_id、post_id和帖子查看时最后一个回复的reply_id。
我试图创建一个页面“搜索未读的帖子”,向用户显示任何他们没有读过的帖子,或者如果有一个新的回复他们已经看过的帖子。我相信你们大多数人都熟悉这个原则,因为大多数论坛引擎都有这种搜索,但我想确保不要留下任何问题。
以下是关联(去除了所有权限检查和其他关联):

class User < ActiveRecord::Base
  has_many :posts
  has_many :views
end

class Post < ActiveRecord::Base
  belongs_to :user
  has_many :replies
  has_many :views
  has_one :latest_reply, :order => "created_at DESC", :class_name => "Reply", :conditions => { :replystatus => 'published' }
end

class Reply < ActiveRecord::Base
  belongs_to :post
  belongs_to :user
  has_many :views
end

class View < ActiveRecord::Base
  belongs_to :post
  belongs_to :reply
  belongs_to :user
end

我想知道如何只得到的职位,用户,要么,还没有阅读在所有(没有记录的意见),或职位latest_reply.id大于“视图”中保存的id。我已经想出了如何用sql编写这个查询(使用连接,子查询和联合),但我想知道是否有人知道用Rails的方式来做这件事。我猜一些智能(链接)范围将工作,但我只是不能把我的头周围。

ukqbszuj

ukqbszuj1#

我给@BillyChan添加了评论re:发布和查看计数。
Re:latest_reply,我认为你应该把它作为一个范围推到Reply模型中:

scope :latest, order('created_at DESC').first
scope :published, where(:replystatus => 'published')

然后,您可以将其作为文章访问。views.published.最新/回复views.published.最新的和比较,但你想。

aamkag61

aamkag612#

新答案

根据OP的评论,我意识到我误解了以前回答的问题。已读/未读跟踪是如此深入到用户级别。我没有这样的经验,但可以提供两个想法集思广益。
1.数据库方式。你保持你现在做的。但是对于每个新创建的帖子,您需要将每个用户的记录添加到View表中,每个用户的初始值为零视图计数。通过这个你可以使用SQL来检查哪些帖子对于某些用户是未读的。缺点是服务器的工作量会很大,表会很大,也许你可以使用cron来添加View记录,使用缓存来存储用户未读的帖子。
1.数组比较方式。在用户表中添加一个字段,用于存储用户查看的帖子ID。对于他查看的每个帖子,该字段将更新为添加的帖子ID。然后,当你显示一个普通的帖子列表(比如最近的帖子)时,你可以比较这个id是否在这个字段中(转换为数组),如果没有,将帖子标记为未读。要显示未读文章的完整列表,您可以使用SQL搜索不在此字段中的文章id(SELECT id FROM posts WHERE id NOT IN the_id_list_arrary)
你可以随意批评。

对问题理解错误,放弃原答案

我的建议是在Post中添加一个名为view_count的字段,默认值为0。
每次向View表写入一条关于Post的新记录时,view_count也会增加1。
要搜索未读帖子,您可以简单地搜索view_count为0的帖子。
通过这种方法,你将保存大量的服务器资源查询,因为视图表将非常大,查询它的方式在你的问题是非常沉重的。
可选的未来增强功能:
1.您可以设计一个任务来通过View表重建view_count,并手动或通过cron运行它。
1.您甚至可以在将来添加view_count_month,view_count_year,view_count_all字段。有了重建机制,维护这些字段就很容易了。
毕竟,在我看来,基本的解决方案应该足够好了。

lsmepo6l

lsmepo6l3#

使用中介表是一种正确的方法,但它通常太重而无法处理,即使使用计数器缓存或其他东西(想象一下View表中的数百万条记录)。..).
因此,我可以提供一个更轻,更快的选择,没有任何连接表。

User has_many Posts and Replies
Posts has_many Replies

Post有一个属性:last_viewed(日期时间)。每次当用户查看帖子的回复时,属性会更新为特定回复的created_at值(如果更新)。
现在:

Reply.includes(:post => :user).where(["users.id = ? AND posts.last_viewed < replies.created_at", @user.id]) # all replies belongs_to post, which ones the post's author (user) has never seen.

# and

Post.includes(:replies).where(["posts.user_id = ? AND posts.last_viewed < replies.created_at", @user.id]) # all user's posts having replies, which he has never seen.

这种方法在论坛引擎中最常用。因为它很快。

**[更新]**所有用户和所有帖子和所有回复:

将属性添加到用户:last_view_posts_at(datetime)当用户打开该属性时,必须使用Post或Reply的created_at值(如果较新)更新该属性。

Reply.includes(:post).where(["posts.created_at > ? OR replies.created_at > ?", @user.last_view_posts_at.to_s(:db)]) # all replies which the @user has never seen.

# and

Post.includes(:replies).where(["posts.created_at > ? OR replies.created_at > ?", @user.last_view_posts_at.to_s(:db)]) # all unseen posts and post having unseen replies.

问题是你不能管理特定帖子和回复的读/未读,因为它依赖于时间戳。你必须使用`last_view_posts_at '。假设你有3条未读消息。之后,你打开最旧的,然后阅读最新的,在这种情况下,第二个(中间)将不会是'未读'了。

r7s23pms

r7s23pms4#

我能够微调服务于我的问题的实际查询。下面是实际的查询(在这个例子中,user_id是硬编码的,但在控制器中,它是@current_user。id):

select posts.id, posts.title
  from posts
 where posts.id not in (
        select p.id
          from (select posts.id, max(replies.id)
              from posts
              left outer join replies on replies.post_id = posts.id
             group by posts.id
            INTERSECT
            select views.post_id, views.reply_id
              from views
             where views.user_id = 1 ) as p )

下面是我如何将它放在控制器中(将它作为一个范围移动到模型中):

@posts = Post.includes(:latest_reply).where('posts.id not in ( select p.id from (select posts.id, max(replies.id) from posts left outer join replies on replies.post_id = posts.id group by posts.id INTERSECT select views.post_id, views.reply_id from views where views.user_id = ? ) as p )', @current_user.id).page(params[:page]).per(15)

我知道这并不漂亮,但我已经花了将近两天的时间来弄清楚如何使用这种“Rails方式”,现在必须这样做,因为我还有很多编码要做:)
如果任何人有任何提示或如何做到这一点的“Rails方式”或如何加快这个查询(这是令人惊讶的快),我会感谢您的所有输入。

ee7vknir

ee7vknir5#

要获取用户的未读帖子,您可以在Post模型中使用以下范围:

scope :unread_for_user, lambda { |user|
  joins("LEFT JOIN views ON views.post_id = posts.id AND views.user_id = #{user.id}")
    .where("views.id IS NULL OR views.reply_id < ?", latest_reply.id)
}

这会触发所有帖子的JOIN,即使用户没有查看过任何帖子。此范围允许您过滤所有未读帖子,即使该用户尚未对帖子作出React。最近的回复总是与用户的React不同。您可以对特定用户使用GetUnreads作用域来获取该用户尚未阅读的所有帖子的列表我在我的site上使用了这个。

相关问题