Rails范围/类方法,用于选择存在关联的项目

Jef*_*cke 2 ruby scope ruby-on-rails

似乎无法解决这个问题.我有一个消息模型如下

Message
 # content:string
 # original_id:integer
 # sender_id:integer
 # receiver_id:integer

 has_one :reply, class_name: "Message", foreign_key: "original_id"
 belongs_to :original, class_name: "Message"
Run Code Online (Sandbox Code Playgroud)

每条消息只能有一个回复,回复消息将有相应的原始消息.

我想要做的是创建一个范围或类方法,允许我在一个批处理中提取已回复的消息,在另一个批处理中提取未提取的消息.

就像是

 # return messages that have a reply present
 def self.replied
   where(reply.present?)
 end

 # return messages that have no reply
 def self.unreplied
   where(reply.nil?)
 end
Run Code Online (Sandbox Code Playgroud)

所以我可以链接方法,最终用它来拉消息

 user1.messages.replied
Run Code Online (Sandbox Code Playgroud)

它目前不起作用,因为我不能使用where子句,除非它是一个DB列......所以我在考虑在DB中添加一个"replied"布尔列,所以我可以使用where子句但是可能有一个解决方案对此,我只是没想到.一个范围有一个lambda?我现在卡住了.

任何帮助非常感谢

Pin*_*nyM 6

找到那些已经回复的内容相当简单:

scope :replied, joins(:reply)
Run Code Online (Sandbox Code Playgroud)

因为任何没有回复的内容都会被INNER JOIN过滤掉.要找到那些没有回复的东西有点复杂 - 您可以使用LEFT JOIN或EXISTS子查询来完成此任务. includes是一种强制LEFT JOIN的简单方法:

scope :unreplied, includes(:reply).
                  where(replies_messages: {id: nil}).
                  where(original_id: nil)
Run Code Online (Sandbox Code Playgroud)

EXISTS子查询可能更有效,但写入更复杂(此时),因为它涉及调用Arel表(或Squeel).对于大多数情况,LEFT JOIN将"足够好",并且includes是一种强制API使用它的快速而肮脏的方法.