简体   繁体   中英

Suggest friends based off of mutual friends

I've already looked around on this site, but none seem to be exactly what I'm looking for. A lot of them are talking about how Facebook does it, or how Twitter does it to suggest followers, but even then they don't give any straight-forward answer.
All of the ones I find are just calculating mutual friends using two user IDs.

I want to be able to take the logged in user's ID, go through their friends, and go through THEIR friends to calculate the ones who have the most mutual friends with the logged in user, to suggest people to add that have the most mutual friends.

I'm using PHP and MySQL for this. I just can't seem to wrap my head around how I'd do it.

My friendship table looks something like this:

---------------------------------
| friend1 |  friend2  | pending |
---------------------------------
|    1    |     2     |    0    |
|    2    |     1     |    0    |
|    3    |     1     |    0    |
|    1    |     3     |    0    |
---------------------------------

This table is showing that user_id 1 is friends with 2 and 3
and 2 isn't friends with 3, but is friends with 1.

So, if the user was logged into user_id 2, I want it to suggest user_id 3 because both of them are friends with user_id 1.


What I have so far:

  public function friendList($user_id = null){
    if(!$user_id){
      $user_id = $this->_data->user_id;
    }
    $query = "SELECT friend2 FROM user_friends WHERE pending = 0 AND ((friend1 = ".$user_id.") AND (friend2 IN (SELECT user_id FROM users WHERE active = 1 AND user_id = friend2)))";
    $data = $this->_db->hardquery($query);
    return $data->results();
  }
  public function suggestUsers(){
    $user_id = $this->_data->user_id;
    $my_friends = array();
    $suggest_friends = array();
    foreach($this->friendList() as $friend){
      array_push($my_friends,$friend->friend2);
    }
    foreach($my_friends as $friend_id){
      foreach($this->friendList($friend_id) as $friendOfFriend){
        $friendOfFriend = $friendOfFriend->friend2;
        if(!in_array($friendOfFriend,$my_friends) && $friendOfFriend != $user_id){
          array_push($suggest_friends,$friendOfFriend);
        }
      }
    }
    foreach($suggest_friends as $sgf){
      $sgf = new user($sgf);
      $sgf = $sgf->data();
      echo "<a href=\"#\">".$sgf->display."</a><br>";
    }
  }

and it works, listing off friends who are friends of the user's friends, but don't have the user added...
HOWEVER, I can't sort it based off of who has the most mutual friends, which I guess is okay (Though I'd like if I could),
but it also doesn't seem like a very efficient way to do it..

It seems like this would take a lot of resources having to go through all of the user's friends, especially if the user has a couple hundred to a thousand friends added, and they have a couple hundred to a thousand added, etc.

I'm not that familiar with advanced SQL, so I'm not sure how I'd go about doing this.

Here is an attempt at doing this with 1 query. The idea is to select the friend2 list, and join it to a select where the friend2 is friend1 . Using GROUP BY , we are then able to return the rows ordered by the relevance of the friendship, and everyone that is friends with that person.

SELECT 
    a.friend2, 
    COUNT(*) as relevance, 
    GROUP_CONCAT(a.friend1 ORDER BY a.friend1) as mutual_friends 
FROM 
    user_friends a
JOIN 
    user_friends b
ON  (
     b.friend2 = a.friend1
     AND b.pending = 0 
     AND b.friend1 = LOGGED_IN_USER
    )
WHERE 
    a.pending = 0
AND 
    a.friend2 != LOGGED_IN_USER
GROUP BY 
    a.friend2
ORDER BY 
    relevance DESC;

A sqlFiddle example - http://sqlfiddle.com/#!9/3dbf0/3

Edit

In my original query I forgot to exclude any user that is already friends with the LOGGED_IN_USER . By using a LEFT JOIN and IS NULL where a friendship does not exist, this should return your desired result.

SELECT 
    a.friend2, 
    COUNT(*) as relevance, 
    GROUP_CONCAT(a.friend1 ORDER BY a.friend1) as mutual_friends 
FROM 
    user_friends a
JOIN 
    user_friends b
ON  (
     b.friend2 = a.friend1
     AND b.pending = 0 
     AND b.friend1 = LOGGED_IN_USER
    )
LEFT JOIN
    user_friends c
ON
    (
     c.friend2 = a.friend2 
     AND c.pending = 0 
     AND c.friend1 = LOGGED_IN_USER
    )     
WHERE 
    a.pending = 0
AND
    c.friend1 IS NULL
AND 
    a.friend2 != LOGGED_IN_USER
GROUP BY 
    a.friend2
ORDER BY 
    relevance DESC;

updated sqlFiddle example - http://sqlfiddle.com/#!9/c38b5c/2

You Can do that by single sql query. You should write sql inner sub query for this. Look at following query. It returns mutual friend ids when given logged in user id.

$sql= "SELECT `friend2` FROM `user_friends` WHERE `friend1` IN (SELECT `friend2` FROM `user_friends` WHERE `friend1`=$logged_in_user_id) AND `friend2` != $logged_in_user_id";

Just pass the logged in user id for variable $logged_in_user_id . If you want to know how to write sql sub queries, You can learn that by looking Sql Sub Queries link .

Following function will out put mutual friends data.

public function testSuggest(){
    $logged_in_user_id = $this->_data->user_id;
    $suggest_friends = array();
    $sql = "SELECT `friend2` FROM `user_friends` WHERE `friend1` IN (SELECT `friend2` FROM `user_friends` WHERE `friend1`=$logged_in_user_id) AND `friend2` != $logged_in_user_id AND `friend2` IN (SELECT `user_id` FROM `users` WHERE `active`=1 AND `user_id` = `friend2`)";
    $data = $this->_db->hardquery($sql);
    foreach($data->results() as $mutuals){
          array_push($suggest_friends,$mutuals);
    }
    foreach($suggest_friends as $sgf){
      $sgf_user = new user($sgf);
      $sgf_user_data = $sgf_user->data();
      echo "<a href=\"".config::get('site/url')."/u/".$sgf_user_data->username."\">".$sgf_user_data->display."</a><br>";
    }
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM