Search code examples
mongodbsetaveragedivide

Update a field on my mongo collection with $set and $avg, max, min


I have in my mongo collection this document:

{
    "_id": {
        "date": "2046-12-17",
        "device": "782330b8f41-8980-4b81-9e04-d7c08a9d621e"
    },
    "organizationId": 7,
    "smartSpaceId": 82,
    "typeName": "sensor",
    "typeId": 12,
    "smartDeviceId": "330b8f41-8980-4b81-9e04-d7c08a9d621e",
    "isVirtual": false,
    "receivedTopic": "spot-pre.7.82.data",
    "samples": [
        {
            "t": 2428626587,
            "tS": 2428626640,
            "tR": 2428626631,
            "value": {
                "measure": 4.77799988,
                "time": 161304136258,
                "meta": {
                    "is_reachable": true
                }
            }
        },
        {
            "t": 2428627687,
            "tS": 2428627840,
            "tR": 2428627731,
            "value": {
                "measure": 7.7234234988,
                "time": 161304146358,
                "meta": {
                    "is_reachable": true
                }
            }
        },
        {
            "t": 2428626997,
            "tS": 2428624570,
            "tR": 2428226631,
            "value": {
                "measure": 12.74352588,
                "time": 161304132348,
                "meta": {
                    "is_reachable": true
                }
            }
        },
        {
            "t": 2428626587,
            "tS": 2428626587,
            "tR": 2428626587,
            "value": {
                "measure": 20.01,
                "time": 161304136368,
                "meta": {
                    "is_reachable": true
                }
            }
        }
    ],
    "nsamples": 4,
    "stats_value_measure": {
        "avg": 8.414983086266667,
        "min": 4.77799988,
        "max": 12.74352588,
        "sum": 45.254949258799999
    }
}

And I am trying to execute this query to update the filed avg, max and min:

db.deviceMeasure.updateOne(
{ 
    "_id": {
        "date": "2020-12-17",
        "device": "782330b8f41-8980-4b81-9e04-d7c08a9d621e"
    } 
},
{ 
    "$push": { 
        "samples": {
            "t": 2428626587,
            "tS": 2428626587,
            "tR": 2428626587,
            "value": { 
                "measure": 25.01, 
                "time": 161304136368, 
                "meta": { 
                    "is_reachable": true 
                } 
            }
        } 
    },
    "$inc": { "nsamples": 1, "stats_value_measure.sum": 20.01 },
    "$set": { 
        "stats_value_measure": {
            "avg": { $avg: "samples.value.measure" },
            "min": { $min: "samples.value.measure" },
            "max": { $max: "samples.value.measure" },
            "sum": { $sum: "samples.value.measure" }
    }
},
{ upsert: true })

But in my shell I only see "..." Can you help me?


Solution

  • For Mongo v4.2+ you can use pipelined updates, this allows us to use document field values in an update. You will just have to adjust the operators to use aggregation operators instead of update operators, like so:

    db.deviceMeasure.updateOne(
    { 
        "_id": {
            "date": "2020-12-17",
            "device": "782330b8f41-8980-4b81-9e04-d7c08a9d621e"
        } 
    },
    [
      {
        "$set": {
          "samples": {
            $concatArrays: [
              "$samples",
              [
                {
                  "t": 2428626587,
                  "tS": 2428626587,
                  "tR": 2428626587,
                  "value": {
                    "measure": 25.01,
                    "time": 161304136368,
                    "meta": {
                      "is_reachable": true
                    }
                  }
                }
              ]
            ]
          },
          nsamples: {
            $sum: [
              1,
              "$nsamples"
            ]
          },
          "stats_value_measure.sum": {
            $sum: [
              "stats_value_measure.sum",
              20.01
            ]
          }
        }
      },
      {
        "$set": {
          "stats_value_measure": {
            "avg": {
              $avg: "$samples.value.measure"
            },
            "min": {
              $min: "$samples.value.measure"
            },
            "max": {
              $max: "$samples.value.measure"
            },
            "sum": {
              $sum: "$samples.value.measure"
            }
          }
        }
      }
    ])
    

    Mongo Playground

    For lesser Mongo versions you'll have to read the document into memory and do the calculations in code as you can't use document field values within an update.