adelbg
adelbg

Reputation: 23

Mongodb project results into single document

I have a collection like this:

{ 
    "_id" : ObjectId("5f4e81f1da5ea3cb7c248a8f"), 
    "type" : "TYPE_1", 
    "updateTime" : ISODate("2020-08-24T11:10:43.219+0000")
}
{ 
    "_id" : ObjectId("5f4e8206da5ea3cb7c248a90"), 
    "type" : "TYPE_1", 
    "updateTime" : ISODate("2020-09-24T11:10:43.219+0000")
}
{ 
    "_id" : ObjectId("5f4e821fda5ea3cb7c248a91"), 
    "type" : "TYPE_2", 
    "updateTime" : ISODate("2020-09-25T11:10:43.219+0000")
}

I want to know how many documents there are of each type and also obtain the date of the last global modification. For now I can get these results like this:

db.getCollection("test").aggregate(

    // Pipeline
    [
        // Stage 1
        {
            $group: {
                      _id : "$type",
                      count: { $sum: 1 },
                      lastUpdate: { "$max": "$updateTime" }      
            }
        },

        // Stage 2
        {
            $sort: { 
              lastUpdate : -1
            }
        },

    ]
);

With which I get the results this way:

{ 
    "_id" : "TYPE_2", 
    "count" : 1.0, 
    "lastUpdate" : ISODate("2020-09-25T11:10:43.219+0000")
}
{ 
    "_id" : "TYPE_1", 
    "count" : 2.0, 
    "lastUpdate" : ISODate("2020-09-24T11:10:43.219+0000")
}

So I have both the sum of each document and the last modification (thanks to the sort).

But I would like to project and get the results like this, in a single result document:

{ 
    "type1" : 2.0, 
    "type2" : 1.0, 
    "lastUpdate" : ISODate("2020-09-25T11:10:43.219+0000")
}

Upvotes: 2

Views: 160

Answers (2)

varman
varman

Reputation: 8894

You can use following stages after your stage.

{
    $group: {
      _id: null,
      data: {
        $push: {
          type: "$_id",
          count: "$count"
        }
      },
      lastUpdate: {
        $first: "$lastUpdate"
      }
    }
  },
  {
    $project: {
      data: {
        $arrayToObject: {
          $map: {
            input: "$data",
            in: {
              k: "$$this.type",
              v: "$$this.count"
            }
          }
        }
      },
      lastUpdate: 1
    }
  },
  {
    $addFields: {
      "data.lastUpdate": "$lastUpdate"
    }
  },
  {
    "$replaceRoot": {
      "newRoot": "$data"
    }
  }

Working Mongo playground

Upvotes: 1

turivishal
turivishal

Reputation: 36154

@varman's answer is good, this is just in different way,

  • $group you have already done by your self
  • $group create types array to combine all documents
  • $replaceWith to replace root with field types to convert $arrayToObject
db.collection.aggregate([
  {
    $group: {
      _id: "$type",
      count: { $sum: 1 },
      lastUpdate: { $max: "$updateTime" }
    }
  },
  {
    $group: {
      _id: null,
      types: {
        $push: {
          k: "$_id",
          v: "$count"
        }
      },
      lastUpdate: { $max: "$lastUpdate" }
    }
  },
  {
    $replaceWith: {
      $mergeObjects: [
        { lastUpdate: "$lastUpdate" },
        { $arrayToObject: "$types" }
      ]
    }
  }
])

Playground

Upvotes: 1

Related Questions