我正在尝试对使用较新的虚拟填充功能的 Mongoose 架构进行聚合(使用 Mongoose 4.13、Mongo 3.6)。
假设我有以下(出于说明目的而简化的)架构:
const ProjectSchema = new mongoose.Schema({
projectId: Number,
description: String
});
ProjectSchema.virtual('tasks', {
ref: 'Task',
localField: 'projectId',
foreignField: 'projectId'
justOne: false
]);
const TaskSchema = new mongoose.Schema({
taskId: Number,
projectId: Number
hours: Number
});
const Project = mongoose.model('Project', ProjectSchema);
const Task = mongoose.model('Task', TaskSchema);
使用 .populate() 查询项目并填充相关任务工作正常,如下所示:
Project.find({projectId: <id>}).populate('tasks');
但现在我想按项目对任务的时间进行求和(顺便说一句,将 $sum 部分留在下面......)。无论如何,我只会返回空数组。难道不能用 virtual populate 聚合吗?
const result = await Project.aggregate([
{ $match : { projectId: <id> } },
{ $lookup: {
from: 'tasks',
localField: 'projectId',
foreignField: 'projectId',
as: 'tasks'
},
{
$unwind: '$tasks'
}
}
]);
最佳答案
引用 mongoose 的文档 Model.aggregate
:
Arguments are not cast to the model's schema because $project operators allow redefining the "shape" of the documents at any stage of the pipeline, which may leave documents in an incompatible format.
本质上,这意味着 Mongoose 通过使用模式所允许的任何魔力在使用聚合时都不会应用。事实上,您需要直接引用 MongoDB 的文档 aggregation (具体来说 $lookup )。
const ProjectSchema = new mongoose.Schema({
projectId: Number,
description: String
});
const TaskSchema = new mongoose.Schema({
taskId: Number,
projectId: Number,
hours: Number
});
const Project = connection.model('Project', ProjectSchema);
const Task = connection.model('Task', TaskSchema);
const project1 = new Project({ projectId: 1, description: 'Foo'});
const project2 = new Project({ projectId: 2, description: 'Foo'});
const task1 = new Task({ task: 1, projectId: 1, hours: 1});
const task2 = new Task({ task: 2, projectId: 1, hours: 2});
const task3 = new Task({ task: 3, projectId: 2, hours: 1});
Promise.all([
project1.save(),
project2.save(),
task1.save(),
task2.save(),
task3.save()
]).then(() => {
return Project.aggregate([
{ $match: { projectId: 1 }},
{
$lookup: {
from: 'tasks',
localField: 'projectId',
foreignField: 'projectId',
as: 'tasks'
}
}
]).exec();
}).then((result) => {
console.dir(result, { depth: null, color: true });
});
输出以下内容:
[{
_id: ObjectID {/*ID*/},
projectId: 1,
description: 'Foo',
__v: 0,
tasks: [{
_id: ObjectID {/*ID*/},
projectId: 1,
hours: 2,
__v: 0
},
{
_id: ObjectID {/*ID*/},
projectId: 1,
hours: 1,
__v: 0
}
]
}]
但您可能会问:“这基本上就是我所拥有的,那么为什么它会起作用?!”
我不确定您的示例代码是否已复制/粘贴,但在提供的示例中,$unwind
阶段似乎意外包含在第二阶段中($lookup
) 您的管道。如果您将其添加为第三阶段,如以下代码所示,则输出将如图所示进行更改。
return Project.aggregate([
{ $match: { projectId: 1 }},
{
$lookup: {
from: 'tasks',
localField: 'projectId',
foreignField: 'projectId',
as: 'tasks'
}
},
{ $unwind: '$tasks' }
]).exec();
输出:
[{
_id: ObjectID {/*ID*/},
projectId: 1,
description: 'Foo',
__v: 0,
tasks: {
_id: ObjectID {/*ID*/},
projectId: 1,
hours: 1,
__v: 0
}
},
{
_id: ObjectID {/*ID*/},
projectId: 1,
description: 'Foo',
__v: 0,
tasks: {
_id: ObjectID {/*ID*/},
projectId: 1,
hours: 2,
__v: 0
}
}
]
关于node.js - Mongoose 虚拟填充和聚合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47669178/