MongoDB Aggregate Query, Logins Averages - mongodb

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

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

Aggregate Hourly Weekly Monthly Yearly data in mongodb

Q1. I need to filter data by created date and driverId then need to sum up the total by Hourly, Weekly, Monthly, and Yearly. I already checked with other solutions but it doesn't help much.
Sample Data:
[
{
id: "1",
created : "2022-01-04T03:22:18.739Z",
completed: "2022-01-06T03:53:28.463Z",
driverId: "B-72653",
total: 15,
},
{
id: "2",
created : "2022-01-01T03:22:18.739Z",
completed: "2022-01-02T03:53:28.463Z",
driverId: "B-72653",
total: 33
},
{
id: "3",
created : "2021-08-26T01:22:18.739Z",
completed: "2021-08-26T09:53:28.463Z",
driverId: "B-72653",
total: 43
},
{
id: "4",
created : "2021-03-26T02:22:18.739Z",
completed: "2021-03-26T07:53:28.463Z",
driverId: "B-73123",
total: 35
},
]
Response needed:
{
Hourly:[10,5,5,6,7,8,4,5,6,3,44,2,1,2,3,44,5,6,75,4,3,2,1], // 24 Hours (Each Hour Total)
Weekly:[10,30,34,45,56,67,78], // 7 days (Each Day Total)
Monthly:[10,30,34,45,56,67,78,55,44,33,22,12], // 12 Months (Each Month Total)
Yearly: [10,30] // Year Total (Each Year Total)
}
Q2. How can we filter nested array by-products > brand id and get the sum of product price by its id and filter by Hourly, Weekly, Monthly, Yearly?.
You can use $group with _id being $hour / $week / $month / $year to aggregate the sum. $push them into an array to get your expected result.
Use $facet to repeat the process for all 4 cases.
db.collection.aggregate([
{
"$facet": {
"Hourly": [
{
$group: {
_id: {
$hour: "$created"
},
total: {
$sum: "$total"
}
}
},
{
$sort: {
_id: 1
}
},
{
$group: {
_id: null,
result: {
$push: {
hour: "$_id",
total: "$total"
}
}
}
}
],
Weekly: [
{
$group: {
_id: {
"$week": "$created"
},
total: {
$sum: "$total"
}
}
},
{
$sort: {
_id: 1
}
},
{
$group: {
_id: null,
result: {
$push: {
week: "$_id",
total: "$total"
}
}
}
}
],
Monthly: [
{
$group: {
_id: {
$month: "$created"
},
total: {
$sum: "$total"
}
}
},
{
$sort: {
_id: 1
}
},
{
$group: {
_id: null,
result: {
$push: {
month: "$_id",
total: "$total"
}
}
}
}
],
Yearly: [
{
$group: {
_id: {
$year: "$created"
},
total: {
$sum: "$total"
}
}
},
{
$sort: {
_id: 1
}
},
{
$group: {
_id: null,
result: {
$push: {
year: "$_id",
total: "$total"
}
}
}
}
]
}
},
{
"$addFields": {
"Hourly": {
"$arrayElemAt": [
"$Hourly",
0
]
},
"Weekly": {
"$arrayElemAt": [
"$Weekly",
0
]
},
"Monthly": {
"$arrayElemAt": [
"$Monthly",
0
]
},
"Yearly": {
"$arrayElemAt": [
"$Yearly",
0
]
}
}
},
{
"$addFields": {
"Hourly": "$Hourly.result",
"Weekly": "$Weekly.result",
"Monthly": "$Monthly.result",
"Yearly": "$Yearly.result"
}
}
])
Here is the Mongo playground for your reference.

Aggregate by all days of month mongodb

Hey i need to get the sum of all totalPrice group by days
I get this result
but i need to fetch all rest days of month even if it returns 0
i need solution
this is my code
Order.aggregate([
{ $project: { yearMonthDay: { $dateToString: { format: "%Y-%m-%d", date: '$created' }}, totalPrice:"$totalPrice" }},
{ $group: { _id: "$yearMonthDay", count: { $sum: 1 }, total: {"$sum": "$totalPrice"} }},
{ $sort: { _id: -1 } },
{ $group: { _id: null, stats: { $push: "$$ROOT" }}},
{
$project: {
results: {
$map: {
input:{ $range:[16,31] },
as: 'day',
in: {
$let: {
vars: {
dateIndex: {
"$indexOfArray": ["$stats._id", {$dateToString:{ date:{$dateFromParts:{'year':2020, 'month':5, 'day':"$$day"}}, format:'%Y-%m-%d'}}]
}
},
in: {
$cond: {
if: { $ne: ["$$dateIndex", -1] },
then: { $arrayElemAt: ["$stats", "$$dateIndex"] },
else: { _id: {$dateToString:{ date:{$dateFromParts:{'year':2020, 'month':5, 'day':"$$day"}}, format:'%Y-%m-%d'}, count: 0, total: 0 } }
}
}
}
}
}
}
}
},
{ $unwind: "$results" },
{ $replaceRoot: { newRoot: "$results"}}
]
This query should work for you.
db.collectionName.aggregate([
{ $project: { yearMonthDay: { $dateToString: { format: "%Y-%m-%d", date: '$created' }}, totalPrice:"$totalPrice" }},
{ $group: { _id: "$yearMonthDay", count: { $sum: 1 }, total: {"$sum": "$totalPrice"} }},
{ $sort: { _id: -1 } },
{ $group: { _id: null, stats: { $push: "$$ROOT" }},
{
$project: {
results: {
$map: {
input: ["2020-05-16","2020-05-15","2020-05-14","2020-05-13","2020-05-12"],
as: "date",
in: {
$let: {
vars: {
dateIndex: {
"$indexOfArray": ["$stats._id", "$$date"]
}
},
in: {
$cond: {
if: { $ne: ["$$dateIndex", -1] },
then: { $arrayElemAt: ["$stats", "$$dateIndex"] },
else: { _id: "$$date", count: 0, total: 0 }
}
}
}
}
}
}
}
},
{ $unwind: "$results" },
{ $replaceRoot: { newRoot: "$results"}}
])
The First 3 steps is same as yours.
{ $group: { _id: null, stats: { $push: "$$ROOT" }} will push previous stage results into an arrray stats which we will use for lookup in later stage.
In last stage, we will create possible date range and iterate over that.
for each key in range.
"$indexOfArray": ["$stats._id", "$$date"] will check if date is present in stats array or not
Then we will use that index to fetch value from stats array otherwise push default values.
As these results are still under results, we will unwind that array and move to root.
If you server version is above 3.6,
we can simplify date range creation part as well. let's initialize input arrays as days using $range.
input:{ $range:[16,31] },
as: 'day'
and modifiy dateIndex part like this
dateIndex: {
"$indexOfArray": ["$stats._id", {$dateToString:{ date:{$dateFromParts:{'year':2020, 'month':5, 'day':"$$day"}}, format:'%Y-%m-%d'}]
}
And change default value part as well similarly.
else: { _id: {$dateToString:{ date:{$dateFromParts:{'year':2020, 'month':5, 'day':"$$day"}}, format:'%Y-%m-%d'}}, count: 0, total: 0 }
Or alternatively, we can also use concat for generating keys
dateIndex: {
"$indexOfArray": ["$stats._id", {$concat:["2020-05","-", {$convert:{input:"$$day", to:"string"}}]}]
}
// And default value
else: { _id: {$concat:["2020-05","-", {$convert:{input:"$$day", to:"string"}}]}, count: 0, total: 0 }
Similarly, you can run another loop for months as well.

How to get sum of counted records using group by in mongodb?

I am trying to get the sum of count which I get from group, match. How can I get the same.
I have this code...
VisitorCompany.aggregate(
[
{
$match: {
$and:[
{ entry_date: { $gt: start, $lt: end } }
]
}
},
{
$group:
{
_id:
{
day: { $dayOfMonth: "$entry_date" },
month: { $month: "$entry_date" },
year: { $year: "$entry_date" }
},
count: { $sum:1 },
entry_date: { $first: "$entry_date" }
}
},
{
$project:
{
entry_date:
{
$dateToString: { format: "%Y-%m-%d", date: "$entry_date" }
},
count: 1,
_id: 0
}
},
{ $sort : { entry_date : -1 } },
])
and the output is ...
{
"count": 2,
"entry_date": "2018-12-12"
},
{
"count": 1
"entry_date": "2018-12-11"
}
Is anyone have idea that how to get sum of count i.e. 3 (2+1), means total number of records before group. thanks in advance.
Below modified query of yours will be giving you the sum of count, I have just added the
$group:{_id:"", sum:{$sum: "$count"}}}
to the existing aggregation pipeline query
Modified query
VisitorCompany.aggregate(
[
{
$match: {
$and:[
{ entry_date: { $gt: start, $lt: end } }
]
}
},
{
$group:
{
_id:
{
day: { $dayOfMonth: "$entry_date" },
month: { $month: "$entry_date" },
year: { $year: "$entry_date" }
},
count: { $sum:1 },
entry_date: { $first: "$entry_date" }
}
},
{
$project:
{
entry_date:
{
$dateToString: { format: "%Y-%m-%d", date: "$entry_date" }
},
count: 1,
_id: 0
}
},
{ $sort : { entry_date : -1 } },
{$group:{_id:"", sum:{$sum: "$count"}}}
])
The result
{ "_id" : "", "sum" : 3 }

mongo Aggregate and return results by months

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