如何在没有竞争条件的情况下使用mongodb实现有序数组?

Min*_*ark 5 optimization mongodb

我是mongodb的新手,也许这是一个微不足道的问题.我有两个mongodb集合:userpost.用户可以创建和关注多个帖子,并按照上次修改日期列出帖子.特定帖子后可能会有大量用户,因此我不想在每个帖子文档中保留关注者列表.另一方面,一个用户可能不会关注超过几千个帖子,因此我决定在每个用户文档中保留关注帖子的对象列表.

为了能够快速列出给定用户的50个最近修改的帖子,我选择保留该last_updated_at字段以及post objectid.

post文件相当基础:

{
    "_id" : ObjectId("5163deebe4d809d55d27e847"),
    "title" : "All about music"
    "comments": [...]
    ...
}
Run Code Online (Sandbox Code Playgroud)

user文件如下:

{
  "_id": ObjectId("5163deebe4d809d55d27e846"),
  "posts": [{
    "post": ObjectId("5163deebe4d809d55d27e847"),
    "last_updated_at": ISODate("2013-04-09T11:27:07.184Z")
  }, {
    "post": ObjectId("5163deebe4d809d55d27e847"),
    "last_updated_at": ISODate("2013-04-09T11:27:07.187Z")
  }]
  ...
}
Run Code Online (Sandbox Code Playgroud)

当用户创建或关注帖子时,我可以简单地在用户文档中$push的帖子ObjectId和列表last_updated_at的末尾posts.修改帖子时(例如,当评论添加到帖子中时),我会last_updated_at在所有关注者的用户文档中更新该帖子的字段.那很重,但我不知道如何避免它.

当我想获得用户的50个最近更新的帖子列表时,我很遗憾地需要获得所有后续帖子的列表,然后last_updated_at在内存中排序,然后仅保留前50个帖子.

所以当我修改帖子时,我尝试更改实现以重新排序列表:我$push到列表的末尾,$pull它来自任何地方.由于这是一个两步程序,因此存在竞争条件,我可能会在列表中获得两倍相同的帖子.有没有更好的方法来维护mongodb中的排序数组?

Ste*_*nie 9

数据模型调整

由于您可能经常更新给定用户的最新帖子,因此您可能希望避免不必要地重写数据以维护已排序数组的开销.

更好的方法是将数据模型展平并使用单独的集合而不是有序数组:

  • 使用更新的帖子流创建单独的集合: (userID, postID, lastUpdated)
  • 更新帖子后,您可以update()使用multi:trueupsert:true选项以及$setlast_updated_at对新值执行简单操作.
  • 要检索给定用户ID的最近50个更新帖子,您可以find()使用排序和限制选项执行常规操作.
  • 要自动清理"旧"文档,您甚至可以为此集合设置TTL到期时间,以便在一定天数后从活动流中删除更新

在MongoDB 2.4中推送到固定大小和排序的数组

如果你想维护有序数组,MongoDB 2.4增加了两个与这个用例相关的有用功能:

  • 能够推送到固定大小的阵列
  • 能够推送到按嵌入文档字段排序的数组

因此,您可以实现推送到按上次更新日期降序排序的50个项目的固定大小数组的结果:

db.user.update(
    // Criteria
    { _id: ObjectId("5163deebe4d809d55d27e846") },

    // Update
    { $push: {
        posts: {
            // Push one or more updates onto the posts array
            $each: [
                {
                    "post": ObjectId("5163deebe4d809d55d27e847"),
                    "last_updated_at": ISODate()
                }
            ],

            // Slice to max of 50 items
            $slice:-50,

            // Sorted by last_updated_at desc
            $sort: {'last_updated_at': -1}
        }
    }}
)
Run Code Online (Sandbox Code Playgroud)

$push将更新排序的顺序列表,以及$slice列表修剪的前50项.由于帖子不是唯一的,您仍然需要$pull先从列表中找到原始文件,例如:

db.user.update(
    // Criteria
    { _id: ObjectId("5163deebe4d809d55d27e846") },

    // Update
    { 
        $pull: {
            posts: { post: ObjectId("5163deebe4d809d55d27e847") }
        }
    }
)
Run Code Online (Sandbox Code Playgroud)

这种方法的一个好处是数组操作正在服务器上完成,但与在应用程序中对数组进行排序一样,您可能仍然需要更新文档.