admin管理员组

文章数量:1356815

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();
};

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();
};
Share Improve this question asked Jul 5, 2015 at 23:47 SpentakSpentak 3,3594 gold badges22 silver badges31 bronze badges 2
  • Asynchronous functions and synchronous loops don't mix well. You should never use .update() without $set or similar operators. All arrays are referenced so unless the actual objects exist already in another collection you don't create this way. Hopefully objId has been imported from Schema.Types.ObjectId. Those are just a few thoughts that e to mind at first glance. – Blakes Seven Commented Jul 5, 2015 at 23:58
  • I'm not sure what you are saying I should do. The idea is a user creates a Day with X amount of headings and X amount of content objects within the content array. Then they click save and the back-end updates objects that need to be updated, and if it doesn't exist it creates it. I could save each Content object individually, but how would I make sure they are added to the array in the Content array inside the Day? – Spentak Commented Jul 6, 2015 at 0:09
Add a ment  | 

2 Answers 2

Reset to default 6

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./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();
}


 };

本文标签: javascriptMongoose save subdocument arrayStack Overflow