我有一个拥有500万用户的潜在数据库,每个用户都有一定数量的朋友。我需要做的是实现搜索,这样用户可以按某些属性(即其他用户的性别等)进行过滤,这部分相当简单。
用户A搜索所有男性并获取结果集。用户B,用户C,用户D
但是,用户还应该可以选择按用户A与结果集中所有用户的共同朋友数量对搜索结果进行排序
假设
用户A和用户B = 3个相互朋友
用户A和用户C = 8个相互朋友
用户A和用户D = 5个相互朋友
然后,搜索应按以下顺序对结果进行排序
用户C,用户D,用户A
最重要的是,由于结果将显示在网页上,我也需要支持分页,因此每次用户在搜索结果之间导航时,都不会处理所有500万条左右的条目。
Web代码是用C#编写的,但就数据库而言,我非常开放,可以使用SQL,NoSQL或Redis。主要优先考虑的是高性能,因此Redis似乎是一个不错的选择。如何从数据存储区和伪代码的角度实现这样的搜索要求?
答案 0 :(得分:1)
在redis中,您可能希望将其存储在zset
中;红色中没有内置的自动索引,因此当您添加或删除朋友时,您需要zadd
或zincrby
(zincrby
以减去负数)。然后,您只需zrange
或zrangebyscore
即可查询。这些方法都可以在NuGet上SortedSet
包中的StackExchange.Redis
前缀下使用。
答案 1 :(得分:1)
我的两分钱。
如果您将性别索引保持为Set,则可以利用ZINTERSTORE,并且共同的朋友计为每个用户的SortedSet(分数是共同的朋友数)。 此外,每位用户还需要一组朋友。
对于性别使用,使用密钥格式设置为gender:{gender}
,每个项目都是用户ID。
对于朋友,请使用包含密钥格式的设置:friends:{userId}
,每个项目都是用户ID。
对于共同的朋友,请使用密钥格式为mutual:{userId}
的SortedSet,每个项目都是用户ID,分数将是普通朋友的数量。
因此,在添加用户时:
void AddUser(string user, string gender)
{
// Add to a gender set "gender:{gender}"->{users}
db.SetAdd($"gender:{gender}", user);
}
在两个用户之间添加友谊:
void AddFriendship(string user1, string user2)
{
// All friends of user1, should increment its mutual count with user2
var user1Friends = db.SetMembers($"friends:{user1}");
foreach(var user1Friend in user1Friends)
{
db.SortedSetIncrement($"mutual:{user1Friend}", user2, 1);
db.SortedSetIncrement($"mutual:{user2}", user1Friend, 1);
}
// All friends of user2, should increment its mutual count with user1
var user2Friends = db.SetMembers($"friends:{user2}");
foreach (var user2Friend in user2Friends)
{
db.SortedSetIncrement($"mutual:{user2Friend}", user1, 1);
db.SortedSetIncrement($"mutual:{user1}", user2Friend, 1);
}
// Add to friend sets "friends:{user}->{users}"
db.SetAdd($"friends:{user1}", user2);
db.SetAdd($"friends:{user2}", user1);
}
并按共同朋友的顺序排列用户:
static IEnumerable<string> GetUsersByGenderOrderByMutualFriends(string user, string gender)
{
var db = mux.GetDatabase();
var tempKey = "temp";
db.SortedSetCombineAndStore(SetOperation.Intersect, tempKey, $"gender:{gender}", $"mutual:{user}", Aggregate.Sum);
var result = db.SortedSetRangeByRank(tempKey, 0, -1, Order.Descending).Select(x => x.ToString());
db.KeyDelete(tempKey);
return result;
}