How to join two Aggregation results in MongoDB? - mongodb

I have a data set looks as
{"BrandId":"a","SessionId":100,"Method": "POST"}
{"BrandId":"a","SessionId":200,"Method": "PUT"}
{"BrandId":"a","SessionId":200,"Method": "GET"}
{"BrandId":"b","SessionId":300,"Method": "GET"}
I wrote aggregation count distinct session id by brandid:
db.collection.aggregate([
{$group: {
"_id": {
brand: "$BrandId",
session: "$SessionId"
},
count: {$sum: 1}
}},
{$group: {
_id: "$_id.brand",
countSession:{$sum:1}
}}
])
The expected result of the query is :
{ "_id" : "a", "countSession" : 2 }
{ "_id" : "b", "countSession" : 1 }
Another query is to count where the Method is POST by brand:
db.collection.aggregate([
{$match: {Method:"POST"}},
{$group: {
_id: '$BrandId',
countPOST:{$sum:1}
}}
])
The expected result:
{ "_id" : "a", "countPOST" : 1 }
{ "_id" : "b", "countSession" : 0 }
And now, I want to combine these two query and get the expected result as following:
{"BrandId:"a","countSession":2,"countPOST":1}
{"BrandId:"b","countSession":1,"countPOST":0}
I do not how to combine these two result of two aggregation, anyone can help?

You can use $cond operator as follows.
db.Collection.aggregate(
{
'$group': {
'_id': {'BrandId':'$BrandId','Session': '$SessionId'},
'countPOST':{
'$sum':{
'$cond': [{'$eq':['$Method','POST']},1,0]
}
}
}
},
{
'$group': {
'_id': '$_id.BrandId',
'countSession': {'$sum':1},
'countPOST': {'$sum': '$countPOST'}
}
}
)
Ouput:
{
"result" : [
{
"_id" : "a",
"countSession" : 2,
"countPOST" : 1
},
{
"_id" : "b",
"countSession" : 1,
"countPOST" : 0
}
],
"ok" : 1
}

Related

Is there a way to add a counter to mongodb query?

I would like to add a counter to the documents that match my query. E.g., 1st document has counter = 1, 2nd document has counter = 2, and so on.
Here's a snippet of the data:
"_id": ObjectId("5d1b9aea5c1dd54e8c773f42")
"timestamp":
[
"systemTimestamp": 2019-07-02T17:56:53.765+00:00
"serverTimestamp": 0001-01-01T00:00:00.000+00:00
"systemTimeZone": "System.CurrentSystemTimeZone"
]
"urlData":
[0]:
"fullUrl":"https://imgur.com/gallery/EfaQnPY"
"UID":"00000-W3W6C42GWTRE960"
"safety": "safe"
My query (this is copied from the Compass UI):
$match:
{
$and: [{"UID": "00000-WVUCW3JW7OTHDVE"},
{"timestamp.serverTimestamp":
{
$gte:ISODate("2019-08-01T00:00"),
$lte:ISODate("2019-09-30T00:00")
}}]
}
$unwind:
{
path: "$urlData",
includeArrayIndex: 'index'
}
$match:
{
"index": 0
}
$project:
{
_id: 0,
date: { $dateToString: {
format: "%Y-%m-%d",
date: "$timestamp.serverTimestamp"}},
safety: "$safety",
url: "$urlData.fullUrl",
UID: "$UID"
}
Is there any way to add something to $project to include a counter?
The answer is there in your question itself. We can get the expected output if the output of the last pipeline is added into an array and again unwinded with the included index.
Let's say, I have the following data:
{
"_id" : ObjectId("5d81c3b7a832f81a9e02337b"),
"first" : "John",
"last" : "Smith"
}
{
"_id" : ObjectId("5d81c3b7a832f81a9e02337c"),
"first" : "Alice",
"last" : "Johnson"
}
{
"_id" : ObjectId("5d81c3b7a832f81a9e02337d"),
"first" : "Bob",
"last" : "Williams"
}
On running the following query:
db.collection.aggregate([
{
$group:{
"_id":null,
"data":{
$push:"$$ROOT"
}
}
},
{
$unwind:{
"path":"$data",
includeArrayIndex: 'counter'
}
},
{
$addFields:{
"data.counter":{
$sum:["$counter",1]
}
}
},
{
$replaceRoot:{
"newRoot":"$data"
}
}
]).pretty()
Output would be:
{
"_id" : ObjectId("5d81c3b7a832f81a9e02337b"),
"first" : "John",
"last" : "Smith",
"counter" : 1
}
{
"_id" : ObjectId("5d81c3b7a832f81a9e02337c"),
"first" : "Alice",
"last" : "Johnson",
"counter" : 2
}
{
"_id" : ObjectId("5d81c3b7a832f81a9e02337d"),
"first" : "Bob",
"last" : "Williams",
"counter" : 3
}

how can I find out the sum of a field in mongodb

I am really new to mongoDB. In the given data structure how can i sum the misscallcount field
{
"_id" : ObjectId("596c5f6905c36efd35000009"),
"misscallDetails" : [
{
"contactId" : "1573778945692669180",
"misscallCount" : NumberInt(1),
"promotedOn" : ISODate("2017-08-04T13:54:17.298+0000")
},
{
"contactId" : "1573778945692669180",
"misscallCount" : NumberInt(1),
"promotedOn" : ISODate("2017-08-04T13:56:17.243+0000")
}
],
"promoId" : ObjectId("596c5f4705c36efd35000003"),
}
From comments:
I tried from my side but its give total 0 ;
db.promoledger.aggregate( [
{ $group: { _id: null, total: { $sum: "$misscallDetails.misscallCount" } }}
])
You need to $unwind the array before grouping for sum
b.promoledger.aggregate( [
{$unwind: '$misscallDetails'},
{ $group: { _id: null, total: { $sum: "$misscallDetails.misscallCount" } }}
])

mongodb count number of documents for every category

My collection looks like this:
{
"_id":ObjectId("5744b6cd9c408cea15964d18"),
"uuid":"bbde4bba-062b-4024-9bb0-8b12656afa7e",
"version":1,
"categories":["sport"]
},
{
"_id":ObjectId("5745d2bab047379469e10e27"),
"uuid":"bbde4bba-062b-4024-9bb0-8b12656afa7e",
"version":2,
"categories":["sport", "shopping"]
},
{
"_id":ObjectId("5744b6359c408cea15964d15"),
"uuid":"561c3705-ba6d-432b-98fb-254483fcbefa",
"version":1,
"categories":["politics"]
}
I want to count the number of documents for every category. To do this, I unwind the categories array:
db.collection.aggregate(
{$unwind: '$categories'},
{$group: {_id: '$categories', count: {$sum: 1}} }
)
Result:
{ "_id" : "sport", "count" : 2 }
{ "_id" : "shopping", "count" : 1 }
{ "_id" : "politics", "count" : 1 }
Now I want to count the number of documents for every category, but where document version is the latest version.
This is where I am stuck.
It's ugly but I think this gives you what you're after:
db.collection.aggregate(
{ $unwind : "$categories" },
{ $group :
{ "_id" : { "uuid" : "$uuid" },
"doc" : { $push : { "version" : "$version", "category" : "$categories" } },
"maxVersion" : { $max : "$version" }
}
},
{ $unwind : "$doc" },
{ $project : { "_id" : 0, "uuid" : "$id.uuid", "category" : "$doc.category", "isCurrentVersion" : { $eq : [ "$doc.version", "$maxVersion" ] } } },
{ $match : { "isCurrentVersion" : true }},
{ $group : { "_id" : "$category", "count" : { $sum : 1 } } }
)
You can do this by first grouping the denormalized documents (from the $unwind operator step) by two keys, i.e. the categories and version fields. This is necessary for the preceding pipeline step which orders the grouped documents and their accumulated counts by the version (desc) and categories (asc) keys respectively using the $sort operator.
Another grouping will be required to get the top documents in each categories group after ordering using the $first operator. The following shows this
db.collection.aggregate(
{ "$unwind": "$categories" },
{
"$group": {
"_id": {
'categories': '$categories',
'version': '$version'
},
"count": { "$sum": 1 }
}
},
{ "$sort": { "_id.version": -1, "_id.categories": 1 } },
{
"$group": {
"_id": "$_id.categories",
"count": { "$first": "$count" },
"version": { "$first": "$_id.version" }
}
}
)
Sample Output
{ "_id" : "shopping", "count" : 1, "version" : 2 }
{ "_id" : "sport", "count" : 1, "version" : 2 }
{ "_id" : "politics", "count" : 1, "version" : 1 }

Get record of another field with aggregate

I am new in MongoDB world.
I've following data in my collection
{
"_id" : ObjectId("5735d8d4d147aa34e440988f"),
"DeviceLogId" : "26962",
"DeviceId" : "10",
"UserId" : "78",
"LogDateTime" : ISODate("2016-05-12T07:52:44.000+0000")
}
{
"_id" : ObjectId("5735d8d4d147aa34e4409890"),
"DeviceLogId" : "26963",
"DeviceId" : "10",
"UserId" : "342",
"LogDateTime" : ISODate("2016-05-12T07:54:09.000+0000")
}
{
"_id" : ObjectId("5735d8d4d147aa34e4409891"),
"DeviceLogId" : "26964",
"DeviceId" : "10",
"UserId" : "342",
"LogDateTime" : ISODate("2016-05-12T07:54:10.000+0000")
}
{
"_id" : ObjectId("5735d8d4d147aa34e4409892"),
"DeviceLogId" : "26965",
"DeviceId" : "10",
"UserId" : "78",
"LogDateTime" : ISODate("2016-05-12T07:54:27.000+0000")
}
I want to query DeviceId of each user with maximum LogDateTime using group by.
I've written group by query like below but have no idea how would I get DeviceLogId for each record.
collectionName.aggregate(
[{
$match: { LogDateTime: { $gt: todaysDateStart, $lt: todayDateEnd } }
}, {
$group: {
_id: "$UserId",
maxPunchTime: { $max: { $add: [ "$LogDateTime", 330*60000 ] } },
}
}])
In MSSQL, I could easily do it with nested query but I've no idea how would I achieve that in MongoDB.
Thanks in advance.
Use the $addToSet Group Accumulator:
collectionName.aggregate(
[{
$match: { LogDateTime: { $gt: todaysDateStart, $lt: todayDateEnd } }
}
, {
$group: {
_id: "$UserId",
maxPunchTime: { $max: { $add: [ "$LogDateTime", 330*60000 ] } },
deviceLogIds:{$addToSet: "$DeviceLogId"} //<----
}
} ,
{ $sort: {"maxPunchTime" : -1} } , {$limit : 1} //Sort Descending + Limit to 1
])
Add deviceid to an array in group phase,
Device:{$addToSet:deviceId}

Aggregate objects' nested array occurrences count

I'm trying to aggregate logs in that way, so I can get count of how many times keywords were favorited by particular user. What I came up is following query:
db.a.aggregate([
{$unwind: "$keywords"},
{$group : {_id : {word : "$keywords", user : "$favorited_by"}, count : {$sum : 1}}}
]);
But it produces output:
{ "_id" : { "word" : "another", "user" : "too_creepy" }, "count" : 1 }
{ "_id" : { "word" : "test", "user" : "too_creepy" }, "count" : 2 }
Whilst I want to get something like this:
INPUT
{
_id: ObjectId("5475cf117ccee624583ba94a"),
favorited_by: "too_creepy",
keywords: [
"test"
]
},
{
_id: ObjectId("5475cf117ccee624583ba949"),
favorited_by: "too_creepy",
keywords: [
"test"
]
},
{
_id: ObjectId("5475cf117ccee624583ba949"),
favorited_by: "too_creepy",
keywords: [
"anotherone"
]
},
{
_id: ObjectId("5475cf117ccee624583ba09a"),
favorited_by: "hello_world",
keywords: [
"test"
]
}
OUTPUT
{
favorited_by: "too_creepy",
keywords: [
{keyword: "test", count: 2},
{keyword: "anotherone", count: 1}
]
},
{
favorited_by: "hello_world",
keywords: [
{keyword: "test", count: 1}
]
}
Any ideas how can to write this query if it's even possible?
You can do that by adding a second $group to your pipeline followed up with a final $project to reshape the output a bit:
db.a.aggregate([
{$unwind: "$keywords"},
{$group: {_id: {word: "$keywords", user: "$favorited_by"}, count: {$sum: 1}}},
// Group again on just user, and use $push to assemble an array of their keywords
{$group: {
_id: '$_id.user',
keywords: {$push: {keyword: '$_id.word', count: '$count'}}
}},
// Reshape the output
{$project: {favorited_by: '$_id', keywords: 1, _id: 0}}
]);
Output:
{
"keywords" : [
{
"keyword" : "anotherone",
"count" : 1
},
{
"keyword" : "test",
"count" : 2
}
],
"favorited_by" : "too_creepy"
},
{
"keywords" : [
{
"keyword" : "test",
"count" : 1
}
],
"favorited_by" : "hello_world"
}