Reputation: 627
Can't seem to wrap my head around this problem. I have a message model below
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"
Each message can only have one reply and the reply message will have its corresponding original message.
What I'd like to do is create a scope or a class method that allows me to pull replied messages in one batch and unreplied messages in another.
Something like
# 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
so I can chain the methods and ultimately pull messages with
user1.messages.replied
It doesn't currently work because I can't use the where clause unless it's a DB column...so I was thinking about adding a "replied" boolean column into the DB so I could use the where clause but there's probably a solution to this that I'm just not thinking about. A scope with a lambda? I'm stuck right now.
Any help much appreciated
Upvotes: 2
Views: 2152
Reputation: 35533
To find those that have been replied is fairly straightforward:
scope :replied, joins(:reply)
as anything without a reply will be filtered out with an INNER JOIN. To find those without replies is a bit more complex - you can either use a LEFT JOIN or an EXISTS subquery to accomplish this. includes
is a simple way to force a LEFT JOIN:
scope :unreplied, includes(:reply).
where(replies_messages: {id: nil}).
where(original_id: nil)
An EXISTS subquery may be somewhat more efficient, but more complex to write (at this time), as it would involve invoking Arel tables (or Squeel). For most cases a LEFT JOIN would be 'good enough', and includes
is a quick-and-dirty way to force the API to use one.
Upvotes: 6