根据共同好友数查询推荐好友?

时间:2021-02-10 02:16:01

标签: mysql sequelize.js

我想根据共同好友的数量来推荐用户,就像这样。

Suggested Friends:

Amy Adams (42 mutual friends)
Brian Bautista (21 mutual friends)
Chris Cross (6 mutual friends)

注意:我已经阅读了几个类似的帖子和答案,但是解决方案非常依赖于表结构,而且我无法解决我们的 Friendships 表的设置方式。见下文。

USERS TABLE
id
name

FRIENDSHIPS TABLE
id
initiatingUserId (FK to Users Table)
targetUserId (FK to Users Table)
status ('active', 'denied', 'pending')

如您所见,每个好友都占一行。发送好友请求的用户为发起用户,接受好友请求的用户为目标用户。这两列都有索引。

如何有效地获取当前用户还不是好友的用户列表,以及他们共同的好友数量?

这是一个示例数据集,根据@Strawberry 的要求...

CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `firstName` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `lastName` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `friends` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `initiatingUserId` int(10) unsigned NOT NULL,
  `targetUserId` int(10) unsigned NOT NULL,
  `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
  PRIMARY KEY (`id`),
  UNIQUE KEY `compositeInitiatingUserIdTargetUserIdIndex` (`initiatingUserId`,`targetUserId`),
  KEY `friends_initiating_user_id` (`initiatingUserId`),
  KEY `friends_target_user_id` (`targetUserId`),
  CONSTRAINT `friends_ibfk_1` FOREIGN KEY (`initiatingUserId`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `friends_ibfk_2` FOREIGN KEY (`targetUserId`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
);

INSERT INTO `users` (`id`, `firstName`, `lastName`) VALUES 
(1, 'A', 'A'),
(2, 'B', 'B'),
(3, 'C', 'C'),
(4, 'D', 'D'),
(5, 'E', 'E'),
(6, 'F', 'F'),
(7, 'G', 'G');

INSERT INTO `friends` (`id`, `initiatingUserId`, `targetUserId`, `status`) VALUES 
( 1, 1, 2, 'active'),
( 2, 1, 3, 'active'),
( 3, 4, 1, 'active'),
( 4, 2, 3, 'active'),
( 5, 2, 4, 'active'),
( 6, 5, 2, 'active'),
( 7, 2, 6, 'active'),
( 8, 2, 7, 'active'),
( 9, 3, 5, 'active'),
(10, 6, 3, 'active'),
(11, 4, 5, 'active'),
(12, 6, 7, 'active');

...和SQL Fiddle of same

假设我们为用户 ID 1 提取建议用户,所需的输出是:

+----------------+-------------+
| strangerUserId | mutualCount |
+----------------+-------------+
| 5              | 2           |
+----------------+-------------+
| 6              | 2           |
+----------------+-------------+
| 4              | 1           |
+----------------+-------------+
| 7              | 1           |
+----------------+-------------+

我确实有一个解决方案,但它假设我们已经拥有用户朋友的 ID,我不确定它的运行速度:

select
count(*) as 'mutualCount',
case when f.initiatingUserId in (2, 3) then f.targetUserId else f.initiatingUserId end as strangerUserId
from friends f
    join users initiating on f.initiatingUserId=initiating.id
    join users target on targetUserId=target.id
and (
(
    initiatingUserId in (2, 3)
    and targetUserId not in (1, 2, 3)
)
or 
(
    targetUserId in (2, 3)
    and initiatingUserId not in (1, 2, 3)
)
)
group by strangerUserId
order by mutualCount desc;

1 个答案:

答案 0 :(得分:0)

我想出了这个快速(仅限 MySql)解决方案。

<块引用>

注意:此查询未针对您现有的索引进行优化,总体而言,在较大的数据集上可能会出现性能问题。如果您需要添加一些额外的条件,也很难修改查询。我会使用查询的各个部分先拉朋友列表,然后是非朋友列表,然后运行单独的查询来确定共同朋友的数量。

因此,这里是拉取 userId '1' 的共同好友计数的非好友列表的查询。您必须使用动态变量替换查询中的“1”硬编码 userId 值:

SELECT COUNT(fnf.friendOfNonFriendId) AS mutualFriednCount, fnf.nonFriendId
 FROM
    -- Subquery to pull the list of non-friends and their friends
    (SELECT
        IF(f.initiatingUserId = nf.nonFriendId, f.targetUserId, f.initiatingUserId) AS friendOfNonFriendId,
        nf.nonFriendId
    FROM friends f
    JOIN
        (SELECT u.id AS nonFriendId
        FROM users u
        LEFT JOIN (
            SELECT IF(f.initiatingUserId = 1, f.targetUserId, f.initiatingUserId) AS friendId
            FROM friends f
            WHERE f.status = 'active' AND (f.initiatingUserId = 1 OR f.targetUserId = 1)) f
                                           ON f.friendId = u.id
        WHERE f.friendId IS NULL AND u.id != 1) nf ON nf.nonFriendId = f.initiatingUserId
          OR  nf.nonFriendId = f.targetUserId) fnf
 -- Joining the subquery with the list of friends of a user with ID 1 to filter the above subquery to only mutual friends 
 JOIN 
    (SELECT IF(f.initiatingUserId = 1, f.targetUserId, f.initiatingUserId) AS friendId
    FROM friends f
    WHERE f.status = 'active' AND (f.initiatingUserId = 1 OR f.targetUserId = 1)) f
                                   ON f.friendId = fnf.friendOfNonFriendId
 GROUP BY fnf.nonFriendId