Question

I'm trying to design a data model that denotes one user being the friend of another user. This is what i've come up with so far, but it seems clunky, is there a better solution?

User
=====
Id
Name
etc...

UserFriend
===========
UserId
FriendId
IsMutual
IsBlocked
Was it helpful?

Solution

UserRelationship
====
RelatingUserID
RelatedUserID
Type[friend, block, etc]

Agree that mutuality doesn't belong as a column; breaks normalization.

OTHER TIPS

To go one record per two users and avoid consuming extra memory that the proposed methods suggest (twice as much as needed, since there are two records for each user), you can do the following:

  1. Table structure:

    USER_RELATIONSHIP {
        user_first_id,
        user_second_id,
        type
    
        primary key(user_first_id, user_second_id)
    }
    
  2. Ensure: user_first_id < user_second_id

  3. The most interesting part - type: for all of the possible states of a relationship, you create the corresponding values. For exmaple:

    pending_first_second
    pending_second_first
    friends
    block_first_second
    block_second_first
    block_both
    

What you have:

  1. The user_first_id < user_second_id ensures that there is only one record of a relationship between two given users, since this and primary key constraints won't allow placing it otherwise.
  2. By proper switching between the relationship states, you determine how each of two users relate to each other. If there is no relationship between two users, there is no record.
  3. To find out the relationship between two users (as well as update), you simply go by a single query:

    select * from USER_RELATIONSHIP where
        user_first_id = user1_id and
        user_second_id = user2_id;
    

    without an or statement that would check this columns vice versa, which is faster.

Example scenario:

  1. no record : are not in a relationship

  2. pending_first_second : the first made a friend request to the second

  3. friends : the second approved the friend request

  4. no record : one of the users removed the other from his firends

This solution is efficient in terms of both memory and speed, because you create, store and update only one single record.

I am currently building a social networking site for a client and I expressed things this way

CREATE TABLE [dbo].[PersonFriend] (
    [Id]                          INT            IDENTITY (1, 1) NOT NULL,
    [Timestamp]                   DATETIME       NOT NULL,
    [ChangeUser]                  NVARCHAR (200) NOT NULL,
    [FriendStatusId]              TINYINT        NOT NULL,
    [Person1Id]                   INT            NOT NULL,
    [Person2Id]                   INT            NOT NULL,
    [Person1RequestTimestamp]     DATETIME       NOT NULL,
    [Person2AcknowledgeTimestamp] DATETIME       NULL
);

Each person is stored in the Person table (imagine that). The Person1Id and Person2Id fields are FK to the person table. I keep a status list in the FriendStatus table for covering whether something has been request, accepted, denied, ignored etc. The Timestamp field is standard in my design to indicate record creation (it is a pattern thing that is used in by base persistence class) and its kind of duplicated in this table as the Person1RequestTimestamp contains the same data. I also capture when the Person2 saw the request and made an action (which gets indicated in FriendStatusId) on it and store that in the Person2AcknowledgeTimestamp).

One of the core assumptions of this design can be stated that Person1 requested friendship of Person2 - if that friendship is accepted then the friendship is considered mutual.

I'd do something similar to what you have, but remove the "IsMutual" flag. Simply add a second row with inverse values when it is mutual. It does add rows, but feels a lot cleaner.

Perhaps add a Relationship table, put the relationship properties there, and reference it from UserFriend.

Probably over the top but one can use the semantic web to model this. One can use the FOAF (FOAF Friend of a Friend)-format.

I did it like this :

TABLE user

id  name
-----------
1   foo
2   roo
3   shoo
4   mooo

TABLE friend relation

id   u_id f_id
--------------
1    1    2
2    2    1
3    3    1
4    1    3

Everytime friend request accepted insert reverse manner 1 2 & 2 1 and simple query:

$ufq=mysql_query("SELECT t1.f_id,t2.id,t2.name FROM friends AS t1, user AS t2 WHERE t1.u_id='$u_id' AND t2.id=t1.f_id ORDER BY t2.name ")or die(mysql_error());
while($fn=mysql_fetch_array($ufq)){
    echo $fn["name"].'<br>';
}

What do you think about my solution?

You have 3 tables

1 **the user** (id,.etc)
2 **friend_request**(id,flaggerID,flaggedID)
3 **friend**(id,frienderID,friendedID)

You sign in, check if I'm in friend table, if yes list friends (I'm in friender>list friended) (I'm in friended>listfriender)

Do I have request? (Am I in flaggedID?) Know him? If not, delete record; if yes, create new record in request as me get putted in flagger and the flagger get in flagged. Now we have mutuality between 2 records in request table so we delete both and put them in friend table. Easy to separate req/friends.

Friendships are less clear cut than the classic employer/boss and user/spouse self-join scenarios. Is friendship a relationship or an activity? I've received a fair amount of criticism for neglecting the latter. Either way, you're probably going to need more than one table, no matter how generic your data model is.

Do you actually need a physical table to discover if there are mutual friends? Why not do a SQL query like:

SELECT U.ID, U.NAME FROM USER U
INNER JOIN USERFRIEND UF
ON U.ID = UF.FRIENDID
WHERE U.ID = (SELECT USER.ID FROM USER
            WHERE USER.ID = friend_id AND USER.ID != your_id);

The query's results should return all of the mutual friends.

I think you should be create two table:

1. user
u_id int
u_username string
balahhh............

2. friendship
fs_id int
relating_id int
related_id int

Well, I'm late to the party, but here is my bid.

First the tables:

User
-id

Type
-id

Relationship
-user_id_1
-user_id_2
-type_id

Now, I want to keep my type table simple. So the types that I add only represent a single user's relationship with another. They never represent the bidirectional relationship. For example:

friend
ignored

This makes adding new types easy. I don't have to think about or create all possible combinations of all my types. I just add the new type.

To setup a friendship, you would need 2 entries into the relationship table. If both users agree that they are friends, they are friends. If only one say he is friends with the other, and the other has him blocked, they are not friends.

Making queries is very simple. Here's how you get all the friends in MySQL:

SELECT u.* FROM user u
LEFT JOIN relationship r1 ON u.id = r1.user_id_2
LEFT JOIN relationship r2 ON u.id = r2.user_id_1
WHERE r1.user_id_1 = <my_user_id> # my relationship with them
AND r1.type_id = <friend_type_id> # i say i'm a friend
AND r2.user_id_2 = <my_user_id> # their relationship with me
AND r2.type_id = <friend_type_id> # they say they're friends

I also think this approach is more "transaction safe". Imagine you send a friend request to someone and then block that person afterwards. If that person later accept the friend request, it doesn't matter. It doesn't change the state of the relationship, because they are completely independent.

If you instead had a single type that represented the bidirectional relationship, you would be forced to make some sort of evaluation in your code, of what exactly the new state of the relationship should be, once the friend accepted the friend request. If you don't, you could end up unblocking a user and making that person friends with the user he or she had blocked.

I would much rather handle this on database-level. If you have a bunch of programmers working on the application it doesn't take long before someone forgets this issue somewhere and a hard-to-find bug is created.

From my understanding Friendship is a result of a relation between two users (User1 and User2), and because a user1 can have 0 or many user(s) as friend(s) and vice-versa user2, therefore a junction table "Friend" commes in the middle to represent that relationship like this :

User1 id(int) username(string)

User2 id(int) username(string)

Friend ---- id(int) user1_Id(int) user2_Id(int) active(bool)

Both user1_Id and user2_Id are FKs in Frind Table

I hope i am right.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top