I am working on a Rails 3.2.6 app for a friend which has implements some message board functionality. Users can post messages into a categories. Each user also has a list of other users they have friended.
I got a feature request recently to display the number of unread posts made by the current user's friends next to each category. This is tracked on a category basis, not a per post basis, since the posts are displayed on a main newsfeed and you can't read a single post. So the feature request states that when a user clicks a category, which filters posts by that category, it will reset the unread count for that category.
I've implemented the unread counter by adding a database table that tracks the last time a user "read" all the messages. The table contains user_id, category_id, and last_read_time.
Now I am trying to implement the unread counters. I set up some scopes in my Post model (some of these are used in different places, which is why they are separate scopes):
scope :by_users, lambda { |users| where("user_id IN (?)", users) unless users.nil? }
scope :by_categories, lambda { |categories| where("category_id IN (?)", categories) unless categories.nil? }
scope :since, lambda { |date| where("updated_at > ?", date) unless date.nil? }
Here is my first crack at an unread method (also in the Post model):
def self.unread_count(user, category)
read_time = ReadStatus.where("user_id = ? AND category_id = ?", user.id, category.id).first
if read_time.nil?
#handle it
end
#if user has no friends, there will be no unread posts displayed
if user.friends.count == 0
return
else
friends = user.friends
end
unread = by_users(friends).since(read_time.last_read_time).by_categories(category.id).count
unless unread == 0
"(" + unread.to_s + " new)"
end
end
This works fine, but I would like to make this more efficient. Right now, there are 81 categories users can post in. This means when the homepage loads there are 81 count queries firing off, and this is repeated anytime the homepage is re-loaded. I can't think of any way to cache this value or eliminate these queries, since the unread count can change at any given moment and needs to be refreshed when the page is reloaded.
Any tips/tricks/Rails pixie dust to make this better? Every solution I think of either results in stale counter values or a large DB hit (for what I feel is a fairly trivial "feature")