What is the best way to store a user's Facebook friends list in my database? - ruby-on-rails

Overview
I'm creating a Ruby on Rails website which uses Facebook to login.
For each user I have a database entry which stores their Facebook User ID along with other basic information.
I'm also using the Koala gem in order to retrieve a user's friendlist from Facebook, but I'm unsure as to how I should store this data...
Option 1
I could store the user's friends as a serialized hash in the User table, then if I wanted to display a list of all the current user's friends, I could grab this hash and do something along the lines of SELECT FROM Users WHERE facebook_user_id IN hash
Each time the user logs in I could update this field to store the latest friends list.
Option 2
I could create a Friend table and store friendship information in here, where a User has many Friends. So there would be a row for each friendship, (User1 and User2 columns). Then to display a list of the current user's friends I could do something like SELECT User2 FROM Friends WHERE User1 = current_user
This seems like the better option to me, but...
It has the disadvantage that there would be many rows... If there were 100,000 users, each with 100 friends, that's now 10,000,000 rows in the Friends table.
It also means each time the user logs in, I'd need to loop over their Facebook friends list returned using Koala and create a Friend record if someone on their friendlist is in my User table and there isn't a corresponding entry in the Friends table. This seems like it'd be slow if a user has 1000 Facebook friends?
I'd appreciate any guidance on how it would be best to achieve this.
Apologies for the badly worded question, I'll try and reword/organise it shortly.
Thanks for any help in advance.

If you need to store a lot of data, then you need to store a lot of data. If you are like most, you probably won't run into that problem sooner than you have the cash to solve it. In other words, you are probably assuming you'll have more traffic and data than you'll get, at least in the short-term. So I doubt this is an issue, even though it is a good sign that you are thinking about it now rather than later.
As I mentioned in my comment below, the easiest solution is to have a tie table with a row for each side of the friend relationship (a has_many :friends, through: :facebook_friend_relationships, class_name: 'FacebookFriend' on FacebookFriend, per the design mentioned below). But your question seemed to be about how to reduce the number of records, so that is what the remainder of the answer will address.
If you have to store in the DB and you know for sure that you will absolutely have every FB user on the planet hitting your site because it is so awesome, but they won't all hit at once, then if you are limited in storage, you may want to use a LRU algorithm (remove the least recently used records) possibly with timed expiration also. You could just have a cron job that does a query on the DB then deletes old/unused records to do this. Wouldn't be perfect, but it would be a simple solution.
You could also archive older data rather than throw it away. So, frequently used data could stay in the table of active users, and then you might offload older data to another table or even another database (and you might see the apartment and second_base gems for that). However, once you get to the size, you're probably looking at a number of other architectural solutions that have much less to do with ActiveRecord models/associations or schema design. Though it pays to plan ahead, I wouldn't worry about that excessively until you are sure that the application will get enough users to invest the time in that.
Even though ActiveRecord has some caching, you could just avoid the DB and cache friends in memory yourself in the beginning for speed, especially if you don't yet have many users, which you probably don't yet. If you think you'll run out of memory because of the high number of users, LRU might be a good option here also, and lru_redux looks interesting. Again, you might want to time the cache also so expires and re-gets friends when the cache expires. Even just storing the results in the user session may be adequate, i.e. in the controller action method, just do #friends ||= Something.find_friends(fb_user_id), and the latter is what most might do as a first shot at it while you're getting started.
If you use ActiveRecord, in your query in the controller (or on the association in the model) consider using include: to avoid n+1 queries. That will speed up things.
For the schema design, maybe:
User - users table with email and authN info. Look at the Devise gem.
FacebookUser - info about the Facebook user.
FacebookFriendRelationship - a tie model with (id and) two columns, one for one FacebookUser id and one for the other.
By separating the authN info (User) from the FB data (FacebookUser and FacebookFriendRelationship), you make it easier to have other social media accounts, etc. each with information specific to those accounts in other tables.
The complexity comes in FacebookUser's relationship with friends if the goal is to minimize rows in the relationship table. To half the number of rows, you'd have a single row for a relationship where the id of FacebookUser could be in either foreign key column. Either the user has a friend or is a friend, so you could have two has_many :through associations on FacebookFriend that each use a different foreign key in FacebookFriendRelationship. Or you could do HABTM without the model and use foreign_key and association_foreign_key options in each association. Either way, you could add a method to add both associations together (because they are arrays). Instead, you could use custom SQL in a single has_many if you didn't care about having to use ActiveRecord to remove associations the normal way. However, per your comments, I think you want to avoid this complexity, and I agree with you, unless you really must limit the number of relationship rows. However, it isn't the number of tie table rows that will eat the data, it is going to be all of the user info you keep in the FacebookFriends table.

Related

How to Model Firestore Data?

I'm new to Firestore and trying to develop a data model for my app.
Background: I have a dating type of app with 3 primary ways that users will communicate with one another. Liking, dismissing, and commenting user profiles. Users likes & comments are private. In other words, only I can see who's liked or commented my profile (it's not like Social media where everyone can see who's liked a post). I'll need to be able to query users to know who's dismissed their profile so I won't show it to those users again. I'll also need to know who's liked/commented a users profile so I can query which users have liked/commented each other (they've matched)
Users can like many profiles and vice versa
Users can dismiss/skip many profiles and vice versa
Users can comment on many profiles and vice versa
I believe this means I'll need a root collection for likedUsers, dismissedUsers, and commentedUsers
Problem:
For dismissed users, I thought I'd store every single user as a Document of the dismissedUsers root collection and store every user they've skipped as a field/value pair like so...
dismissedUsers/User/user1, user2, user3, etc
The above would create the many-many relationship I want where dismissedUsers can have many users and users can have many dismissedUsers. However, I don't believe it would be scalable as the User Document would grow too large.
Question: How do I create this many-many relationship where dismissedUsers can have many users and users can have many dismissedUsers so that it's scalable and least expensive? And query it?
First of all I would ask myself why I am using Firestore, being a document database, instead of choosing a relational database. I personally love Firestore and highly recommend it. We pick a document database because it is faster and easier to use in many ways. In other ways it is a drawback because you have very limited query power. It sounds to me like your brain is working towards a relational database implementation.
Here is one solution
First of all I would try to avoid storing user data in more than one location to avoid anomalies (of course right). I would have one collection of users where I stored all user data with a unique id (best to use the one that Firestore assigns so I don't have collisions). Within each users document I would link a subcollection for dismissed, liked, been dismissed by someone else, liked by someone else etc.. I would keep a record of all users (just the user id) that they have dismissed, liked, been dismissed by, been liked by etc.. This way I can look up all data for who that user has liked or disliked and display whatever I want to that user accordingly.
Drawbacks
You will have to write twice per like, dismiss etc. Use a batch write to update both the liked and likee data at the same time.
You don't need a collection of users who liked, dismissed, or commented on another users profile. You can have one user collection which stores all users. Inside each user document you can have three array of the user ids of the users that liked, commented, and/or dismissed a user profile. Just make sure that the document ID of the documents inside the users collection matches the user id of the corresponding user.

Rails: Multiple databases, same schema

I'm in the middle of a fictional scenario project where I have allowed multiple users for a company to log in, create records, and so on, who all connect to the one database. They can all records absence records, attendance records, and so on.
What I want to do however, is use this same schema but expands this to allow several companies to have their own databases using the same schema. So each company will have their own data, but all companies use the same data model. In other words all company's can create absence records, but they each only have access to their own absence records that they created themselves.
How can I achieve this?
All I need is two or three files for this, I'm not going commercial with it in case you guys think I'm cutting corners at someone else's expense!
Something as simple as an if-else that decides which file to use would be very useful to me, so if such a line of code exists please let me know.
I think you are doing it wrong (unless you have a really good reason to have a database for each company), because it seems like you are repeating your data model over and over while introducing unnecessary complexity to your code.
Try to have all the companies in one DB/tables with having separated by the company_id.
Ex: data structure would be as follows
companies table
id
name
users table
id
user_name
company_id
However if you really want to connect to multiple databases, check this SO question.

Should votes be it's own model in RoR app?

Let's say you want to create a Digg.com-like site. Should the votes be its own separate model, or should the votes be a field in the table for the model of the object that is voted on?
It depends on how much information you want to store. If you just have a reference to something and a total score, then you don't need a model. If you have want to store who voted, how many up/down votes were received, timestamp when votes were received, and be able to rollback votes from unruly sources, then you'll need to keep each of those votes as their own model. Personally, I'd make each vote its own record, if I were designing such a system.
Considering Digg.com-like site requirement, I would say - own model. Greatly due to the need of so called "voting rings" detection - detecting groups of fake voters.
Other than that - I would go with fields. MySQL for example can update rows atomically (so they say, never tried it myself), what is supposed to be quite efficient. More information on the MySQL docs.
It depends if you want to keep related vote information or not. This has nothing to do with RoR but with database normalization.
If you want to keep extra information with the votes, like maybe the date it was recorded you should keep it in another table (and as such it will be another model). If not you can store it in the other objects table.

Should a user's profile be a separate model?

I'm learning Rails by building a simple site where users can create articles and comment on those articles. I have a view which lists a user's most recent articles and comments. Now I'd like to add user 'profiles' where users can enter information like their location, age and a short biography. I'm wondering if this profile should be a separate model/resource (I already have quite a lot of fields in my user model because I'm using Authlogic and most of it's optional fields).
What are the pros and cons of using a separate resource?
I'd recommend keeping profile columns in the User model for clarity and simplicity. If you find that you're only using certain fields, only select the columns you need using :select.
If you later find that you need a separate table for some reason (e.g. one user can have multiple profiles) it shouldn't be a lot of work to split them out.
I've made the mistake of having two tables and it didn't buy me anything but additional complexity.
Pros: It simplifies each model
Cons: Managing 2 at once is slightly harder
It basically comes down to how big the user and profile are. If the user is 5 fields, and the profile 3, there is no point. But if the user is 12 fields, and the profile 20, then you definitely should.
I think you'd be best served putting in a separate model. Think about how the models correspond to database tables, and then how you read those for the various use cases your app supports.
If a user only dips in to his actual profile once in a while but the User model is accessed frequently, you should definitely make it a separate object with a one-to-one relationship. If the profile data is needed every time the User data is needed, you might want to stick them in the same table.
Maybe the location is needed every time you display the user (say on a comment they left), but the biography should be a different model? You'll have to figure out the right breakdown, but the general rule is to structure things so you don't have to pull data that isn't being used right away.
A user "owns" various resources on your site, such as comments, etc. If you separate the profile from the user then it's just one more resource. The user is static, while the profile will change from time to time.
Separating it out would also allow you to easily maintain a profile history.
I would keep it separate. Not all your users would want to fill out a profile, so those would be empty fields sitting in your user table. It also means you can change the profile fields without changing any of the logic of your user model.
Depends on the width of the existing user table. Databases typically havea limit to the number of bytes a recird can contain. I fyou are close to (or over which you can usually do if you have lots of fields with null values) the limit, I would add a table with a one-to-one relationship for better performance and less of a likelihood of a record that suddenly can't be inserted as there is too much data for the row size. If you are nowhere near the limit, the add to the exisiting table.

Restricting multiple votes from the same person in a picture rating web application

I'm trying to write a web application in ASP.NET MVC that allows each user to vote for multiple pictures but does not allow them to vote multiple times for the same picture. Users are not authenticated. What should I save in the database or in cookies?
Store the votes in a database table with columns PictureId, UserId, Score, and add a composite unique constraint to the columns PictureId and UserId - this will ensure that there is only a single vote per user and picture.
With anonymous users, you have two options, neither of which are very good:
1) Track the user with a user id stored in a cookie. As long as the cookie persists. the user can't vote twice. However, they can delete or otherwise modify the cookie. They might have cookies turned off. They could have two different browsers open at the same time. Scripts for "cheating" (curl http://site/vote?score=5&pic_id=1) won't store a cookie anyways. Basically, you'll end up with people voting more than they should.
1.5 *
2) Track the user by IP address. This is essentially the opposite. Users can't vote twice, regardless of deleting cookies, switching browsers, etc. However, several people from the same household (using a DSL router) can only vote once combined. Many companies will similarly hide many users behind a single IP address. I think some ISPs do, too (AOL?). You'll end up with far fewer "votes" than legitimately should have been recorded.
So the question is do you want over or under votes? If you think cheating is likely, I'd go for #2. But if cheating is likely, that means there's an incentive. And if people realize their votes aren't counted (which they may not realize), they'll be unhappy.
After that, whether you store each vote as a row, or combine the votes into a single row (update pictures set num_votes = num_votes + 1, total_score = total_score + [submitted score]) is up to you.
1.5 The third option would be to record their vote and an email address, send them the email with a confirmation link and ask them to click it to record their vote. People can still cheat with fake email addresses, but it's not as likely as deleting a cookie.
Database records for unique, authenticated users, as Daniel Brückner suggests, has to be the way forward. Cookies are unreliable as, for example, they can be deleted or a user may use a different browser.
If your users are authenticated, then you can save UserIDs with Image votes.
If your users are anonymous, then systems tend to store their IP address with Image votes. It's not perfect, it's not 100% proof, but it works in majority of situations.

Resources