node.js - Mongoose 虚拟填充和聚合

标签 node.js mongodb mongoose mongoose-populate

我正在尝试对使用较新的虚拟填充功能的 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/

相关文章:

javascript - Node.js Istanbul/Mocha 单元测试不打印详细信息

JavaScript 递归奇怪的行为?

javascript - Handlebars ,访问每个循环变量的父级

node.js - 查找两个文档的日期差异

node.js - npm错误-在终端中找不到npm

mongodb - 获取数组中的第一个元素并使用聚合返回?

java - java - 如何使用java api查询 “like”的mongodb?

mongodb - 是否可以在cosmos mongodb中的多重嵌套字段上创建索引?

node.js - 有效的 mongodb + Mongoose 。架构设计

javascript - 无法让 mongoose-unique-validator 工作