使用populate()

时间:2019-10-30 11:56:19

标签: node.js mongodb express asynchronous mongoose

我正在尝试建立一个记录时间表的系统。每天,员工可以针对分配给他们的不同任务记录不同的时间。我的时间表集合中的每个条目都包含一组单独的时间表条目。此集合的样本数据/模式:

[
    {
        "_id": "5db2c672620ed61854818ccd",
        "EmployeeId": "5da9aed442e3070bbd9f7581",
        "TimeSheet": [
            {
                "_id": "5db2c672620ed61854818ccf",
                "TaskId": "5db14152e6537a05258bf573",
                "Hours": "2.5",
                "Remarks": "Test 2.5"
            },
            {
                "_id": "5db2c672620ed61854818cce",
                "TaskId": "5db1886ee6537a05258bf575",
                "Hours": "11.5",
                "Remarks": "Test 11.5"
            }
        ],
        "__v": 0
    }
]

对于相应的Task集合,数据以以下方式驻留-

[
    {
        "_id": "5db14152e6537a05258bf573",
        "EmployeeId": "5da9aed442e3070bbd9f7581",
        "ProjectId": "5db141d9e6537a05258bf574",
        "TaskName": "Finish the timesheet page",
        "TaskDescription": "Write the front-end and back-end code to allow employees to record their timesheets."
    },
    {
        "_id": "5db1886ee6537a05258bf575",
        "EmployeeId": "5da9aed442e3070bbd9f7581",
        "ProjectId": "5db141d9e6537a05258bf574",
        "TaskName": "Learn Populate",
        "TaskDescription": "Learn how MongoDB/Mongoose uses the equivalent of SQL joins"
    },
    {
        "_id": "5db27e3ca2445c05255dbad0",
        "EmployeeId": "5da9aed442e3070bbd9f7581",
        "ProjectId": "5db141d9e6537a05258bf574",
        "TaskName": "Timesheet save API",
        "TaskDescription": "Code the API to save a timesheet to the database"
    }
]

我正在尝试将任务详细信息(任务名称,任务描述等)添加到每个任务时间表条目中。为此,我尝试在控制器中使用populate()方法,就像这样-

exports.findByEmployee = (req, res) => {
    TimeSheet.find({ EmployeeId: req.query.EmployeeId })
        .then(timesheets => {
            timesheets.forEach((ts, ts_index) => {
                ts.TimeSheet.forEach((item, index) => {
                    Task.findById(item.TaskId).populate('TaskId').exec((err, taskDetails) => {
                        item.TaskDetails = taskDetails;
                    })
                });
            })

            res.send(timesheets);

        }).catch(err => {
            res.status(500).send({
                message: err.message || "Some error occurred while retrieving timesheets."
            });
        });
}

但是,API响应(用于获取所有时间表)在各个任务时间表部分中都不包含名为TaskDetails的键。我的猜测是,由于函数调用的异步特性,res.send(timesheets)部分在其上面的部分没有时间完成之前就被触发了。所以我的问题是,我该如何解决?总而言之,我希望Task集合中的Task详细信息具有按ID链接到Task的每个单独的时间表项。另外,这是使用populate()的正确方法,还是有一种更好/更简单/更正确的方法可以解决我的问题?

编辑: 有人要求模型,所以这里是任务和时间表:

const mongoose = require('mongoose');
var ObjectId = mongoose.Schema.Types.ObjectId;

const TaskSchema = mongoose.Schema({
    EmployeeId: ObjectId,
    ProjectId: ObjectId,
    TaskName: String,
    TaskDescription: String
}, { collection: 'TASK' });

module.exports = mongoose.model('Task', TaskSchema);
const mongoose = require('mongoose');
var ObjectId = mongoose.Schema.Types.ObjectId;

const TimeSheetSchema = mongoose.Schema({
    EmployeeId: ObjectId,
    Date: Date,
    TimeSheet: [
        {
            TaskId: {
                type: ObjectId,
                ref: 'TASK'
            },
            Hours: String,
            Remarks: String
        }
    ]
}, { collection: 'EMPLOYEE_TIMESHEET' });

module.exports = mongoose.model('TimeSheet', TimeSheetSchema);

1 个答案:

答案 0 :(得分:1)

这里发生了一些事情。 1)TaskId在Task集合中不存在,因此填充不起作用(也不需要在那里使用)2)您在item中声明的ts.TimeSheet.forEach()对象不会在该forEach()循环之外存在,因此向其添加taskDetails并不会完成任何事情,因为在forEach()循环结束时item对象会被破坏。

我相信您想要的是这样的东西

exports.findByEmployee = (req, res) => {
  try {
    // returns just the TimeSheet object from within the TimeSheet collection 
    // (recommend renaming one of these to avoid confusion!)
    TimeSheet.find({ EmployeeId: req.query.EmployeeId }, 'TimeSheet')
      // populates TaskId (which I recommend renaming 'Task') 
      // with the details from the Task collection
      .populate('TimeSheet.TaskId')
      // executes the query
      .exec(timesheets => {
        // sends the object once the query has finished executing
        res.send(timesheets);
      });
  } catch (err) {
    res.status(500).send({
      message: err.message || 'Some error occurred while retrieving timesheets.',
    });
  }
};

我强烈推荐MDN "Local Library" Express tutorial,以很好地介绍如何使用MongoDB和Mongoose。