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
}
问题:
- 此行为是故意的还是 $skip 和 $limit 有问题?
- 我们想出的解决方案是否正确?它会扩展吗?正如我认为最后一页有太多文档清除了 $match 阶段,但这也是 MongoDB 内部做对的事情......如我们案例的第 2 页所示?
是的,您的解决方案就是要走的路:
- $skip 只忽略管道给它的设定数量
- $limit 实际上只有 returns 告知的金额。
所以你所做的链接转化为这句话:
我们限制在 20 个,同时跳过前 10 个结果以获得第二组 10 个文档。
如果你说你限制在 10 个,然后通过跳过忽略 10 个,你就没有剩余文件了。
对于您的第二个问题,是的,它会缩放,请继续使用如下格式:
const baseLimit = 10
const skip = 10
const limit = baseLimit + sort
$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
}
问题:
- 此行为是故意的还是 $skip 和 $limit 有问题?
- 我们想出的解决方案是否正确?它会扩展吗?正如我认为最后一页有太多文档清除了 $match 阶段,但这也是 MongoDB 内部做对的事情......如我们案例的第 2 页所示?
是的,您的解决方案就是要走的路:
- $skip 只忽略管道给它的设定数量
- $limit 实际上只有 returns 告知的金额。
所以你所做的链接转化为这句话:
我们限制在 20 个,同时跳过前 10 个结果以获得第二组 10 个文档。
如果你说你限制在 10 个,然后通过跳过忽略 10 个,你就没有剩余文件了。
对于您的第二个问题,是的,它会缩放,请继续使用如下格式:
const baseLimit = 10
const skip = 10
const limit = baseLimit + sort