【发布时间】:2022-01-25 18:04:39
【问题描述】:
$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(跳过+限制),有20个文档进入下一阶段。 $lookup 对 20 个文档进行。减慢我们的流水线,在最后阶段 $skip 丢弃前 10 个文档。在这里,我们浪费了前 10 个文档的工作。
这导致我们的管道出现问题,分页速度变慢。
解决方案:我们最终做的是在 $match 之后移动 $limit 和 $skip。 然后 $limit = 跳过 + 限制,$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
}
问题:
- 这种行为是有意的还是 $skip 和 $limit 有问题?
- 我们提出的解决方案是否正确?它会扩展吗?我认为最后一页有太多的文档清除了 $match 阶段,但这也是 MongoDB 内部做对的事情......如我们案例的第 2 页所示?
【问题讨论】:
标签: mongodb mongoose aggregation-framework