Mongo Aggregate issue - mongodb

Here is my data in mongodb
{
"_id" : ObjectId("5a77e82c19e5b90363fe55d4"),
"serviceId" : 85,
"clusterId" : 122,
"metricTimestamp" : ISODate("2018-02-05T04:33:58.000Z"),
"metricNames" : [
"host",
"count",
"time",
"out"
],
"metricValues" : [
"wwe.123.com",
"8829",
"2018-02-05T04:16:02.685Z",
"25327782"
],
"createtime" : ISODate("2018-02-05T05:14:20.273Z")
}
and I want to aggregate the data:
db.getCollection('metrics.value').aggregate([
{ $match: {
'serviceId': 85,
'metricTimestamp':{
$gte: ISODate("2018-02-03T04:33:58"),
$lte: ISODate("2018-02-05T04:33:58")
}
}},
{ $project: {
host: { $arrayElemAt:["$metricValues", 0]},
count:{ $arrayElemAt:["$metricValues", 1]}
},
{$group:{}}
}])
I want to group by host (here it is "wwe.123.com"), and aggregate the count (here it is "8829"). I don't know how to convert from string to int (parseInt is not working). Please help me solve these issues.

db.getCollection('foo').aggregate([
{ $match:
{ "serviceId" : 85,
"metricTimestamp" :
{ $gte: ISODate("2018-02-05T04:33:58.000Z"),
$lte: ISODate("2018-02-05T04:33:58.000Z") }}},
{ $project :
{ "_id" : 0,
"host" : { $arrayElemAt:["$metricValues", 0] },
"count" : { $arrayElemAt:["$metricValues", 1] }}},
])
Outputs:
{
"host" : "wwe.123.com",
"count" : "8829"
}
In order to then $group after your $projection you would need to convert count field into an int. How you would do that has already been asked and answered here. once you've converted your field to an int you could just add this snippet after your $projection
{ $group : { _id : "$host", count : { $sum : "$count"}}}

Related

How to get percentage total of data with group by date in MongoDB

How to get percentage total of data with group by date in MongoDB ?
Link example : https://mongoplayground.net/p/aNND4EPQhcb
I have some collection structure like this
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4b"),
"date" : "2019-05-03T10:39:53.108Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4c"),
"date" : "2019-05-03T10:39:53.133Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4d"),
"date" : "2019-05-03T10:39:53.180Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4e"),
"date" : "2019-05-03T10:39:53.218Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
And I have query in mongodb to get data of collection, how to get percentage of total data. in bellow example query to get data :
db.name_collection.aggregate(
[
{ "$match": {
"update_at": { "$gte": "2019-11-04T00:00:00.0Z", "$lt": "2019-11-06T00:00:00.0Z"},
"id": { "$in": [166] }
} },
{
"$group" : {
"_id": {
$substr: [ '$update_at', 0, 10 ]
},
"count" : {
"$sum" : 1
}
}
},
{
"$project" : {
"_id" : 0,
"date" : "$_id",
"count" : "$count"
}
},
{
"$sort" : {
"date" : 1
}
}
]
)
and this response :
{
"date" : "2019-11-04",
"count" : 39
},
{
"date" : "2019-11-05",
"count" : 135
}
how to get percentage data total from key count ? example response to this :
{
"date" : "2019-11-04",
"count" : 39,
"percentage" : "22%"
},
{
"date" : "2019-11-05",
"count" : 135,
"percentage" : "78%"
}
You have to group by null to get total count and then use $map to calculate the percentage. $round will be a useful operator in such case. Finally you can $unwind and $replaceRoot to get back the same number of documents:
db.collection.aggregate([
// previous aggregation steps
{
$group: {
_id: null,
total: { $sum: "$count" },
docs: { $push: "$$ROOT" }
}
},
{
$project: {
docs: {
$map: {
input: "$docs",
in: {
date: "$$this.date",
count: "$$this.count",
percentage: { $concat: [ { $toString: { $round: { $multiply: [ { $divide: [ "$$this.count", "$total" ] }, 100 ] } } }, '%' ] }
}
}
}
}
},
{
$unwind: "$docs"
},
{
$replaceRoot: { newRoot: "$docs" }
}
])
Mongo Playground

MongoDB nested group by query

I want to count correct, incorrect and unattempted question count. I am getting zero values.
Query -
db.studentreports.aggregate([
{ $match: { 'groupId': 314 } },
{ $unwind: '$questions' },
{ $group:
{
_id: {
dateTimeStamp: '$dateTimeStamp',
customerId: '$customerId'
},
questions : { $push: '$questions' },
unttempted : { $sum : { $eq: ['$questions.status',0]}},
correct : { $sum : { $eq: ['$questions.status',1]}},
incorrect : { $sum : { $eq: ['$questions.status',2]}},
Total: { $sum: 1 }
}
}
])
Schema structure -
{
"_id" : ObjectId("59fb46ed560e1a2fd5b6fbf4"),
"customerId" : 2863318,
"groupId" : 309,
"questions" : [
{
"questionId" : 567,
"status" : 0,
"_id" : ObjectId("59fb46ee560e1a2fd5b700a4"),
},
{
"questionId" : 711,
"status" : 0,
"_id" : ObjectId("59fb46ee560e1a2fd5b700a3")
},
....
values unttempted, correct and incorrect are getting wrong -
"unttempted" : 0,
"correct" : 0,
"incorrect" : 0,
"Total" : 7558.0
Group by is required based on datetime and customerId.
Can some one correct query ?
Thanks.
You want to sum these fields only if a certain condition is met.
You just have to rewrite your group statement like this:
{ $group:
{
_id: {
dateTimeStamp: '$dateTimeStamp',
customerId: '$customerId'
},
questions : { $push: '$questions' },
unttempted : { $sum : {$cond:[{ $eq: ['$questions.status',0]}, 1, 0]}},
correct : { $sum : {$cond:[{ $eq: ['$questions.status',1]}, 1, 0]}},
incorrect : { $sum : {$cond:[{ $eq: ['$questions.status',2]}, 1, 0]}},
Total: { $sum: 1 }
}
}
Check out the documentation $eq. $eq compares and returns true or false. So then your $sum cannot do anything with that result

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}

How to retrieve objects inside an array, if matches given condition, using mongodb 3.0.0

I want to project all the objects from an array, if it matches the given condition.
I have following data
{
_id : 1,
em : 'abc#12s.net',
name : 'NewName',
od :
[
{
"oid" : ObjectId("1234"),
"ca" : ISODate("2016-05-05T13:20:10.718Z")
},
{
"oid" : ObjectId("2345"),
"ca" : ISODate("2016-05-11T13:20:10.718Z")
},
{
"oid" : ObjectId("57766"),
"ca" : ISODate("2016-05-13T13:20:10.718Z")
}
]
},
{
_id : 2,
em : 'ab6c#xyz.net',
name : 'NewName2',
od :
[
{
"oid" : ObjectId("1234"),
"ca" : ISODate("2016-05-11T13:20:10.718Z")
},
{
"oid" : ObjectId("2345"),
"ca" : ISODate("2016-05-12T13:20:10.718Z")
},
{
"oid" : ObjectId("57766"),
"ca" : ISODate("2016-05-05T13:20:10.718Z")
}
]
}
I want to get all the objects from od array, if 'od.ca' comes between range say, if greater than 10th may and less than 15th may.
I tried using aggregate method of mongodb and I am new to this method. My query is as given below.
db.userDetail.aggregate(
{
$match:
{
'od.ca':
{
'$gte': '10/05/2016',
'$lte': '15/05/2016'
},
lo: { '$ne': 'd' }
}
},
{
$redact:
{
$cond:
{
if:
{
$gte: [ "$$od.ca", '10/05/2016' ],
$lte : ["$$od.ca" , '15/05/2016']
},
then: "$$DESCEND",
else: "$$PRUNE"
}
}
})
When I am trying to use this command, getting error :-
assert: command failed: {
"errmsg" : "exception: Use of undefined variable: od",
"code" : 17276,
"ok" : 0
} : aggregate failed
Since I am using mongodb 3.0.0 I can not use $fiter. So I tried using $redact.
Can someone tell me what wrong I am doing? Is the query correct?
Also referred question Since I am not using 3.2 of mongodb (as I have mentioned), can not use the accepted answer of the question.
query explanation:
$match - match documents for criteria - limit documents to process
$unwind - econstructs od array field from the input documents to output a document for each element. Each output document is the input document with the value of the array field replaced by the element.
$match - match documents for criteria
$group - this is opposite of $unwind in our case - so we are recreating results array
If you are expecting document like this:
{
"_id" : 2,
"od" : [{
"oid" : 1234,
"ca" : ISODate("2016-05-11T13:20:10.718Z")
}, {
"oid" : 2345,
"ca" : ISODate("2016-05-12T13:20:10.718Z")
}
]
}, {
"_id" : 1,
"od" : [{
"oid" : 2345,
"ca" : ISODate("2016-05-11T13:20:10.718Z")
}, {
"oid" : 57766,
"ca" : ISODate("2016-05-13T13:20:10.718Z")
}
]
}
you can use query bellow:
db.userDetail.aggregate([{
$match : {
"od.ca" : {
$lt : new Date(new Date().setDate(new Date().getDate() + 2)),
$gte : new Date(new Date().setDate(new Date().getDate() - 4))
}
}
}, {
$unwind : "$od"
}, {
$match : {
"od.ca" : {
$lt : new Date(new Date().setDate(new Date().getDate() + 2)),
$gte : new Date(new Date().setDate(new Date().getDate() - 4))
}
}
}, {
$group : {
_id : "$_id",
od : {
$push : "$od"
}
}
}
])
The following query gave me the desired result.
If you are using mongodb-2.6.X up to 3.0.X can use this solution.
var object = {st : "10/05/2016", et : "13/05/2016"};
db.userDetail.aggregate(
[
{
$match:
{
"od.ca":
{
'$gte': new Date(object.st),
'$lte': new Date(object.et)
},
"lo" : {$ne : 'd'}
}
},
{
$project:
{
em: 1,
fna : 1,
lna : 1,
ca :1,
od:
{
"$setDifference":
[{
"$map":
{
"input": "$od",
"as": "o",
"in":
{
"$cond":[
{
"$and":
[
{ "$gte": [ "$$o.ca", new Date(object.st) ] },
{ "$lte": [ "$$o.ca", new Date(object.et) ] },
{ "$ne": [ "$$o.oid", ObjectID(config.pid.toString())
] }
]
},
"$$o",false]
}
}
},[false]
]
}
}
},
{$sort : {_id : 1}}
];
)
If you are using 3.2.X, use $filter to get the result.

MongoDB $sum and $avg of sub documents

I need to get $sum and $avg of subdocuments, i would like to get $sum and $avg of Channels[0].. and other channels as well.
my data structure looks like this
{
_id : ... Location : 1,
Channels : [
{ _id: ...,
Value: 25
},
{
_id: ... ,
Value: 39
},
{
_id: ..,
Value: 12
}
]
}
In order to get the sum and average of the Channels.Value elements for each document in your collection you will need to use mongodb's Aggregation processing. Further, since Channels is an array you will need to use the $unwind operator to deconstruct the array.
Assuming that your collection is called example, here's how you could get both the document sum and average of the Channels.Values:
db.example.aggregate( [
{
"$unwind" : "$Channels"
},
{
"$group" : {
"_id" : "$_id",
"documentSum" : { "$sum" : "$Channels.Value" },
"documentAvg" : { "$avg" : "$Channels.Value" }
}
}
] )
The output from your post's data would be:
{
"_id" : SomeObjectIdValue,
"documentSum" : 76,
"documentAvg" : 25.333333333333332
}
If you have more than one document in your collection then you will see a result row for each document containing a Channels array.
Solution 1: Using two groups based this example:
previous question
db.records.aggregate(
[
{ $unwind: "$Channels" },
{ $group: {
_id: {
"loc" : "$Location",
"cId" : "$Channels.Id"
},
"value" : {$sum : "$Channels.Value" },
"average" : {$avg : "$Channels.Value"},
"maximun" : {$max : "$Channels.Value"},
"minimum" : {$min : "$Channels.Value"}
}},
{ $group: {
_id : "$_id.loc",
"ChannelsSumary" : { $push :
{ "channelId" : '$_id.cId',
"value" :'$value',
"average" : '$average',
"maximun" : '$maximun',
"minimum" : '$minimum'
}}
}
}
]
)
Solution 2:
there is property i didn't show on my original question that might of help "Channels.Id" independent from "Channels._Id"
db.records.aggregate( [
{
"$unwind" : "$Channels"
},
{
"$group" : {
"_id" : "$Channels.Id",
"documentSum" : { "$sum" : "$Channels.Value" },
"documentAvg" : { "$avg" : "$Channels.Value" }
}
}
] )