Search code examples
mongodbaggregatefieldaverage

How do I calculate the average of multiple fields in a mongoDB document?


I have a collection with the following fields:

_id:612ff22c17286411a17252cf
WELL:"199-H4-15A"
TYPE:"E"
SYSTEM:"HX"
ID:"199-H4-15A_E_HX"
Jan-14:-168.8
Feb-14:-151
Mar-14:-164.1
Apr-14:-168.7
May-14:-172.6
Jun-14:-177.3
Jul-14:-177.6
Aug-14:-171.9
Sep-14:-138.9
Oct-14:-130.3
Nov-14:-163.8
Dec-14:-161.4
Jan-15:-168.7
Feb-15:-168.9
Mar-15:-168.6
Apr-15:-164.6
May-15:-141.7
Jun-15:-153.5
Jul-15:-163.7
Aug-15:-167.7

and I am trying to take the average of all of the month fields (e.g. all files like "Jan-14", "Feb-14" and so on). I was thinking of somehow pushing all of the month fields data into an array and then average the values but would like to avoid having to list all of the individual field names. Below is what I have so far.

[{$match: {
  'WELL': '199-H4-15A'
}}, {$group: {
  _id: null,
  MonthAverageFlows: {
    $push: {
          $isNumber: ['$all']
    }
  }
}}, {$unwind: '$MonthAverageFlows'}, {$group: {
  _id: null,
  average: {
    $avg: '$MonthAverageFlows.value'
  }
}}]

All that comes out is ```null``. Any and all help would be appreciated. The raw data is in csv form:

WELL, TYPE, SYSTEM, ID, JAN-14, FEB-14, . . .
"199-H4-15A", "E", "HX", "199-H4-15A_E_HX", -168.8, -151, . . .

Solution

  • Using dynamic values as field name is generally considered as anti-pattern and you should avoid that. You are likely to introduce unnecessary difficulty to composing and maintaining your queries.

    Nevertheless, you can do the followings in an aggregation pipeline:

    1. $objectToArray to convert your raw document into k-v tuples array
    2. $filter the array to get contain the monthly data only
    3. $avg to calculate the sum
    db.collection.aggregate([
      {
        $match: {
          "_id": ObjectId("612ff22c17286411a17252cf")
        }
      },
      {
        "$addFields": {
          "data": {
            "$objectToArray": "$$ROOT"
          }
        }
      },
      {
        "$addFields": {
          "data": {
            "$filter": {
              "input": "$data",
              "as": "tuple",
              "cond": {
                "$isNumber": "$$tuple.v"
              }
            }
          }
        }
      },
      {
        "$addFields": {
          "data": {
            $avg: "$data.v"
          }
        }
      }
    ])
    

    Here is the Mongo playground for your reference.