【发布时间】:2022-01-27 22:12:06
【问题描述】:
我有一组用户(约 8k 文档)和一组作业(约 150k 文档)。我需要使用一个阶段创建一个聚合,该阶段可以找到每个用户被接受的工作(在工作模型中接受的是一个 ObjectIds 数组):
db.getCollection('users')
.aggregate([
{
$lookup: {
from: 'jobs',
as: 'jobs',
let: { accepted: '$_id' },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $gte: ['$timestamp', 1643223600] },
{ $in: ['$$accepted', '$accepted'] }
]
}
},
},
]
}
},
])
这种聚合非常慢。即使将输出记录的数量限制为 10 也需要大约 1 分钟才能完成。虽然,时间戳大于 1643223600 的作业数量并不多——大约是 3000 个文档。我添加了以下索引:
{timestamp: -1},
{accepted: 1}
但这无济于事。但是,如果我创建数据库的副本并删除所有不满足时间戳条件的班次,则聚合工作得更快(几秒钟)。这是否意味着索引由于某种原因不起作用?如果在作业集合上运行一个简单的查询,并在接受字段上加上一个条件的解释,则表明执行了 IXSCAN
UPD:由于 $in 不适用于复合索引,Mongodb4.4 的解决方法是:
{
$lookup: {
from: 'jobs',
as: 'jobs',
localField: '_id',
foreignField: 'accepted'
}
},
{ $match: { $expr: { $ne: [{ $size: '$jobs' }, 0] } } },
{ $unwind: '$jobs' },
{
$match: { 'jobs.timestamp': { $gte: 1643223600 } }
},
【问题讨论】:
标签: node.js mongodb mongoose indexing aggregation