Lucas Watson Lucas Watson - 4 months ago 60
AngularJS Question

Mongoose doesn't create subdocument from JSON array

I'm trying to write a JSON object that contains both first-level data along with arrays into MongoDB.

What happens instead is all first-level data is stored, but anything contained in an array isn't. When logging the data the server receives, I see the entire object, which leads me to believe there's something wrong with my Mongoose code.

So for example if I send something like this:

issueId: "test1",
issueTitle: "testtest",
rows: [
{order:1,data: [object]},
{order:2,data: [object]},
]


Only the following gets stored:

issueId: "test1",
issueTitle: "testtest",
lastUpdated: Date,


I have the following model for Mongo:

//model.js
var mongoose = require('mongoose');

var model = mongoose.Schema({

issueId : String,
issueTitle : String,
lastUpdated : {type: Date, default : Date.now},

rows : [{
order : Number,
data : [
{
title : String,
text : String,
link : String,
}
]
}]

});

module.exports = mongoose.model('Model', model);


And the routing code, where I believe the problem likely is:

//routes.js
const mongoose = require('mongoose');
const Model = require('./model.js');
...
app.post('/api/data/update', function(req, res) {
let theData = req.body.dataToInsert;

console.log(JSON.stringify(theData,null,4));

Model.findOneAndUpdate(
{issueId : theData.issueId},
{theData},
{upsert: true},
function(err,doc){
if(err) throw err;
console.log(doc);
});
});


As well, here's the part of the Angular controller storing the data. I don't think there's any problem here.

pushToServer = function() {
$http.post('/api/data/update',{
dataToInsert : $scope.dataObject,
}).then(function successCallback(res){
console.log("all good", JSON.stringify(res,null,3));
}, function errorCallback(res){
console.log("arg" + res);
});
}

Answer

Look at the first question in the mongoose FAQ: http://mongoosejs.com/docs/faq.html

Mongoose doesn't create getters/setters for array indexes; without them mongoose never gets notified of the change and so doesn't know to persist the new value. The work-around is to use MongooseArray#set available in Mongoose >= 3.2.0.

// query the document you want to update
// set the individual indexes you want to update
// save the document
doc.array.set(3, 'changed');
doc.save();

EDIT

I think this would work to update all of the rows. I'd be interested to know if it does work.

let rowQueries = [];
theData.rows.forEach(row => {
    let query = Model.findOneAndUpdate({
        issueId: theData.issueId,
        'row._id': row._id
    }, {
        $set: {
            'row.$': row
        }
    });
    rowQueries.push(query.exec());
});

Promise.all(rowQueries).then(updatedDocs => {
    // updated
});
Comments