mongo Aggregate and return results by months - mongodb

I have this...
$group: {
'_id': this.userId,
'hours': {
$sum: '$hours'
},
'magazines': {
$sum: '$magazines'
},
'brochures': {
$sum: '$brochures'
},
'books': {
$sum: 'books'
}
}
}, {
$project: {
hours: '$hours',
magazines: '$magazines',
brochures: '$brochures',
books: '$books'
}
... which returns the sum of the above fields.
What I want to do is, return the total sum of, say, hours for a particular user, grouped into months.
Something like:
January:
userId:
hours: 10
magazines: 10
....
February:
userId:
hours:2
magazines: 2
etc

Use the following aggregation pipeline which uses the date aggregation operators $year and $month to get the total sums for the given attributes per user grouped into months:
var pipeline = [
{
"$group": {
"_id": {
"userId": "$userId"
"year": { "$year": "$date" }
"month" { "$month": "$date" }
},
'hours': {
$sum: '$hours'
},
'magazines': {
$sum: '$magazines'
},
'brochures': {
$sum: '$brochures'
},
'books': {
$sum: 'books'
}
]
}
]
db.collection.aggregate(pipeline);
To get the exact schema where you convert the month and userId values to keys, use the map() method from the cursor returned by the aggregate() method:
var result = db.collection.aggregate(pipeline).map(function (doc){
var obj = {};
obj[doc._id.month][doc._id.userId]["hours"] = doc.hours;
obj[doc._id.month][doc._id.userId]["magazines"] = doc.magazines;
obj[doc._id.month][doc._id.userId]["brochures"] = doc.brochures;
obj[doc._id.month][doc._id.userId]["books"] = doc.books;
return obj;
});
printjson(result);

This is the solution that works. Answer on MongoDB-User group:
[{
$group: {
_id: {
$month: "$createdAt"
},
hours: {
$sum: "$hours",
},
magazines: {
$sum: "$magazines"
},
brochures: {
$sum: "$brochures"
},
books: {
$sum: "$books"
}
}
}]

Related

How to calculate average records per month?

My records like this [{ createdAt }, {createdAt}, {createdAt} ]
I need average records per month.
january => 3 records
february => 2 records etc..
You can try to $group by month and year when counting and by month when averaging:
db.collection.aggregate([
{
$group: {
_id: {
month: {
$month: "$createdAt"
},
year: {
$year: "$createdAt"
},
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: {
month: "$_id.month"
},
average: {
$avg: "$count"
}
}
},
{
$project: {
_id: 0,
month: "$_id.month",
average: 1
}
}
])
Link to playground
Not fully clear what you mean by "average records per month" but I think it would be this:
db.collection.aggregate([
{
$group: {
_id: {
$dateTrunc: {
date: "$createdAt",
unit: "month"
}
},
count: { $count: {} }
}
},
{
$group: {
_id: null,
data: { $push: { k: { $toString: { $month: "$_id" } }, v: "$count" } }
}
},
{ $replaceWith: { $arrayToObject: "$data" } }
])
Getting the month name is not so easy, either you use a external library or build your own with $switch

How to group mongodb aggregate array of objects data To sum numbers on the same date

I'm trying to create charts but I can't combine the data to be like "Expected result below"
What can I use to return "Expected result".
I tried using $group in $group and $reduce and it didn't work well.
I hope someone can help me solve this task
Current result is
[
{
"_id":"5fd4c3586e83b334d97c5218",
"consumption":5,
"charts":[
{
"date":"2020-10",
"consumption":1
},
{
"date":"2020-10",
"consumption":1
},
{
"date":"2020-11",
"consumption":1
},
{
"date":"2020-11",
"consumption":1
}
]
}
]
Expected result is
[
{
"_id":"5fd4c3586e83b334d97c5218",
"consumption":5,
"charts":[
{
"date":"2020-10",
"consumption":2
},
{
"date":"2020-11",
"consumption":2
},
}
]
You need to go with aggregations
db.collection.aggregate([
{ $unwind: "$charts" },
{
$group: {
_id: { _id: "$_id", month: "$charts.date.month", year: "$charts.date.year" },
consumption: { $first: "$consumption" },
total: { $sum: "$charts.consumption" },
date: { $first: "$charts.date" }
}
},
{
$group: {
_id: "$_id._id",
consumption: { $first: "$consumption" },
charts: {
$push: {
date: "$date",
consumption: "$total"
}
}
}
}
])
Working Mongo playground

Mongodb aggregation , group by items for the last 5 days

I'm trying to get the result in some form using mongodb aggregation.
here is my sample document in the collection:
[{
"_id": "34243243243",
"workType": "TESTWORK1",
"assignedDate":ISODate("2021-02-22T00:00:00Z"),
"status":"Completed",
},
{
"_id": "34243243244",
"workType": "TESTWORK2",
"assignedDate":ISODate("2021-02-21T00:00:00Z"),
"status":"Completed",
},
{
"_id": "34243243245",
"workType": "TESTWORK3",
"assignedDate":ISODate("2021-02-20T00:00:00Z"),
"status":"InProgress",
}...]
I need to group last 5 days data in an array by workType count having staus completed.
Expected result:
{_id: "TESTWORK1" , value: [1,0,4,2,3] ,
_id: "TESTWORK2" , value: [3,9,,3,5],
_id : "TESTWORK3", value: [,,,3,5]}
Here is what I'm trying to do, but not sure how to get the expected result.
db.testcollection.aggregate([
{$match:{"status":"Completed"}},
{$project: {_id:0,
assignedSince:{$divide:[{$subtract:[new Date(),$assignedDate]},86400000]},
workType:1
}
},
{$match:{"assignedSince":{"lte":5}}},
{$group : { _id:"workType", test :{$push:{day:"$assignedSince"}}}}
])
result: {_id:"TESTWORK1": test:[{5},{3}]} - here I'm getting the day , but I need the count of the workTypes on that day.
Is there any easy way to do this? Any help would be really appreciated.
Try this:
db.testcollection.aggregate([
{
$match: { "status": "Completed" }
},
{
$project: {
_id: 0,
assignedDate: 1,
assignedSince: {
$toInt: {
$divide: [{ $subtract: [new Date(), "$assignedDate"] }, 86400000]
}
},
workType: 1
}
},
{
$match: { "assignedSince": { "$lte": 5 } }
},
{
$group: {
_id: {
workType: "$workType",
assignedDate: "$assignedDate"
},
count: { $sum: 1 }
}
},
{
$group: {
_id: "$_id.workType",
values: { $push: "$count" }
}
}
]);

MongoDB Aggregate Query, Logins Averages

let pipeline = [{
$match: {
time: { $gt: 980985600 },
user_id: mongoose.Types.ObjectId("60316a2e7641bd0017ced7b1")
}
},
{
$project: {
newDate: { '$toDate': "$time" },
user_id: '$user_id'
}
},
{
$group: {
_id: { week: { $week: "$newDate" }, year: { $year: "$newDate" }},
count: { $sum: 1 }
}
}]
I am currently trying to perform an aggregate through mongoose to find the average logins per week for a specific user. So far I have been able to get to the total number of logins each week, but was curious if there was a way to find the average of these final groupings within the same function. How would I go about doing this?
Just add one last stage to your query:
{
$group: {
_id: null,
avg: { $avg: "$count" }
}
}
So try this:
let pipeline = [
{
$match: {
time: { $gt: 980985600 },
user_id: mongoose.Types.ObjectId("60316a2e7641bd0017ced7b1")
}
},
{
$project: {
newDate: { '$toDate': "$time" },
user_id: '$user_id'
}
},
{
$group: {
_id: { week: { $week: "$newDate" }, year: { $year: "$newDate" } },
count: { $sum: 1 }
}
},
{
$group: {
_id: null,
avg: { $avg: "$count" }
}
}
];

How can I get a sum of sums using MongoDB aggregation?

I would like to get the total (sum) of the totalHoursForYear value. Here is my current aggregation:
const byYear = await this.workHistoryModel.aggregate([
{
$group: {
_id: '$year',
history: {
$push: '$$ROOT'
},
totalHoursForYear: {
$sum: {
$add: [
{ $multiply: ['$eightHourDays', 8] },
{ $multiply: ['$tenHourDays', 10] },
{ $multiply: ['$twelveHourDays', 12] },
]
}
},
}
},
]);
Now I need to get a sum of the totalHoursForYear to get the total for all years. Is this possible? I can't seem to get the syntax correct. When I attempt to add another sum:
$group: {
...,
totalHours: {
$sum: {
$add: '$totalHoursForYear'
},
$push: '$$ROOT'
}
}
I get an error: "The field 'totalHours' must specify one accumulator"
When you need total sum, you can do another $group with _id:null which means to consider all documents
With your script, add the following to get the total and get the same structure.
{
$group: {
_id: null,
data: {
$push: "$$ROOT"
},
total: {
$sum: "$totalHoursForYear"
}
}
},
{
"$unwind": "$data"
},
{
"$addFields": {
_id: "$data._id",
history: "$data.history",
totalHoursForYear: "$data.totalHoursForYear",
_id: "$$REMOVE",
data: "$$REMOVE"
}
}
Working Mongo playground