根据共同的朋友推荐朋友

Posted

技术标签:

【中文标题】根据共同的朋友推荐朋友【英文标题】:Suggest friends based off of mutual friends 【发布时间】:2015-11-19 04:07:04 【问题描述】:

我已经在这个网站上四处看了看,但似乎没有一个是我正在寻找的。他们中的很多人都在谈论 Facebook 是如何做到的,或者 Twitter 是如何推荐关注者的,但即便如此,他们也没有给出任何直截了当的答案。 我发现的所有这些都只是使用两个用户 ID 计算共同的朋友。

我希望能够获取登录用户的 ID,通过他们的朋友,并通过他们的朋友来计算与登录用户有最多共同朋友的人,建议添加具有大多数共同的朋友。

我为此使用 phpmysql。我似乎无法理解我将如何做到这一点。

我的友谊表看起来像这样:

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

此表显示 user_id 1 是 2 和 3 的朋友 并且 2 不是 3 的朋友,而是 1 的朋友。

所以,如果用户登录到 user_id 2,我希望它建议 user_id 3,因为他们都是 user_id 1 的朋友。


到目前为止我所拥有的:

  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>";
    
  

它有效,列出了用户朋友的朋友,但没有添加用户... 但是,我无法根据谁拥有最多的共同朋友来对其进行排序,我想这是可以的(尽管我希望可以),但这似乎也不是一种非常有效的方法做吧。。

这似乎需要大量资源,必须通过用户的所有朋友,特别是如果用户添加了几百到一千个朋友,并且他们添加了几百到一千个,等等。

我对高级 SQL 不太熟悉,所以我不知道该怎么做。

【问题讨论】:

所有好友都列出了两次,一次是friend1,一次是friend2 @Sean 是的,对于每个友谊,有两行。 friend1 始终是登录用户,而friend2 始终是其他用户。 (这是为了简单起见,我意识到这可能不是提高效率的最佳方法) @Axiom 你能看到我的回答吗?它返回所有共同的朋友。如果有任何问题,请评论答案。我会帮你的。 【参考方案1】:

这里尝试使用 1 个查询来执行此操作。这个想法是选择friend2 列表,并将其加入到friend2friend1 的选择中。使用GROUP BY,我们可以返回按友谊相关性排序的行,以及与该人成为朋友的每个人。

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;

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

编辑

在我的原始查询中,我忘记排除任何已经与LOGGED_IN_USER 成为朋友的用户。通过使用不存在友谊的LEFT JOINIS NULL,这应该会返回您想要的结果。

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;

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

【讨论】:

在 PHP 中查看这个结果有点令人困惑......似乎它正在返回一个共同朋友的列表等,但我怎么知道哪些没有添加与当前用户?这是我正在尝试的:pastebin.com/JKLyP3nW - 这显示了我添加的一些朋友,而一些我没有。对不起,如果我没有正确地做某事,就像我说我在高级 SQL 查询方面是个菜鸟.. 哈哈 为了更好地参考,这里有一个使用来自实际表的数据的小提琴(它只包含用户 ID,我认为这不是问题) - sqlfiddle.com/#!9/c38b5c/1 - 它应该返回以下 ID:据我所知,24254344138139。我的 ID 是 1 我已通过更新编辑了我的答案。我忘了排除登录用户的当前朋友。此外,sqlFiddle example 不显示24,因为您有(21,24,1),所以pending != 0 你是对的,我没有意识到这一点。非常感谢,效果很好!【参考方案2】:

您可以通过单个 sql 查询来做到这一点。您应该为此编写 sql 内部子查询。查看以下查询。当给定登录用户ID时,它会返回共同的朋友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";

只需传递变量$logged_in_user_id 的登录用户ID。如果你想知道如何编写sql子查询,你可以通过查看Sql Sub Queries link来了解。

以下功能将输出共同好友数据。

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>";
    
  

【讨论】:

虽然我不是反对它的人,但它仍然无法按预期工作。我希望能够使用查询(如果可能)获得建议的朋友。我必须做一些调整才能让它按预期工作,它仍然使用 4 个foreach() 循环来让它与我上面的函数一样工作。 (并且仍然无法按谁拥有最多的相互关系) - 链接到最终功能:pastebin.com/1qFnax4y - 但是据我所知,它似乎工作得更好,因为它从数据库中查询的次数更少。只是不一定是我想要的。不过,谢谢。 这似乎返回了我朋友的朋友列表。我不止一次看到用户使用这个确切的功能。 (切换foreach($data-&gt;results() as $mutuals-&gt;friend2),因为它正在拉动friend2)。这是我的意思的截图,因为我不能很好地解释它:i.imgur.com/k2mSIyA.png |这就是为什么我在 pastebin 链接中添加了额外的 foreach() 循环。 pastebin 链接返回的朋友我还不是朋友,但有共同点。为了清楚起见,我不是在寻找共同点列表,而是根据谁建议登录用户添加的用户列表有最多的互惠生。

以上是关于根据共同的朋友推荐朋友的主要内容,如果未能解决你的问题,请参考以下文章

Postgresql 选择你可能认识的人,按共同朋友的数量排序

推荐系统三十六式-刑无刀

MapReduce课程设计——好友推荐功能

好课推荐:《HTML5+CSS3高级开发系列》

清华博士教你如何用推荐算法技术「找到女朋友」

Steam让你过年在家和朋友“友情升温”的游戏推荐