MongoDB $group + $project + weekly average - mongodb

I'm trying to get the average of a puntations for some stores weekly in Mongo but it don't work. Could u help me?
db.collection.aggregate(
[
{$match:{storeId:{$in:[
ObjectId("e069d1b76557685b9e235v"),ObjectId("e069d1b76557685b9t7j8n"),
ObjectId("e069d1b76557685b9e2fg6"),ObjectId("e069d1b76557685b9p56r2")
]}}},
{$group:{_id:"$storeId", week: { date: new Date("$createdAt") }, totalPoints: {$sum: "$points"}, averagePoints: {$avg: "$points"}} },
{$sort: {totalPoints:-1}}
])
It doesn't work. But if I delete the part of the week, the code work it but make a wrong average
db.collection.aggregate(
[
{$match:{storeId:{$in:[
ObjectId("e069d1b76557685b9e235v"),ObjectId("e069d1b76557685b9t7j8n"),
ObjectId("e069d1b76557685b9e2fg6"),ObjectId("e069d1b76557685b9p56r2")
]}}},
{$group:{_id:"$storeId", totalPoints: {$sum: "$points"}, averagePoints: {$avg: "$points"}} },
{$sort: {totalPoints:-1}}
])

For groupBy week you can use $week function from mongo and add start Date in $match operation.
db.collection.aggregate(
[
{$match:{storeId:{$in:[
ObjectId("e069d1b76557685b9e235v"),ObjectId("e069d1b76557685b9t7j8n"),
ObjectId("e069d1b76557685b9e2fg6"),ObjectId("e069d1b76557685b9p56r2")
]},
timeStamp: {$gt: ISODate(createdAt)},
}},
{$group:{_id:"$storeId", week: { $week: '$timeStamp'}, totalPoints: {$sum: "$points"}, averagePoints: {$avg: "$points"}} },
{$sort: {totalPoints:-1}}
])

Related

How to I group by date while splitting multi-day records into multiples?

I have a mongodb collection that has the processName, start and stop fields. Each document thus describes a process that has occurred during a time interval. Each time interval can range from minutes to months, and there can be multiples.
I need to find out the total time each process ran each day. That, I think, requires splitting a process which ran for, say, 30 days, into 30 records, and then grouping per day per processName. Ideally, I wouldn't count processes running simultaneously twice, but if it's very hard to do, I can sacrifice that for the sake of readability.
One option is:
Create a list of dates according to the days difference
Use $zip to create array of timestamps for each day
$unwind
Calculate each document's key using $dateToString and working minutes using $dateDiff
Group and format
db.collection.aggregate([
{$set: {
days: {$map: {
input: {$range: [1,
{$add: [{$dateDiff: {startDate: "$start", endDate: "$stop", unit: "day"}}, 1]}]},
in: {
$dateAdd: {startDate: {$dateTrunc: {date: "$start", unit: "day"}},
unit: "day",
amount: "$$this"
}}
}}
}},
{$project: {
_id: 0,
processName: 1,
timestamp: {$zip: {
inputs: [
{$concatArrays: [["$start"], "$days"]},
{$concatArrays: ["$days", ["$stop"]]}
]
}}
}},
{$unwind: "$timestamp"},
{$project: {
processName: 1,
key: {$dateToString: {
date: {$dateTrunc: {date: {$first: "$timestamp"}, unit: "day"}},
format: "%Y-%m-%d"
}},
minutes: {$dateDiff: {
startDate: {$first: "$timestamp"},
endDate: {$last: "$timestamp"},
unit: "minute"
}}
}},
{$group: {
_id: {processName: "$processName", key: "$key"},
totalDurationHrs: {$sum: "$minutes"}
}},
{$project: {
_id: 0,
totalDurationHrs: {$round: [{$divide: ["$totalDurationHrs", 60]}, 1]},
date: "$_id.key",
processName: "$_id.processName"}}
])
See how it works on the playground example

How to get number of working days between two dates in mongodb aggregation

I have two dates Start Date: 2019-08-13 and End Date: 2019-08-20.I want the difference of end date to start date in terms of working days(excluding holidays) .so my results would be 4 as 2019-08-15 is a holiday and 2019-08-17, 2019-08-18 are weekends.
try this query:
db.collection.aggregate([
{$addFields: {days_in_millis: { $add: [{$subtract: ["$end_date", "$start_date"]}, 86400000] } }},
{$project: {end_date: 1, start_date: 1, millis_range: {$range: [0, "$days_in_millis", 86400000 ] } } },
{$project: {dates_in_between_inclusive: {
$map: {
input: "$millis_range",
as: "millis_count",
in: {$add: ["$start_date", "$$millis_count"]}
}
}}},
{$unwind: "$dates_in_between_inclusive"},
{$project: {date: "$dates_in_between_inclusive", is_not_weekend: {
$cond: {
if: { $in: [ {$dayOfWeek: "$dates_in_between_inclusive"}, [1, 7] ]},
then: 0,
else: 1
}}}},
{$match: {date: {$nin: holidays_dates_list}}},
{$group: {
_id: "$_id",
days: {$sum: "$is_not_weekend"}
}}
])
Assumptions:
1. every document has at least start_date and end_date fields which are mongodb dates.
2. "holidays_dates_list" is an array of dates which has holidays (may or may not include weekends)
Above query itself filters weekends. So, "holidays_dates_list" need not have weekends.

How to slice and mondify a mongodb aggregation

I am new to MongoDB and I am trying to project the amount field from a decimal to a double when queried. Right now I add $$ROOT to an array and then slice that array to just get a subset of transactions. I figure I can probably convert it when I push to the array ($group stage) or after slicing ($project stage) but I can't figure out how. Any tips? End goal is to present the sum, count, and most recent transactions (I need to add sorting logic later).
db.transactions.aggregate([
{$match:{}},
{$group:{
_id:null,
transactions:{
$push:"$$ROOT"
},
count:{$sum: 1},
sum: {$sum: "$amount"}
}},
{$project:{
_id:0,
summary:{ count: "$count", sum: {$convert:{input:"$sum",to:"double"}}},
transactions: {$slice:["$transactions", 2]}
}}
])
I figured out that $facet allow me to do this:
db.transactions.aggregate([{
$facet:{
summary: [
{$match:{}},
{$group:{
_id:null,
count:{$sum: 1},
sum: {$sum: "$amount"}
}},
{$project:{
_id:0,
count: "$count",
sum: {$convert:{input:"$sum",to:"double"}}}
}
],
transactions: [
{$match:{}},
{$sort:{date:-1,timestamp:-1}},
{$limit:1},
{$addFields:{amount: {$convert:{input:"$amount",to:"double"}}}}
]
}
}]);

how to getting the count with all records

I am trying this mongodb aggregation. I got the output but how can I get the count value with all records.
db.STREETLIGHTS.aggregate(
[
{$match : {"CreateDate":{$gt:new Date(ISODate("2018-04-09T23:54:16.064Z") - 24*60*60 * 1000)}}},
{ $project: {_id:1, SLC_ID:1,LONGITUDE:1,LATITUDE:1,DCUID:1,CUMILITIVE_KWH:1,LAMPSTATUS:1,CreateDate:1 } },
]
)
Please try this.
db.STREETLIGHTS.aggregate([
{$match : {"CreateDate":{$gt:new Date(ISODate("2018-04-09T23:54:16.064Z") - 24*60*60 * 1000)}}},
{$project: {_id:1, SLC_ID:1,LONGITUDE:1,LATITUDE:1,DCUID:1,CUMILITIVE_KWH:1,LAMPSTATUS:1,CreateDate:1 } },
{$group: {_id: null, count: {$sum: 1}}}
])
You need to use the $sum operator in the $project stage :
{$project: {
// Your others projections
total: {$sum: 1}
}

mongodb group every 2 weeks

This is how to group data weekly :
transactions.aggregate({
[
{$match: {status: "committed"}},
{$group: {
_id: {
$year: "$date",
$week: "$date"
},
count: {$sum: 1},
start_date: {$min: "$date"},
end_date: {$max: "$date"}
}}
]
});
The question is, how about grouping every 2 weeks and get the count?
Thank you.
CORRECT WAY ACCORDING TO ACCEPTED ANSWER
Thanks to Mzzl, this works for grouping every 2 weeks. Below is the complete version.
db.transactions.aggregate([
{$match: {status: "committed"}},
{$project: {
twoweekperiod: {
$subtract: [
{$week: "$date"}, {$mod: [{$week: "$date"}, 2] }
]
},
date:1,
status:1
}},
{$group: {
_id: {
year: {$year: "$date"},
twoweek: "$twoweekperiod"
},
count: {$sum: 1},
start_date: {$min: "$date"},
end_date: {$max: "$date"}
}}
])
Assuming $week contains the week number, you can create an 'every two weeks' number, and group by that. Try something like this:
{
$project: {
twoweekperiod: {
$subtract: [
'$week', {$mod: ['$week', 2] }
]
}, status:1, date:1, etc...
}
}
I don't know of a way to do an integer divide in a mongo query, so instead I subtract weeknumber mod 2 from the weeknumber, to get a number that changes every other week instead of every week. You could then try grouping by this number.