get the complement of a scope in rails / a scope that filters out matching entries
I am building a model which keeps track of posts that a given user has read (in Rails 3). The table that tracks this is 'readings.' a reading belongs_to both :user and :post, both of which has_many :readings. When a user sees a post, it is marked as read, and a 'reading' is created with the user_id and post_id. This seems to work fine, but now I am trying to write a scope that returns only unread posts for a given user. Finding read posts works fine:
scope :read, lambda { |user_id = nil |
user_id.nil? ? {} : joins(:readings).where('readings.user_id = :user_id', :user_id => user_id)
}
However my attempts to return the complement of this set (unread posts) are failing. How can this be done? Is there a general way to write a scope(method) that just returns the complement of another(here, :read)? I would also be happy to write this as a class method that works the same as a scope.
Bear in mind that readings are only created when a post is read, so a new post would have no readings. This seemed more economical than creating entries for every user for every new post.
EDIT: OK, I am getting closer - the below scope almost works, but does not require that all readings for a post are not equal to user_id. Thus if user #1 has read a post, and so has user #2, that other reading is queried and found to be not equal. So the post is wrongly found to be unread.
scope :unread, lambda { |us开发者_开发问答er_id = nil |
user_id.nil? ? {} : includes(:readings).where('(readings.user_id IS NULL OR readings.user_id != :user_id)', :user_id => user_id)
}
Well, it looks like the below works. Still testing though. If anyone finds a hole in here happy to re-open.
scope :unread_by, lambda { |user_id = nil, hide_unread = nil|
{ :joins => "LEFT JOIN readings ON readings.post_id = post.id
AND readings.user_id = #{user_id}",
:conditions => 'readings.id IS NULL' }
}
精彩评论