当前位置:  开发笔记 > 数据库 > 正文

使用MongoDB的类似Twitter的应用程序

如何解决《使用MongoDB的类似Twitter的应用程序》经验,为你挑选了1个好方法。

我正在制作一个使用经典"跟随"机制的应用程序(Twitter和网络上的许多其他应用程序使用的机制).我正在使用MongoDB.不过,我的系统有所不同:用户可以关注用户.这意味着,如果您关注某个群组,您将自动关注该群组成员的所有用户.当然,用户可以属于多个组.

这就是我想出的:

用户A跟随用户B时,用户B的 id被添加到following用户A文档中的嵌入式阵列(被叫)中

为了取消关注,我从following数组中删除了后续用户的id

组以相同的方式工作:当用户A跟随组X时,组X的 id被添加到following数组中.(我实际上添加了一个,DBRef所以我知道连接是针对用户还是组.)

当我必须检查用户A是否跟随组X时,我只是在用户A的后续数组中搜索组的ID .

当我必须检查用户A是否跟随用户B时,事情变得有点棘手.每个用户的文档都有一个嵌入式数组,列出了用户所属的所有组.所以我使用一个$or条件来检查用户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集合,它可以完成嵌入式following文档的相同工作.我尝试过这种方法的问题是,根据$or我之前使用的条件,跟随两个包含相同用户的组的用户将被列出两次.为了避免这种情况,我可以使用group或MapReduce,我实际上做了它并且它可以工作,但我希望避免这样做以保持简单.也许我只需要开箱即用.或者也许我在两次尝试时采取了错误的方法.任何人都必须做类似的事情并提出更好的解决方案?

(这实际上是对我这个老问题的后续跟进.我决定发布一个新问题来更好地解释我的新情况;我希望这不是问题.)



1> Niels van de..:

您有两种可能的方式来让用户跟随另一个用户; 直接或间接通过组,在这种情况下,用户直接跟随组.让我们从存储用户和组之间的这些直接关系开始:

{
  _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" ] },
{ ownerId: "userA", userId: "userD", origins: [ "groupX", "groupY" ] },
{ ownerId: "userA", 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, userId: userId } , { 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);
}

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

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

几点说明:

在为该用户运行map-reduce之前,您应该删除用户以前的订阅.

如果更新组,则应对该组后面的所有用户运行map-reduce.

我应该注意到这些map-reduce函数比我想象的复杂得多,因为MongoDB不支持数组作为reduce函数的返回值.理论上,函数可以简单得多,但与MongoDB不兼容.但是,users如果您需要,可以使用这个更复杂的解决方案在一次调用中映射 - 减少整个集合.

推荐阅读
小白也坚强_177
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有