Node.js Mongoose虚拟填充和聚合

Node.js Mongoose虚拟填充和聚合,node.js,mongodb,mongoose,mongoose-populate,Node.js,Mongodb,Mongoose,Mongoose Populate,我尝试在使用较新的虚拟填充功能(使用Mongoose4.13、Mongo3.6)的Mongoose模式上进行聚合 假设我有以下模式(为便于说明而简化): 使用.populate()查询项目并填充相关任务工作正常,如: Project.find({projectId:}).populate('tasks'); 但现在我想按项目总结任务的小时数(顺便说一句,将$sum部分保留在下面)。不管怎样,我只会得到空数组。是否可以使用虚拟填充或进行聚合 const result = await Projec

我尝试在使用较新的虚拟填充功能(使用Mongoose4.13、Mongo3.6)的Mongoose模式上进行聚合

假设我有以下模式(为便于说明而简化):

使用.populate()查询项目并填充相关任务工作正常,如:

Project.find({projectId:}).populate('tasks');
但现在我想按项目总结任务的小时数(顺便说一句,将$sum部分保留在下面)。不管怎样,我只会得到空数组。是否可以使用虚拟填充或进行聚合

const result = await Project.aggregate([
   { $match : { projectId: <id> } },
   { $lookup: { 
       from: 'tasks',
       localField: 'projectId',
       foreignField: 'projectId',
       as: 'tasks'
     },
     {
        $unwind: '$tasks' 
     }
   }
 ]);
const result=wait Project.aggregate([
{$match:{projectId:}},
{$lookup:{
来自:“任务”,
localField:'projectd',
foreignField:“projectId”,
作为:“任务”
},
{
$unwind:“$tasks”
}
}
]);

虚拟填充不适用于聚合,因为“返回的文档是纯javascript对象,而不是mongoose文档(因为可以返回任何形状的文档)。”(c),文档中的更多信息-

参考mongoose的文档:

参数不会强制转换到模型的模式,因为$project运算符允许在管道的任何阶段重新定义文档的“形状”,这可能导致文档的格式不兼容

本质上,这意味着当使用聚合时,mongoose允许通过使用模式实现的任何功能都不会被应用。事实上,您需要直接参考MongoDB的文档(特别是)

产出如下:

[{
  _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
    }
  }
]

mongoose>=3.6支持该方法

因此,您可以像这样将聚合方法的
结果
传递给populate方法

var opts=[{path:'tasks'}];
const populatedResult=等待项目。填充(结果,选项);

Hmm。。我在这里提到了对虚拟填充使用$lookup。有什么想法吗?谢谢,非常感谢。我发现了我的问题。我在模式上使用自定义的集合名称,使集合更易于区分。$lookup聚合中的“from”字段指定集合名称,我试图引用的架构名称与本例中的不同。它现在工作得很好!
[{
  _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
    }
  ]
}]
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
    }
  }
]