0

我正在 MongoDB 中实现一个社交网络,我需要跟踪每个用户的关注者和关注者。当我搜索用户时,我想显示一个像 Facebook 这样的列表,其中包含用户名、图片以及关注者和关注者的数量。如果我只想显示用户名和图片(不会更改的信息),这很容易,但我还需要显示关注者和关注者的数量(变化相当规律)。

我目前的策略是将用户关注的人员嵌入到每个用户文档中:

firstName: "Joe",
lastName: "Bloggs",
follows: [
 {
   _id: ObjectId("520534b81c9aac710d000002"),
   profilePictureUrl: "https://pipt.s3.amazonaws.com/users/xxx.jpg",
   name: "Mark Rogers",
 },
 {
   _id: ObjectId("51f26293a5c5ea4331cb786a"),
   name: "The Palace Bar",
   profilePictureUrl: "https://s3-eu-west-1.amazonaws.com/businesses/xxx.jpg",
 }
]

问题是 - 跟踪每个用户的关注者和关注者数量的最佳策略是什么?

如果我将关注/关注的数量作为嵌入文档的一部分,即

follows: [
{
  _id: ObjectId("520534b81c9aac710d000002"),
  profilePictureUrl: "https://pipt.s3.amazonaws.com/users/xxx.jpg",
  name: "Mark Rogers",
  **followers: 10,**
  **following: 400**
}

那么每次用户关注某人时,都需要对所有嵌入文档进行多次更新。

由于这些数据的一致性并不是很重要(即向某人展示我有 10 个而不是 11 个关注者并不是世界末日),我可以将此更新排队。这种方法可以吗,或者有人可以提出更好的方法吗?

4

2 回答 2

1

你在正确的轨道上。想想哪个计算执行得更多——确定追随者/追随者的数量还是改变追随者/追随者的数量?即使您正在缓存 # of follower/following 计算的输出,它仍然会比更改数量更频繁地执行一两个数量级。

另外,想想相反的情况。如果您确实需要为每个用户显示关注者/关注者的数量,那么您必须对每次加载进行聚合(或将其缓存在某处,但您仍在进行大量计算)。

选项 1:缓存嵌入文档中的关注者/关注者数量。
优点:可以在 O(1) 时间内显示统计数据
缺点:需要 O(N) 时间来关注/取消关注

选项 2:计算每个页面视图上的关注者/关注者数量(或缓存失效)
优势:可以在 O(1) 时间内关注/取消关注 劣势
:需要 O(N) 时间才能显示

再加上关注者/关注者的统计数据最终可以保持一致,而计数必须按需显示,我认为缓存它是一个非常容易的决定。

于 2013-08-20T17:56:41.663 回答
0

我已经根据 Mason 推荐的相同策略(选项 1)继续实施更新关注者/关注者。这是我在 NodeJs 和 Mongoose 中的代码,并使用 AsyncJs 瀑布模式以防万一有人感兴趣或有任何意见。我还没有实现排队,但计划是将其中的大部分集中到一个队列中。

async.waterfall([

        function (callback) {

            /** find & update the person we are following */
            Model.User
                .findByIdAndUpdate(id,{$inc:{followers:1}},{upsert:true,select:{fullName:1,profilePictureUrl:1,address:1,following:1,followers:1}})
                .lean()
                .exec(callback);
        },
        function (followee, callback) {

            /** find & update the person doing the following */
            var query = {
                $inc:{following:1},
                $addToSet: { follows: followee}
            }

            Model.User
                .findByIdAndUpdate(credentials.username,query,{upsert:true,select:{fullName:1,profilePictureUrl:1,address:1,following:1,followers:1}})
                .lean()
                .exec(function(err,follower){
                    callback(err,follower,followee);
                });

        },
        function(follower,followee,callback){

            /** update the following count */
            Model.User
                .update({'follows._id':follower.id},{'follows.$.following':follower.following},{upsert:true,multi:true},function(err){
                    callback(err,followee);
                });
        },
        function(followee,callback){
            /** update the followers count */
            Model.User
                .update({'follows._id':followee.id},{'follows.$.followers':followee.followers},{upsert:true,multi:true},callback);
        }
    ], function (err) {
        if (err)
            next(err);
        else {
            res.send(HTTPStatus.OK);
            next();
        }
    });
于 2013-08-20T21:01:34.843 回答