mongo 数据库聚合中 $sort 和 $limit 的顺序

Order of $sort and $limit in mongo db aggregation

$skip 和 $limit 的顺序在管道中重要吗?

我使用运行操作管道的 mongoose 聚合。最后附加了跳过和限制。

   projectModel.aggregate(pipeline)
  .sort({ updatedAt: -1 })
  .skip(skip)
  .limit(limit)

我的管道看起来像

$match(userId) > $lookup(html_collection) > $lookup(records_collection) > $sort(on updatedAt from mongoose) > $skip(mongoose) > $limit(mongoose)

我在分页期间观察到 $limit 受到尊重,但 $skip 仅在管道末尾发生。例如:

第 1 页:跳过 = 0,限制 = 10

根据 .explain() 清除 $match 阶段的文档数为 10。

第 2 页:跳过 = 10,限制 = 10

通过$match阶段的文档数量为20(skip + limit),20个文档进入下一阶段。 $lookup 是在 20 个文档上进行的。放慢我们的管道,它是在 $skip 丢弃前 10 个文档的最后阶段。在这里,我们浪费了前 10 个文档的工作。

这导致我们的管道出现问题,分页速度变慢。

解决方案:我们最终做的是移动 $limit,然后在 $match 之后移动 $skip。 然后 $limit = skip + limit,$skip = skip。我们认为将文档限制为 limit = skip + limit 将获取文档,而下一阶段的 $skip 将拒绝不需要的文档,从而只为 $lookup 阶段提供预期的文档。

第 1 页:跳过 = 0,限制 = 10 $limit = 0 + 10 = 10 然后 $skip = 0

第 2 页:跳过 = 10,限制 = 10 $limit = 10 + 10 然后 $skip = 10

我们的管道现在看起来像:

$match(userId) > $sort(updatedAt) > $limit(limit + skip) > $skip (skip) > $lookup(html_collection) > $lookup(records_collection)

这里是示例采集架构供大家参考:

    PROJECT COLLECTION     
    {
     id: ObjectId,
     records: [ObjectId],
     userId: ObjectId
    }
     
    RECORDS COLLECTION
     
    {
     id: ObjectId,
     text: string
    }
     
    HTML COLLECTION
     
    {
     id: ObjectId,
     html: string,
     projectId: ObjectId
    }

问题:

  1. 此行为是故意的还是 $skip 和 $limit 有问题?
  2. 我们想出的解决方案是否正确?它会扩展吗?正如我认为最后一页有太多文档清除了 $match 阶段,但这也是 MongoDB 内部做对的事情......如我们案例的第 2 页所示?

是的,您的解决方案就是要走的路:

  • $skip 只忽略管道给它的设定数量
  • $limit 实际上只有 returns 告知的金额。

所以你所做的链接转化为这句话:

我们限制在 20 个,同时跳过前 10 个结果以获得第二组 10 个文档。

如果你说你限制在 10 个,然后通过跳过忽略 10 个,你就没有剩余文件了。

对于您的第二个问题,是的,它会缩放,请继续使用如下格式:

const baseLimit = 10
const skip = 10
const limit = baseLimit + sort