2

Schema and model:

var schema = new mongoose.Schema({
    timestamp_hour: Date,
    deviceID: Number,
    minutes: {
        '0': {temperature: Number},
        '1': {temperature: Number},
         .
         .
         .
        '59': {temperature: Number}
    }
},{
    collection: 'devices'
});

var model = mongoose.model('deviceData', schema);

Now in a POST request, I receive some data from an external source containing a timestamp, deviceID and temperature value.

My primary key is timestamp_hour and deviceID, so if there is an existing document in the database, I need to store the temperature value in minutes: {[minute_value]: temperature}. I currently derive minute_value from the timestamp, and I can query the database, all well and good. Now I need to update the minutes object in the document by adding the new key-value pair.

So after deriving the required values, I try running this:

var query = {timestamp_hour: timestamp, deviceID: deviceID};
var update = {minutes: {[minute]: {temperature: tempData}}};

deviceData.findOneAndUpdate(query, update, {upsert: true}, function(err, doc){
    if(err) return res.send(500, {error: err});
    return res.send("successfully saved");
});

Now the issue is, it replaces the entire minutes object inside document with the new single value.

Example:

Original document:

{
    "deviceID" : 1, 
    "timestamp_hour" : ISODate("2016-10-29T08:00:00Z"), 
    "minutes" : { "38" : { "temperature" : 39.5 } }, 
}

Document after update:

{
    "deviceID" : 1, 
    "timestamp_hour" : ISODate("2016-10-29T08:00:00Z"), 
    "minutes" : { "39" : { "temperature" : 38.0 } }, 
}

What I need:

{
    "deviceID" : 1, 
    "timestamp_hour" : ISODate("2016-10-29T08:00:00Z"), 
    "minutes" : {  "38" : { "temperature" : 39.5 } 
                   "39" " { "temperature" : 38.0 } }, 
}

I'm new to MEAN, but I can see why my approach doesn't work since the update call just modifies the nested object.

I'd appreciate any help regarding the correct approach to use for achieving this functionality.

2 Answers 2

4

You can do this within a single update using a combination of the dot and bracket notations to construct the update object as follows:

var query = { "timestamp_hour": timestamp, "deviceID": deviceID },
    update = { "$set": { } },
    options = { "upsert": true };
update["$set"]["minutes."+ minute] = { "temperature": tempData };

deviceData.findOneAndUpdate(query, update, options, function(err, doc){
    if(err) return res.send(500, {error: err});
    return res.send("successfully saved");
});
Sign up to request clarification or add additional context in comments.

2 Comments

Yep, that works. Thanks a ton! Minor follow up, I posted another solution which works, however it uses findOne and save in the function. I guess that's two db calls, so how is findOneAndUpdate doing it in a single update?
findOneAndUpdate() issues a mongodb findAndModify update command which is atomic, i.e. if you fetch an item and then update it, there may be an update by another thread between those two steps. An atomic update means you are guaranteed that you are getting back the exact same item you are updating - i.e. no other operation can happen in between.
0

Okay, so this works:

deviceData.findOne(query, function(err, doc) {
    if(err) return done(err);

    if(!doc){
        data.save(function(err){
            if(err) throw err;
            res.json({"Result":"Success"});
        });
    } else {
        doc.minutes[minute] = {temperature: tempData}
        doc.save(function(err) {});
        res.json(doc);
    }
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.