Sven
Sven

Reputation: 6338

mongoose.js: _id of embedded document

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 :-)

Upvotes: 5

Views: 4545

Answers (2)

Sven
Sven

Reputation: 6338

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 :-)

Upvotes: 10

Davin Tryon
Davin Tryon

Reputation: 67296

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)

            });
       });
});

Upvotes: 1

Related Questions