3

What is the best way to approach making a newsfeed?

Currently I have an observer that creates a new newsfeedactivity record everytime someone creates a record. But, to deal with privacy I end up have 7 or 8 joins to get the appropriate output.

It seems like this is going to be slow and inefficient. What's another strategy for pulling out the right newsfeedactivity records as a scope?


More details:

Currently I have a site to help users track projects that they're working on. There are public and private projects (where people can invite collaborators).

I want my newsfeed to include when public projects are created. When you are invited to a private project. When a user follows a project. And then all of the actions of the other users that you're following. Then for the private projects I have another join table to determine who has access to the projects. (There are also comments on each of these projects that I want to show up in the newsfeed as well).

All of the following relationships are currently in join tables, which is why I have a lot of joins.

To get an idea of the type of query - I'm thinking it would look something like this:

SELECT news_feed_activities.* FROM news_feed_activities LEFT JOIN user_following_relationships ON user_following_relationships.following_id = news_feed_activities.user_id LEFT JOIN user_project_relationships ON user_project_relationships.project_id = news_feed_activities.responding_to_id AND news_feed_activities.responding_to_type = 'Project' WHERE (user_following_relationships.user_id = 1 OR user_project_relationships.user_id = 1 OR news_feed_activities.user_id = 1 OR up2.user_id = 1) GROUP BY news_feed_activities.id ORDER BY news_feed_activities.id DESC


EDIT: I think I'm probably going to end up using Redis along these lines http://blog.waxman.me/how-to-build-a-fast-news-feed-in-redis

Cyrus
  • 3,687
  • 5
  • 35
  • 67
  • This might be helpful: http://stackoverflow.com/questions/3809430/implementing-a-news-feed-activity-feed-on-several-models-recommendations – Mahmoud Gamal Feb 15 '12 at 07:07
  • Thanks. I used that when I was first building my newsfeed. It works great if there aren't any personalization and/or privacy settings. I'm trying to deal with privacy settings and I'm not sure how to filter through all of the newsfeedactivity records efficiently (I'm also going to want pagination, which is why I want a scope). – Cyrus Feb 15 '12 at 07:33
  • My first idea about (simple) privacy setting would be to use some flag or status columns within the table instead of adding additional tables which need joins. Seven or eight joins (= additional tables?) sounds like a lot for such a task. You should give us more information about your requirements to get an useful response. In general I wouldn't worry too much about a few join tables, as long as the indexes are set properly. – thorsten müller Feb 15 '12 at 09:24
  • I added a basic example of the type of sql query I have written right now. – Cyrus Feb 16 '12 at 01:55

1 Answers1

0

As RoR.

In your controller:

@user = current_user # (?)
recent_since = 24.hours.ago
@news_feed = []

# 1) I want my newsfeed to include when public projects are created. 
@news_feed += Project.recent.open
# 2) When you are invited to a private project. 
@news_feed += @user.invites.received.pending
# 3) When a user follows a project.
@news_feed += @user.user_following_relationships.recent
# 4) And then all of the actions of the other users that you're following. 
@news_feed += @user.follows.collect(&:activities)
# 5) Then for the private projects I have another join table to determine who has access to the projects. (There are also comments on each of these projects that I want to show up in the newsfeed as well).
@news_feed += @user.projects.closed

@news_feed.sort!{ |a,b| a.created_at <=> b.created_at }

I did some sample scopes for you too. project.rb

scope :recent, :conditions => ["created_at >= ?", 24.hours.ago]
scope :open, :conditions => "publicity = 'Public'"
scope :closed, :conditions => "publicity = 'Private'"

This is based on the precept that your news feed is actually a summary of recent activity across models rather than having a 'newsfeed' model.

TomDunning
  • 4,829
  • 1
  • 26
  • 33
  • if this wasn't what you were asking please comment and i'll amend as needed – TomDunning Feb 16 '12 at 01:21
  • thanks for your answer, but this isn't quite what i'm looking for. I want to have a scope called newsfeed. So that I can paginate through the newsfeed. This approach seems like it would work if I were trying to list the activity in the last day, but not if I want to get 30 newsfeed activity items at a time so that users can paginate further into the past. – Cyrus Feb 16 '12 at 01:55
  • ok, simple change: 1) remove the 'recent' requirement, 2) add: `@news_feed = @news_feed.paginate :page => params[:page]` – TomDunning Feb 16 '12 at 15:46
  • I tried that. It didn't work. It says that it can't paginate over an array. – Cyrus Feb 16 '12 at 20:54
  • very odd, i use `@resources = current_user.user_resources.paginate :per_page => (params[:per_page] || 25), :page => params[:page]` quite a lot. I'm using will_paginate -v=2.3.12 in fact in the same way, you should be able to move the above into the user model: User.rb `def news_feed ... code as above ... end` then call it with `@user.news_feed` – TomDunning Feb 16 '12 at 21:01
  • when you say `current_user.user_resources` that's a scope not an array - i think – Cyrus Feb 17 '12 at 07:47
  • nope, it's an array, quite a complicated one which i pull back like this `def user_resources x = [] x << Resource.all(:conditions => {:resource_for_type => "All"}) x << self.branch.resources x << self.branch.area.resources x << self.branch.zone.resources return x` – TomDunning Feb 17 '12 at 09:26