Fahad Hassan
Fahad Hassan

Reputation: 811

How can i count total documents and also grouped counts simultanously in mongodb aggregation?

I have a dataset in mongodb collection named visitorsSession like

{ip : 192.2.1.1,country : 'US', type : 'Visitors',date : '2019-12-15T00:00:00.359Z'},
{ip : 192.3.1.8,country : 'UK', type : 'Visitors',date : '2019-12-15T00:00:00.359Z'},
{ip : 192.5.1.4,country : 'UK', type : 'Visitors',date : '2019-12-15T00:00:00.359Z'},
{ip : 192.8.1.7,country : 'US', type : 'Visitors',date : '2019-12-15T00:00:00.359Z'},
{ip : 192.1.1.3,country : 'US', type : 'Visitors',date : '2019-12-15T00:00:00.359Z'}

I am using this mongodb aggregation

[{$match: {
  nsp : "/hrm.sbtjapan.com",
  creationDate : {
  $gte: "2019-12-15T00:00:00.359Z",
  $lte: "2019-12-20T23:00:00.359Z"
 },
 type : "Visitors"
 }}, {$group: {
 _id : "$country",
 totalSessions : {
   $sum: 1
  }

  }}, {$project: {
    _id : 0,
    country : "$_id",
    totalSessions : 1
   }}, {$sort: {
  country: -1
 }}]

using above aggregation i am getting results like this

[{country : 'US',totalSessions  : 3},{country : 'UK',totalSessions  : 2}]

But i also total visitors also along with result like totalVisitors : 5 How can i do this in mongodb aggregation ?

Upvotes: 1

Views: 51

Answers (2)

ambianBeing
ambianBeing

Reputation: 3529

You could be better off with two queries to do this.

To save the two db round trips following aggregation can be used which IMO is kinda verbose (and might be little expensive if documents are very large) to just count the documents.

Idea: Is to have a $group at the top to count documents and preserve the original documents using $push and $$ROOT. And then before other matches/filter ops $unwind the created array of original docs.

db.collection.aggregate([
  {
    $group: {
      _id: null,
      docsCount: {
        $sum: 1
      },
      originals: {
        $push: "$$ROOT"
      }
    }
  },
  {
    $unwind: "$originals"
  },
  { $match: "..." }, //and other stages on `originals` which contains the source documents
  {
    $group: {
      _id: "$originals.country",
      totalSessions: {
        $sum: 1
      },
      totalVisitors: {
        $first: "$docsCount"
      }
    }
  }
]);

Sample O/P: Playground Link

[
  {
    "_id": "UK",
    "totalSessions": 2,
    "totalVisitors": 5
  },
  {
    "_id": "US",
    "totalSessions": 3,
    "totalVisitors": 5
  }
]

Upvotes: 0

prasad_
prasad_

Reputation: 14287

You can use $facet aggregation stage to calculate total visitors as well as visitors by country in a single pass:

db.visitorsSession.aggregate( [
  {
      $match: {
          nsp : "/hrm.sbtjapan.com",
          creationDate : {
              $gte: "2019-12-15T00:00:00.359Z",
              $lte: "2019-12-20T23:00:00.359Z"
          },
          type : "Visitors"
      }
  },
  { 
      $facet: {
            totalVisitors: [
                { 
                    $count: "count" 
                }
            ],
            countrySessions: [
                {
                    $group: {
                        _id : "$country", 
                        sessions : { $sum: 1 }
                    }
                },
                { 
                    $project: { 
                        country: "$_id", 
                        _id: 0, 
                        sessions: 1 
                    } 
                }
            ],
      }
  },
 { 
      $addFields: { 
          totalVisitors: { $arrayElemAt: [ "$totalVisitors.count" , 0 ] },
      } 
  }
] )

The output:

{
        "totalVisitors" : 5,
        "countrySessions" : [
                {
                        "sessions" : 2,
                        "country" : "UK"
                },
                {
                        "sessions" : 3,
                        "country" : "US"
                }
        ]
}

Upvotes: 2

Related Questions