Ruby/Rails/Redis Friends/Followers System
I found an interesting single-level follow/following/friends system using redis:
http://jimneath.org/2011/03/24/using-redis-with-ruby-on-rails.html
I extracted it out into a module and am documenting it here.
It utilizes sets in Redis. Each user has a set called following
and followers
. When a User A wants to follow user B, you add the id of User B to user the set of User A. To get all of the users A is following you simple get all the members of the following
set of User A.
smembers users:[id]:following
And to get all the people following User A you do the same redis command w/ the respective set name:
smembers users:[id]:followers
Then to see if two users A & B are friends you can employ the power of sets and do an intersection of users:A:following
& users:A:followers
Since we are using redis sets we get all the other included functionality, such as counting how many followers I have, how many people I follow by using the scard
command. I can do one-off checks to see if I follow a particular user w/ the sismember
command
In my case I simple include this module into my rails User model file and I get all friending functionality: (User.rb)
class User < ActiveRecord::Base
include Bearbrandd::Friendable
end
Example usage:
u.following
=> #<ActiveRecord::Relation [#<User id: 2, first_name: "cassee", last_name: "aguirre", email: nil, created_at: "2014-08-25 19:32:53", updated_at: "2014-08-25 19:32:53">]>
u=User.last
=> #<User id: 2, first_name: "cassee", last_name: "aguirre", email: nil, created_at: "2014-08-25 19:32:53", updated_at: "2014-08-25 19:32:53">
u.followers
=> #<ActiveRecord::Relation [#<User id: 1, first_name: "Alex", last_name: "egg", email: nil, created_at: "2014-08-25 18:52:27", updated_at: "2014-08-25 19:33:08">]>
u.follow! User.first
=> [true, true]
> u.friends
=> #<ActiveRecord::Relation [#<User id: 1, first_name: "Alex", last_name: "egg", email: nil, created_at: "2014-08-25 18:52:27", updated_at: "2014-08-25 19:33:08">]>
u.followed_by? User.first
=> true
u.followers_count
=> 1
u.following_count
=> 1
And there you have it: a simple friending model built on redis w/o any complicated joins that would result if we did it in SQL!
Here is the complete module: (Friendable.rb)
module Bearbrandd
module Friendable
# follow a user
def follow!(user)
Redis.current.multi do
Redis.current.sadd(self.redis_key(:following), user.id)
Redis.current.sadd(user.redis_key(:followers), self.id)
end
end
# unfollow a user
def unfollow!(user)
Redis.current.multi do
Redis.current.srem(self.redis_key(:following), user.id)
Redis.current.srem(user.redis_key(:followers), self.id)
end
end
# users that self follows
def followers
user_ids = Redis.current.smembers(self.redis_key(:followers))
User.where(:id => user_ids)
end
# users that follow self
def following
user_ids = Redis.current.smembers(self.redis_key(:following))
User.where(:id => user_ids)
end
# users who follow and are being followed by self
def friends
user_ids = Redis.current.sinter(self.redis_key(:following), self.redis_key(:followers))
User.where(:id => user_ids)
end
# does the user follow self
def followed_by?(user)
Redis.current.sismember(self.redis_key(:followers), user.id)
end
# does self follow user
def following?(user)
Redis.current.sismember(self.redis_key(:following), user.id)
end
# number of followers
def followers_count
Redis.current.scard(self.redis_key(:followers))
end
# number of users being followed
def following_count
Redis.current.scard(self.redis_key(:following))
end
# helper method to generate redis keys
def redis_key(str)
"user:#{self.id}:#{str}"
end
end
end
Permalink: ruby-rails-redis-friends-followers-system
Tags: