我正在用消息和通知对一个应用程序建模,如下(简化):
# db/schema.rb
create_table :messages do |t|
...
end
create_table :notifications do |t|
t.references :message, index: true, foreign_key: true, null: false
t.references :recipient, index: true, foreign_key: { to_table: :users }, null: false
t.datetime :read_at
...
end
型号:
class User < ApplicationRecord; end
class Message < ApplicationRecord; end
class Notification < ApplicationRecord
belongs_to :message
belongs_to :recipient, class_name: 'User'
accepts_nested_attributes_for :message
end
在这个简化的例子中:
我正在寻找最干净(大多数类似Rails)和最具表现力的方式来加载用户的通知和相关消息,同时确保即使还没有为该用户在DB中创建通知,也加载了所有消息。这是为了避免每次发布新消息时都需要在DB中创建尽可能多的通知行。只有当用户读取消息(存储read_at
值)时,才会将一行添加到通知表中。
我成功地使用以下SQL实现了它:
# user.rb
def notifications
unsanitised_sql = <<-SQL
SELECT
:user_id AS recipient_id, m.id as message_id, n.read_at, n.created_at, n.updated_at,
m.text, ...
FROM (
SELECT *
FROM notifications
WHERE recipient_id = :user_id
) n
FULL OUTER JOIN messages m
ON (message_id = m.id)
SQL
ActiveRecord::Base
.connection
.select_all(ActiveRecord::Base.sanitize_sql [ unsanitised_sql, { user_id: id } ])
.map do |row|
Notification.new(
recipient_id: row['recipient_id'],
message_id: row['message_id'],
read_at: row['read_at'],
created_at: row['created_at'],
updated_at: row['updated_at'],
message_attributes: {
id: row['message_id'],
text: row['text'],
...
}
)
end
end
例如,如果我有一个id=1消息,两个id=20和id=21用户,一个通知(id=30,message_id=1,recipient_id=20),对于用户21和消息1,我会得到read_at=nil
的“虚拟”通知(因为用户还没有阅读)和相关的消息数据。
> User.find(21).notifications
=> [#<Notification:0x00007fb5c64df138 id: nil, message_id: 1, recipient_id: 21, read_at: nil, created_at: nil, updated_at: nil>]
> User.find(21).notifications.first.message
=> #<Message:0x00007fd57c52b5a8 id: 1, text: ...>
然而:
instance);
Message
或Notification
添加新属性时,代码都需要更新;例如,
has_many :notifications
关联处理User
,或者如果不可能的话,使用自定义notifications
作用域。这是为了避免急切的加载,有一个更流畅的API,能够加入其他可能的关系,等等。或者至少我想改进语法,使之更像Rails。。有什么想法吗?
发布于 2020-03-20 17:55:20
您可以获取属于用户的通知。然后检索没有通知目标用户的消息,并为它们构建一个通知。最后,将这两个集合组合起来。
class Message < ApplicationRecord
has_many :notifications
end
class Notification < ApplicationRecord
belongs_to :message
belongs_to :recipient, class_name: 'User'
end
class User < ApplicationRecord
has_many :notifications # you might need `inverse_of: :recipient`
def all_notifications
notifications.load + Message
.where.not(id: notifications.pluck(:message_id)).pluck(:id)
.map { |message_id| notifications.build(message_id: message_id) }
end
end
遗憾的是,这仍然会产生数组结果。我不确定这是否可能有正常的联系。希望其他答案能证明我错了。
我真的想问你这是否产生了正确的结果?这个问题相当复杂,我不确定我对它的理解是否100%正确。
https://stackoverflow.com/questions/60778492
复制相似问题