MongoDb 中的存储桶模式是处理大型无界数组的最佳方式吗?

UCA*_*dio 5 arrays pagination mongoose mongodb node.js

我正在为 MERN 堆栈应用程序实现社交功能(关注/取消关注用户),并尝试提出一个良好的 MongoDB 解决方案,以避免潜在的大量无限关注者出现问题。具体来说,我希望避免:

  • MongoDB 必须在磁盘上移动大型追随者数组并随着其变大而重建索引
  • 如果用户拥有大量关注者(> 100 万),则达到 16mb bson 限制
  • 查询/返回关注者以通过分页显示或计算/显示关注者计数时性能缓慢

从我研究的所有内容来看,似乎使用存储桶模式方法是最好的解决方案......我在这方面找到了两篇好文章: https ://www.mongodb.com/blog/post/paging-with-the-桶模式--part-1 https://www.mongodb.com/blog/post/paging-with-the-bucket-pattern--part-2

我开始像这样处理它......追随者模型:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const FollowerSchema = new Schema({
  user: {
    type: Schema.Types.ObjectId,
    ref: 'user',
  },
  // creating an array of followers
  followers: [
    {
      user: {
        type: Schema.Types.ObjectId,
        ref: 'user',
      },
      datefol: {
        type: Date,
        default: Date.now,
      },
    },
  ],
  count: {
    type: Number,
  },
  createdate: {
    type: Date,
    default: Date.now,
    required: true,
  },
});

module.exports = Follower = mongoose.model('follower', FollowerSchema);
Run Code Online (Sandbox Code Playgroud)

在 Node.js api 中更新插入,将关注者添加到数组存储桶中(每个存储桶将包含 100 个关注者):

const follow = await Follower.updateOne(
        { user: req.params.id, count: { $lt: 100 } },
        {
          $push: {
            followers: {
              user: req.user.id,
              datefol: Date.now(),
            },
          },
          $inc: { count: 1 },
          $setOnInsert: { user: req.params.id, createdate: Date.now() },
        },
        { upsert: true }
      );
Run Code Online (Sandbox Code Playgroud)

基本上每次添加关注者时,都会将其添加到找到的第一个包含少于 100 个关注者的存储桶(按计数跟踪)。

这是处理潜在大型数组的最佳方法吗?我的担忧是:

  • 如果有人取消关注某个用户,并且应用程序运行 $pull 来从某个存储桶的数组中删除关注者...那么多个存储桶可能包含少于 100 个关注者。新的关注者将不再添加到最新的存储桶中,因此稍后在查询并尝试根据创建的存储桶返回关注者时...一些最新的关注者可能位于较旧的存储桶中,并且未正确返回。上面的文章提到了 MongoDb 4.2 中引入的一些富有表现力的更新指令来解决这个问题,但我不太清楚如何解决。
  • 如果我通过返回用户的所有关注者桶并按关注日期排序来纠正这一点......如果有人拥有大量关注者,这似乎会变得非常慢
  • 如果我希望能够分页并每页返回 100 个关注者(从最新的开始),这种方法将如何工作?我是否应该向模型添加页码条目,并以某种方式在每次创建存储桶时递增它(第一个存储桶包含页码 1,下一个页码 2 等),然后在前端,如果用户跳转到关注者页面 500,则会运行查询拉桶500?

che*_*npi 0

水桶图案与您所暴露的案例并不完美匹配。

最适合您需求的模式是异常值模式https://www.mongodb.com/blog/post/building-with-patterns-the-outlier-pattern

您的案例实际上与本文中的示例相同。