MongoDB nested group by query - mongodb

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

Related

$group with $map not working properly in mongodb

I am new to mongodb aggregation, i have a collection
{
"_id":60ab3312623b0108338a9601,
"items":[{"type":"Tshirt","price":300,"quantity":2}],
"orderId":"ORD189",
"from":"abc",
"to":"xyz",
"createdAt":2021-05-24T05:01:06.960Z,
"__v":0,
"tracking":null
}
I want to find the count of orders per day and totalPrice of items like
{_id: {date:24, month:05, year:2021}, count: 1, totalPrice: 600}
I used aggregations like below
db.getCollection("orders").aggregate(
[
{
"$group" : {
"_id" : {
"date" : {
"$dayOfMonth" : "$createdAt"
},
"month" : {
"$month" : "$createdAt"
},
"year" : {
"$year" : "$createdAt"
}
},
"count" : { $sum : 1},
"totalPrice" : {
$sum: {
$map: {
"input" :"$items",
"as":"item",
"in":{
$multiply: ["$item.price", "$item.quantity"]
}
}
}
}
}
}
]
);
It is giving me the following result
{
"_id" : {
"date" : NumberInt(24),
"month" : NumberInt(5),
"year" : NumberInt(2021)
},
"count" : 1.0,
"totalPrice" : NumberInt(0)
}
If I use unwind am not able to get the proper count.
You just need to correct totalPrice calculation,
$map to iterate loop of items and do multiplication of price and quantity, this will return array of total
$sum to get total of above total
"totalPrice": {
$sum: {
$sum: {
$map: {
input: "$items",
in: {
$multiply: ["$$this.price", "$$this.quantity"]
}
}
}
}
}
Playground

Is there a way to group results from multiple documents when performing aggregation

I am new to mongo and trying to perform aggregation query to calculate min/max of timestamps for a given document.
Sample documents are below -
{
"_id" : ObjectId("5c9cd93adddca9ebb2b3fcba"),
"frequency" : 5,
"s_id" : "30081993",
"timestamp" : NumberLong(1546300800000),
"date" : ISODate("2019-01-01T00:00:00.000Z"),
"values" : {
"1547439900000" : {
"number_of_values" : 3,
"min_value" : 32.13,
"max_value" : 81.42
},
"1547440200000" : {
"number_of_values" : 3,
"min_value" : 48.08,
"max_value" : 84.52
},
"1547440500000" : {
"number_of_values" : 2,
"min_value" : 27.39,
"max_value" : 94.64
}
}
}
{
"_id" : ObjectId("5c9cd851dddca9ebb2b3f2ac"),
"frequency" : 5,
"s_id" : "27061995",
"timestamp" : NumberLong(1546300800000),
"date" : ISODate("2019-01-01T00:00:00.000Z"),
"values" : {
"1547539900000" : {
"number_of_values" : 31,
"min_value" : 322.13,
"max_value" : 831.42
},
"1547540200000" : {
"number_of_values" : 3,
"min_value" : 418.08,
"max_value" : 8114.52
},
"1547740500000" : {
"number_of_values" : 2,
"min_value" : 207.39,
"max_value" : 940.64
}
}
}
I have come up with the following query which works for a single document.
db.testdb.aggregate([
{
$match: {
"s_id": "30081993",
"frequency": 5,
}
},
{
$project: {
_id: 1,
valuesarray: {
$objectToArray: "$values"
}
}
},
{
$unwind: "$valuesarray"
},
{
$group: {
"_id": "",
"min_timestamp": {
$min: "$valuesarray.k"
},
"max_timestamp": {
$max: "$valuesarray.k"
}
}
}
]);
The output is below
{
"_id" : "",
"min_timestamp" : "1547439900000",
"max_timestamp" : "1547440500000"
}
I want an aggregation query which can calculate the max/min of timestamps but for multiple documents i.e I want to use a $in operator during the $match stage and get min/max of all s_id. Is this possible?
Expected :
{
"_id" : "30081993",
"min_timestamp" : "1547439900000",
"max_timestamp" : "1547440500000"
}
{
"_id" : "27061995",
"min_timestamp" : "1547539900000",
"max_timestamp" : "1547740500000"
}
Yes, only small changes are required to make this work for multiple documents.
In $match stage, specify your $in query:
$match: {
"s_id": { $in : [ "30081993", "27061995" ] },
"frequency": 5,
}
In $project stage, rename s_id to _id, to ensure we keep the s_id associated with each document:
$project: {
_id: "$s_id",
valuesarray: {
$objectToArray: "$values"
}
}
In $group stage, group by _id (originally s_id), to ensure we correctly group the timestamps together before calculating $min/$max:
$group: {
"_id": "$_id",
"min_timestamp": {
$min: "$valuesarray.k"
},
"max_timestamp": {
$max: "$valuesarray.k"
}
}
Whole pipeline:
db.testdb.aggregate([
{
$match: {
"s_id": { $in : [ "30081993", "27061995" ] },
"frequency": 5,
}
},
{
$project: {
_id: "$s_id",
valuesarray: {
$objectToArray: "$values"
}
}
},
{
$unwind: "$valuesarray"
},
{
$group: {
"_id": "$_id",
"min_timestamp": {
$min: "$valuesarray.k"
},
"max_timestamp": {
$max: "$valuesarray.k"
}
}
}
]);

Mongo Aggregate issue

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"}}}

Can not get $sum in aggregate function in MongoDB

Here's my data format :
"request" : {
"_id" : 1003,
"user" : {
"username" : "",
"password" : "",
"_id" : 1111,
"gender" : "male",
},
"request" : {
"merchantid" : "TA456",
"txnAmount" : 18000,
"fee" :0,
"IssuerID" : "18801111",
"bankID" : "888888",
}
},
"confirmation" : true
}`
I can not get the sum of 'request.request.txnAmount' group by 'request.user._id'.
Here's my query:
db.getCollection('megabank_response').aggregate(
[ { $group : {
_id : "request.user._id",
totalPrice: { $sum: "request.request.txnAmount"},
count: { $sum: 1 }
}
}
])
And get the result :
{
"_id" : "request.user._id",
"total Price" : 0,
"count" : 4.0
}
Can anyone help me why my query cannot calculate the sum?
You seem to missing the $ symbol in the group aggregation function.
db.megabank_response.aggregate([{
$group: {
_id: "request.user._id",
ss: {
$sum: "request.request.txnAmount"
}
}
}]);
gives: { "_id" : "request.user._id", "ss" : 0 }
But the correct answer is:
db.megabank_response.aggregate([{
$group: {
_id: "$request.user._id",
ss: {
$sum: "$request.request.txnAmount"
}
}
}]);
I've managed to figured out the query using answer from this source.
However, my query is stil a little bit too messy. Any ways to optimize it?
db.getCollection('megabank_response').aggregate([
{
"$match": {
"request.user._id": 1111
}
},
{
"$group":{
_id : "request.user._id",
totalPrice: { $sum: '$request.request.txnAmount'},
count: { $sum: 1 }
}
}
])
Result:
{
"_id" : "request.user._id",
"totalPrice" : 62000,
"count" : 3.0
}
Try:
db.getCollection('megabank_response').aggregate([{
$group: {
_id : "request.user._id",
totalPrice: { $sum: "$request.request.txnAmount"},
count: { $sum: 1 }
}
}])
You can also use totalPrice: { $sum: "$txnAmount"} to display the sum of your transaction amount. I was working on the GET request of Total Sales in the e-shop that I developed, and I had faced the same issue. Just for your reference, here is my code:
//GET total sales data for the ADMIN
router.get('/get/totalsales', async(req, res) => {
const totalSales = await Order.aggregate([
{ $group: { _id: null, totalSales : { $sum: '$totalPrice' }}} // Use $totalPrice instead of totalPrice
])
if(!totalSales) {
return res.status(400).send('The sales cannot be generated')
}
res.send({totalSales: totalSales})
})
Hope this helps :)

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}