简体   繁体   中英

Mongoose save subdocument array

I'm struggling trying to save a subdocument array. Just wont save:

The front end sends up a Day object that has an array of headings, and each heading can have a content array (which is a mongoose schema).

var DaySchema = mongoose.Schema({
date: Date,
startTime: Date,
endTime: Date,
title: String,
order: Number,
description: String,
cohort: {
    type: objId,
    ref: 'cohort'
},
instructors: [{
    type: objId,
    ref: 'instructor'
}],
headings: [{
    title: String,
    subTitle: String,
    content: [{
        type:objId,
        ref: 'content'
    }]
}]
});


var ContentSchema = mongoose.Schema({
title: String,
description: String,
contentUrl: String,
dueDate: Date,
dateAssigned: Date,
downloadUrl: String
});

This is the code I'm using to PUT and POST a Day, with its associated Content objects. Everything works except for saving the content. When I look in the mongo shell each content field under headings looks like this:

content: []

Any thoughts?

exports.putdaycontent = function (req, res) {

var dayId = req.params.dayId;
var headings = req.body.headings;
var day = req.body;

var updateDay = function () {
    Day.update({_id: dayId}, day, {new: true, upsert: true}, function (err, day) {
        if (err)
            error.databaseError(req, res, err);

        res.send(day);
    });
};

if (headings) {
    for (var x = 0; x < headings.length; x++) {
        var h = headings[x];

        if (h.content) {
            for (var y = 0; y < h.content.length; y++) {
                var c = h.content[y];

                //If existing content update it
                if (c._id && c._id.length > 0) {
                    Content.update({_id: c._id}, c, {new: true,upsert: true}, function (err, content) {

                    });
                } else {
                    var content = new Content(c);
                    h.content[y] = content;
                }

            }
        }
    }
    day.headings = headings;
    updateDay();

} else {
    updateDay();
}


};

exports.postdaycontent = function (req, res) {

var headings = req.body.headings;
var day = req.body;
var cohortId = day.cohort._id;

var postDay = function () {
    var d = new Day(day);
    console.log("CONTENT DAYS:",d);
    d.save(function(err, newDay) {
       if (err)
           error.databaseError(req, res, err);

        Cohort.findOneAndUpdate({_id: cohortId}, {$push: {days: newDay}}, {upsert: true}, function(err, newCohort) {
            res.send({msg:"Successfully saved day to cohort"});
        });
    });
};

if (headings) {
    for (var x = 0; x < headings.length; x++) {
        var h = headings[x];

        if (h.content) {
            for (var y = 0; y < h.content.length; y++) {
                var c = h.content[y];

                var content = new Content(c);
                h.content[y] = content;
                console.log("CONTENT:",content);

            }
        }
    }
    day.headings = headings;
} 

postDay();
};

This is something i faced a few days ago on 22nd june 2015. here is the link to the issue i raised on github https://github.com/Automattic/mongoose/issues/3093 .


Now there are two solutions to your problem.

  1. use .set() explicitly whenever you're modifying an array index

for example:

 doc.someArray.set('3', 'changed'); doc.save();

here the array named someArray is being changed and thus we need to change it using array.set method.

Drawback : each single elementy of the array need to be set using array.set() , so for example, 5 elements inside array change you will need to set all of them using array.set .


2. Mark the array as modified and then save it.

for example:

 doc.array[3] = 'changed'; doc.markModified('array');

This is another way, when you can not explicitly call the array.set method. for example: in my case i used loadash to merge the document and saved it, there was no way i could use array.set , thus i used array.markModified .

Benifit: You will not need to set each of your element inside array as the above array.set method.

Drawback:

In case of each array you will ned to mark it as modified before saving. for example: if you have 10 arrays in your document, you will need to mark them all as modified before saving: like,

 doc.markModified('array1'); doc.markModified('array2'); doc.markModified('array3');


There is still no option in mongoose to mark multiple arrays as modified. I have requested this feature in the link i have shared above.

So that would be something like:

 profile.markAllModified(['arrayFirst','arraySecond','arrayThird','arrayFourth']);

which has been added to milestone 4.0.7 of mongoose, hope we see this feature soon.

So I had assumed that Mongoose would save sub documents so long as they had the _id field - but apparently not. The solution to saving sub-document arrays is to

  1. Create a new Model object for each sub-document in the array from the json
  2. Call save() on each object
  3. The parent object doesn't care if the _id of the sub-document exists or not - it can just store it before I even save the sub-document itself in the database.

Again the fix was just calling .save() on the sub-document.

var content = new Content(c); content.save(); h.content[y] = content;

exports.putdaycontent = function (req, res) {

var dayId = req.params.dayId;
var headings = req.body.headings;
var day = req.body;

var updateDay = function () {
    Day.update({
        _id: dayId
    }, day, {
        new: true,
        upsert: true
    }, function (err, day) {
        if (err)
            error.databaseError(req, res, err);

        res.send(day);
    });
};

if (headings) {
    for (var x = 0; x < headings.length; x++) {
        var h = headings[x];

        if (h.content) {
            for (var y = 0; y < h.content.length; y++) {
                var c = h.content[y];

                //If existing content update it
                if (c._id && c._id.length > 0) {
                    Content.update({
                        _id: c._id
                    }, c, {
                        new: true,
                        upsert: true
                    }, function (err, content) {

                    });
                } else {
                    var content = new Content(c);
                    content.save();
                    h.content[y] = content;
                }

            }
        }
    }
    day.headings = headings;
    updateDay();

} else {
    updateDay();
}


 };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM