【问题标题】:MongoDB Social Network Adding FollowersMongoDB 社交网络添加关注者
【发布时间】:2013-08-22 21:19:54
【问题描述】:

我正在 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 个关注者并不是世界末日),我可以将此更新排队。这种方法可以吗,或者任何人都可以提出更好的方法吗?

【问题讨论】:

    标签: mongodb mongodb-query


    【解决方案1】:

    你在正确的轨道上。想想哪个计算执行得更多 - 确定关注者/关注者的数量或更改关注者/关注者的数量?即使您正在缓存 # of follower/following 计算的输出,它仍然会比更改数量多执行一到两个数量级。

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

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

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

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

    【讨论】:

    • 感谢梅森的精彩反馈。我已经实现了选项 1,请看下面。有兴趣听听任何想法:)
    【解决方案2】:

    我已根据 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();
            }
        });
    

    【讨论】:

    • 确保您处理错误情况。例如,如果您的第二次更新失败,那么我们最终会得到单向关注而不是相互关注,并且我们关注的用户的followers 号码会变得不同步。最简单的事情可能是跟踪错误发生的位置,然后尝试回滚您所做的任何事情。您可能还需要设置一个在系统外部运行的后台进程,以确保一切保持一致。
    猜你喜欢
    • 2012-04-07
    • 2010-11-28
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-26
    • 1970-01-01
    相关资源
    最近更新 更多