【问题标题】:Get details of linked collection inside another collection using populate()使用 populate() 获取另一个集合中链接集合的详细信息
【发布时间】:2020-02-25 17:18:53
【问题描述】:

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

[
    {
        "_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) 部分在上面的部分有时间完成之前就被触发了。所以我的问题是,我该如何解决这个问题?总而言之,我希望任务集合中的任务详细信息以及通过 id 链接到任务的每个单独的时间表项。另外,这是使用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);

【问题讨论】:

  • 您可以在问题中添加 TimeSheet、Task 和 TaskDetails 模型吗?
  • @SuleymanSah 添加了。我没有用于 Task 和 TaskDetails 的单独模型,我将所有与任务相关的东西都保存到同一个集合中。
  • 你在一个循环中循环访问db,当出现数百个集合时性能会受到严重影响。

标签: node.js mongodb express asynchronous mongoose


【解决方案1】:

这里发生了一些事情。 1) TaskId 不存在于 Task 集合中,因此填充不起作用(并且没有必要在那里使用它) 2) 您在 ts.TimeSheet.forEach() 中声明的 item 对象将不存在之外forEach() 循环,因此将taskDetails 添加到它不会完成任何事情,因为item 对象在 forEach() 循环完成时被销毁。

我相信你想要的是这样的:

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。

【讨论】:

  • 这很棒,看着它,我有点理解它为什么/如何工作。但是,当我尝试它时,它给了我错误消息“模式尚未注册模型 \"TASK\"。\n使用 mongoose.model(name, schema)”你能告诉我哪里出错了吗?
  • 它告诉你它找不到名为'TASK'的模型。您已在 module.exports = mongoose.model('Task', TaskSchema); 中将模型命名为“Task”(小写),但您在 TimeSheetSchema 中使用了 ref: 'TASK'(大写)。将其更改为ref: 'Task',它应该会找到它
  • 现在一切都是空白的,它没有返回任何数据。您能否解释一下您在find() 步骤中做了什么?我查看了官方的 Mongoose API 文档,找不到字符串类型的第二个参数。
  • 我已重命名对象以避免与 TimeSheet 集合混淆:``` const mongoose = require('mongoose'); var ObjectId = mongoose.Schema.Types.ObjectId; const TimeSheetSchema = mongoose.Schema({ EmployeeId: ObjectId, TimeSheetDate: Date, TimeSheetPerTask: [ { TaskId: { type: ObjectId, ref: 'Task' }, Hours: String, Remarks: String } ] }, { collection: 'EMPLOYEE_TIMESHEET ' }); module.exports = mongoose.model('TimeSheet', TimeSheetSchema); ```
  • [Mongoose find() 文档] (mongoosejs.com/docs/api.html#model_Model.find) 中的一个示例:// executes, name LIKE john and only selecting the "name" and "friends" fields MyModel.find({ name: /john/i }, 'name friends', function (err, docs) { }) 所以字符串包含您要返回的字段。这是可选的。如果您不提供它,将返回 find() 中的所有数据。重命名后,您将希望返回“TimeSheetPerTask”
猜你喜欢
  • 2022-06-30
  • 1970-01-01
  • 2017-05-02
  • 1970-01-01
  • 1970-01-01
  • 2019-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多