5

I am trying to save a task to a list of tasks with mongoose and MongoDB. I want to save it redundantly in the tasks collection and in the corresponding list document as embedded document.

It works fine but one little thing: The list's embedded documents don't have their objectIds. But I need them in order to connect them logically with the documents in the tasks-collection.

My Schemas:

var TaskSchema = new Schema({
    _id: ObjectId,
    title: String,
    list: ObjectId

});

var Task = mongoose.model('task', TaskSchema);

var ListSchema = new Schema({
    _id: ObjectId,
    title: String,
    tasks: [Task.schema]
});

var List = mongoose.model('list', ListSchema);

My controller/router:

app.post('/lists/:list_id/tasks', function(req, res) {

        var listId = req.params.list_id;   

        // 1. Save the new task into the tasks-collection.

        var newTask = new Task();
        newTask.title = req.body.title;
        newTask.list = listId; 

        console.log('TaskId:' + newTask._id);  // Returns undefined on the console!!!

        newTask.save(); // Works fine!

        // 2. Add the new task to the coresponding list.

        list.findById(listId, function(err, doc){

            doc.tasks.push(newTask);

            doc.save();  // Saves the new task in the list but WITHOUT its objectId

            });

        res.redirect('/lists/' + listId)

});

Can I use mongoose a different way to achieve that? Or do I have to save the task and then query it before saving it in the list?

Thank you for advise :-)

Sven
  • 6,288
  • 24
  • 74
  • 116

2 Answers2

10

I solved it by using an awesome feature called populate!

Also dtryon was right: You don't need to declare your _id ObjectIds in your models, they are getting added anyways. And you have to nest this stuff because your task is being saved asyncronously so you must get sure that runs before the other stuff.

Here is the solution:

Schemas:

var TaskSchema = new Schema({
    title: String,
    list: { type: Schema.ObjectId, ref: 'list' }

});

var Task = mongoose.model('task', TaskSchema);

var ListSchema = new Schema({
    title: String,
    tasks: [{ type: Schema.ObjectId, ref: 'task' }]
});

var List = mongoose.model('list', ListSchema);

Controller/router:

app.post('/lists/:list_id/tasks', function(req, res) {

    var listId = req.params.list_id;   

    var newTask = new Task();
    newTask.title = req.body.title;
    newTask.list = listId; 


    // WHEN SAVING, WRAP THE REST OF THE CODE

    newTask.save(function (err){
       if (err) {
            console.log('error saving new task');
            console.log(err);
        } else {
            console.log('new task saved successfully'); 

            list.findById(listId), function(err, doc){

                doc.tasks.push(newTask);

                doc.save(function (err){
                    if (err) {
                    console.log('error adding new task to list');
                    console.log(err);
                    } else {

                    console.log('new task saved successfully'); 
                    res.redirect('/lists/' + listId);

                    }  
                });
            });
        });
    });
});

Now it references correctly and with the populate feature you can access the entries very comfortably. Works like charm :-)

Sven
  • 6,288
  • 24
  • 74
  • 116
1

I think this may have to do with async. You may not be seeing the ObjectId because you are calling list.findById potentially before you are pushing the newTask. Further, you will get into trouble doing a res.redirect outside of the async operation as well.

Try this:

app.post('/lists/:list_id/tasks', function(req, res) {

        var listId = req.params.list_id;   

        // 1. Save the new task into the tasks-collection.

        var newTask = new Task();
        newTask.title = req.body.title;
        newTask.list = listId; 

        console.log('TaskId:' + newTask._id);  // Returns undefined on the console!!!

        // WHEN SAVING, WRAP THE REST OF THE CODE
        newTask.save(function (err){
            if (err) {
                console.log(err);
                    // do something
            }

            // 2. Add the new task to the coresponding list.

            list.findById(listId, function(err, doc){

                doc.tasks.push(newTask);

                doc.save();  // Saves the new task in the list but WITHOUT its objectId

                // REDIRECT AFTER SUCCESS
                res.redirect('/lists/' + listId)

            });
       });
});
Davin Tryon
  • 66,517
  • 15
  • 143
  • 132
  • I don't get what you want to accomplish with that :-( But it also doesn't work: Still no ObjectId for embedded documents... – Sven Jun 06 '12 at 12:34
  • 1
    Also, I'm not sure if you need to define the _id on the Schema if you want one generated. Check this out: http://stackoverflow.com/questions/6854431/how-do-i-get-the-objectid-after-i-save-an-object-in-mongoose – Davin Tryon Jun 06 '12 at 13:03