数据库设计 – 使用MongoDB的类似Twitter的应用程序

前端之家收集整理的这篇文章主要介绍了数据库设计 – 使用MongoDB的类似Twitter的应用程序前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在制作一个应用程序,它使用经典的“跟随”机制(Twitter使用的)和网络上的许多其他应用程序.我正在使用MongoDB.
不过,我的系统有所不同,用户可以跟随用户群.这意味着,如果您遵循一个群组,您将自动跟踪该群组成员的所有用户.当然,用户可以属于多个组.

这是我想出来的:

>当用户A遵循用户B时,用户B的ID被添加用户A的文档中的嵌入式数组(称为以下)
>为了取消关注,我从以下数组中删除跟随用户的id
>组的工作方式相同:当用户A遵循组X时,组X的id将被添加到以下数组中. (我实际上添加了一个DBRef,所以我知道连接是一个用户还是一个组.)
>当我必须检查用户A是否遵循组X,我只是在用户A的以下数组中搜索组的id.
>当我必须检查用户A是否遵循用户B,事情会变得有点棘手.每个用户的文档都有一个嵌入式数组,列出用户所属的所有组.所以我使用$或条件来检查用户A是直接或通过组跟随用户B.喜欢这个:

db.users.find({'$or':{'following.ref.$id':$user_id,'following.ref.$ref','users'},{'following.ref.$id':{'$in':$group_ids},'following.ref.$ref':'groups'}}})


这很好,但我想我有几个问题.例如,如何显示特定用户的关注者列表,包括分页?我不能在嵌入式文档上使用skip()和limit().

我可以改变设计并使用一个userfollow集合,它将执行与嵌入的以下文档相同的工作.我尝试的这种方法的问题是,在我以前使用的$或条件下,包含相同用户的两个组之后的用户将被列出两次.为了避免这种情况,我可以使用组或MapReduce,我实际上是这样做的,但是我很乐意避免这样做,使事情变得更简单.也许我只需要开箱即用.或者也许我尝试错误方法.任何人都必须做类似的事情,并提出一个更好的解决方案?

(这实际上是我的this older question的后续行动,我决定发表一个新的问题来更好地解释我的新情况,希望不是问题.)

解决方法

您有两种可能的方式,用户可以跟随另一个用户;直接或间接地通过组,在这种情况下,用户直接跟随组.我们开始在用户和组之间存储这些直接关系:
{
  _id: "userA",followingUsers: [ "userB","userC" ],followingGroups: [ "groupX","groupY" ]
}

现在,您可以直接或间接地快速找出用户A所关注的用户.为了实现这一点,您可以对用户A所关注的组进行非规范化.假设X和Y组定义如下:

{
  _id: "groupX",members: [ "userC","userD" ]
},{
  _id: "groupY",members: [ "userD","userE" ]
}

基于这些组和用户A具有的直接关系,您可以在用户之间生成订阅.订阅的来源与每个订阅一起存储.对于示例数据,订阅将如下所示:

// abusing exclamation mark to indicate a direct relation
{ ownerId: "userA",userId: "userB",origins: [ "!" ] },{ ownerId: "userA",userId: "userC",origins: [ "!","groupX" ] },userId: "userD",origins: [ "groupX","groupY" ] },userId: "userE",origins: [ "groupY" ] }

您可以轻松地生成这些订阅,对单个用户使用map-reduce-finalize调用.如果一个组被更新,您只需要重新运行所有跟随该组的用户的map-reduce,并且订阅将再次被更新.

的map-reduce

以下map-reduce功能生成单个用户订阅.

map = function () {
  ownerId = this._id;

  this.followingUsers.forEach(function (userId) {
    emit({ ownerId: ownerId,userId: userId },{ origins: [ "!" ] });
  });

  this.followingGroups.forEach(function (groupId) {
    group = db.groups.findOne({ _id: groupId });

    group.members.forEach(function (userId) {
      emit({ ownerId: ownerId,{ origins: [ group._id ] });
    });
  });
}

reduce = function (key,values) {
  origins = [];

  values.forEach(function (value) {
    origins = origins.concat(value.origins);
  });

  return { origins: origins };
}

finalize = function (key,value) {
  db.subscriptions.update(key,{ $set: { origins: value.origins }},true);
}

然后,您可以通过在userA中指定查询来为单个用户运行map-reduce.

db.users.mapReduce(map,reduce,{ finalize: finalize,query: { _id: "userA" }})

几个注释:

>在为该用户运行map-reduce之前,应删除用户的以前订阅.
>如果您更新一个组,则应为该组后面的所有用户运行map-reduce.

我应该注意,这些map-reduce函数证明比我想到的更复杂,因为MongoDB不支持数组作为reduce函数的返回值.在理论上,功能可以简单得多,但与MongoDB不兼容.然而,如果您有必要,这个更复杂的解决方案可用于映射 – 在单个调用中减少整个用户集合.

猜你在找的MongoDB相关文章