skip to Main Content

I have a document from mongo database looking like this:

{
  "_id": "00000001",
  "category": "Weather",
  "city": "Salt Lake City",
  "date": {
    "$date": {
      "$numberLong": "1663236000000"
    }
  },
  "logs": {
    "2022-09-14 12:00:00": {
      "temp": 55,
      "humidity": 25
    },
    "2022-09-14 14:00:00": {
      "temp": 65,
      "humidity": 35
    }
  }
}

I am trying to query it and have it look like this:

{
  "_id": "00000001",
  "category": "Weather",
  "city": "Salt Lake City",
  "date": {
    "$date": {
      "$numberLong": "1663236000000"
    }
  },
  "2022-09-14 12:00:00": "55, 25",
  "2022-09-14 14:00:00": "65, 35"
}

Currently my application query looks like:

collection.aggregate(
    [{
            $match: {
                _id: {
                    $exists: true
                }
            }
        },
        {
            $unwind: "$logs"
        },
        {
            $addFields: {
                "series._id": "$_id",
                "series.category": "$category",
                "series.city": "$city",
                "series.date": "$date",
            }
        },
        {
            $replaceRoot: {
                newRoot: "$logs"
            },
        }
    ])

which results in:

{
  "_id": "00000001",
  "category": "Weather",
  "city": "Salt Lake City",
  "date": {
    "$date": {
      "$numberLong": "1663236000000"
    }
  },
  "2022-09-14 12:00:00": {
      "temp": 55,
      "humidity": 25
    },
  "2022-09-14 14:00:00": {
      "temp": 65,
      "humidity": 35
    }
}

My problem is that the logs will add a new field every n hours, so the field names will be dynamic. I need to set/update the values for the unwound fields from objects to a string representation. How can I set/update field values for fields generated through $unwind aggregation like the example?

2

Answers


  1. Chosen as BEST ANSWER

    Building off of @nimrod serok's answer, still needed to flatten the logs field in my case. Used mergeObjects to flatten the field into the root document, and then used unset to remove the original field. This probably isn't the best way to do this but it is working for me. Thanks

    [{$match: {_id: {$exists: true}}},
            {$set: {logs: {$objectToArray: "$logs"}}},
            {$set: {logs: {
                  $map: {
                    input: "$logs",
                    in: {
                      k: "$$this.k",
                      v: {$concat: [
                          {$toString: "$$this.v.temp"},
                          ", ",
                          {$toString: "$$this.v.humidity"}
                        ]
                      }
                    }
                  }
                }
              }
            },
            {$replaceRoot: { newRoot: { $mergeObjects: ["$$ROOT", {$arrayToObject: "$logs"}] } } },
            {$unset: "logs"}
          ]
    

  2. When field names are dynamic, one option is to use $objectToArray:

    db.collection.aggregate([
      {$match: {_id: {$exists: true}}},
      {$set: {logs: {$objectToArray: "$logs"}}},
      {$set: {logs: {
            $map: {
              input: "$logs",
              in: {
                k: "$$this.k",
                v: {$concat: [
                    {$toString: "$$this.v.temp"},
                    ", ",
                    {$toString: "$$this.v.humidity"}
                  ]
                }
              }
            }
          }
        }
      },
      {$set: {logs: {$arrayToObject: "$logs"}}}
    ])
    

    See how it works on the playground example

    BTW, $unwind is for arrays, not for objects, hence the comment by @CharchitKapoor.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search