Monday, February 11, 2008

Write your own Orkut in Rails - Part 1

So you want to start a new social networking portal? (well almost every third guy I meet is trying to do so!). Lets start writing one in Rails. It may not scale to the level of original orkut but it will surely be able to handle a couple of thousand odd users who login 4-5 times a day(you may be able to easily optimize to handle upto 10K probably on a decent server grade machine or two). Few things I can guarantee you that it will be a lot of fun (since it is in rails ) and will be faster than the original one (just a little bit).

First thing is to create a database table structure for it. I use mysql and i pretty good. Did you know that initially orkut was written on mysql and aspx ? or was it mssql...anyway it now runs on bigtable.

First thing to create is a person table like this:
CREATE TABLE `people` (
`id` int(11) NOT NULL auto_increment,
`emailid` varchar(35) NOT NULL default '',
`password` varchar(100) NOT NULL default '',
`display_name` varchar(100) NOT NULL default '',
`created_at` datetime default NULL,
`updated_at` datetime default NULL,
PRIMARY KEY (`id`),
KEY `display_name` (`display_name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;

This table will have data of people logging to your orkut like email id, password (in md5 hash) and display name.

Lets create friends table now
CREATE TABLE `friends` (
`person_id` int(11) NOT NULL,
`friend_id` int(11) NOT NULL,
KEY `person_id` (`person_id`,`friend_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
That was easy! Well I have not added a friend, good friend, best friend etc column. You add it later if you need it. For now lets keep things simple.

CREATE TABLE `scraps` (
`id` int(11) NOT NULL auto_increment,
`body` varchar(1024) NOT NULL default '',
`created_at` timestamp NOT NULL default CURRENT_TIMESTAMP,
`person_id` int(11) NOT NULL,
`owner_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `owner_id` (`owner_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

Thats pretty trivial and mundane..isn't it ?

Lets create the tricky one. Profiles

CREATE TABLE `profiles` (
`id` int(11) NOT NULL auto_increment,
`type` smallint(6) NOT NULL,
`body` text,
`person_id` int(11) default NULL,
`created_at` datetime default NULL,
`updated_at` datetime default NULL,
PRIMARY KEY (`id`),
KEY `person_id` (`person_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;

Well this table only has a type and a body. Type will contain the information about the type of the profile(like personal, business etc) and body will contain the whole profile in YAML format.

Similarly create pictures
CREATE TABLE `pictures` (
`int` int(11) NOT NULL,
`file_name` varchar(255) NOT NULL default '',
`caption` varchar(255) default NULL,
`person_id` int(11) NOT NULL,
KEY `file_name` (`file_name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

We will give a miss to communities and likes for right now(who needs them anyway!)

After database schema is ready lets create a rails application. To be honest we should have created the rails app first and then create this schema from generators. But I prefer to have more control on the schema.

Lets dip our hands into models now.

class Person < ActiveRecord::Base
has_and_belongs_to_many :friends, :class_name => "Person", :join_table => "friends", :foreign_key => "person_id", :association_foreign_key => "friend_id"
has_many :pictures
has_many :profiles
has_many :scraps
end


This will create the friends relationships for us. Every person can now have many friends in a many to many way.

Picture, scrap, profile etc all are similar

class Profile < ActiveRecord::Base
belongs_to :person, :foreign_key => :person_id
end

Nothing to see here. Move on.


Now we open a console and create a few people here

p1=Person.create(:emailid => 'piyush.@foobar1.com', :password => 'piyush', :display_name => 'pr1')
p2=Person.create(:emailid => 'piyush@foobar2.com', :password => 'piyush', :display_name => 'pr2')


Now lets make them friends

p1.friends << p2
p2.friends << p1

Notice here I make two calls to friends from each person object because if p1 added p2 as friend the relationship is not immediately established as p2 has to acknowledge. Also p1 may add p1 as 'best friend' but p2 may add him/her is 'don't know this person'. For a friend relationship to established two way connection is a must. This may however be done by adding a few more columns to the friends table but I want to keep it lean.

This is it for now. Lets do the views and controllers of it in part 2

Wednesday, February 06, 2008