我有一个包含大约 30 个属性的集合,其中 2 个属性是与其他 2 个集合的关系 (ObjectId)。我需要能够按 2 个子集合的列对父集合进行排序。
为此,我编写了一个如下所示的管道:
Document.aggregate([
{
$match: {
deleted: false,
},
},
{
$lookup:
{
from: 'companies',
localField: 'company',
foreignField: '_id',
as: 'company',
},
},
{
$unwind:
{
path: '$company',
preserveNullAndEmptyArrays: true,
},
},
{
$lookup:
{
from: 'suppliers',
localField: 'supplier',
foreignField: '_id',
as: 'supplier',
},
},
{
$unwind:
{
path: '$supplier',
preserveNullAndEmptyArrays: true,
},
},
{
$sort: { 'company.name': -1 },
},
{
$skip: 0,
},
{
$limit: 10,
}
]);
问题是这太慢了。在 Compass 中,它超出了时间限制,当我在节点中运行它时,大约需要 10 秒才能处理。父集合的大小约为 60k。如果没有排序阶段,它的运行时间约为 25 毫秒。另外,当我将排序阶段移动到管道顶部时,它运行得非常快,但如果我想按“company.name”属性排序,结果不正确。父集合中的“company”和“supplier”属性均已编入索引。
我读过,为了让排序阶段使用索引,需要将其与“匹配”阶段一起放在管道的开头,但这并不能满足我按属性排序的要求子集合。不可能在 100 毫秒内完成这个运行吗?我错过了什么?
编辑1
解释如下:
{ stages:
[ { '$cursor':
{ query: { state: true },
queryPlanner:
{ plannerVersion: 1,
namespace: 'test.documents',
indexFilterSet: false,
parsedQuery: { state: { '$eq': true } },
winningPlan:
{ stage: 'FETCH',
inputStage:
{ stage: 'IXSCAN',
keyPattern: { state: 1 },
indexName: 'state_1',
isMultiKey: false,
multiKeyPaths: { state: [] },
isUnique: false,
isSparse: false,
isPartial: false,
indexVersion: 2,
direction: 'forward',
indexBounds: { state: [ '[true, true]' ] } } },
rejectedPlans: [] } } },
{ '$lookup':
{ from: 'companies',
as: 'company',
localField: 'company',
foreignField: '_id',
unwinding: { preserveNullAndEmptyArrays: true } } },
{ '$lookup':
{ from: 'suppliers',
as: 'supplier',
localField: 'supplier',
foreignField: '_id',
unwinding: { preserveNullAndEmptyArrays: true } } },
{ '$sort': { sortKey: { 'company.name': -1 }, limit: 10 } } ],
ok: 1 }
文档集合中,
company
、supplier
、state
字段都是索引。
我最近遇到了类似的超时问题,我在这种情况下所做的并且对我有用的是将查找对象投影为这样: 就像我们的案例中的 company.name 一样: @project: { 名称 = $company.name } 现在对此名称属性进行排序,它不会显示错误。这很奇怪,即使它应该直接作用于查找对象,但这仍然对我有用。