Mongodb Aggregate - Grouping by hour for today - javascript

I was wondering if someone could help me get my aggregation function right. I'm trying to count load of visits of my Fitness. I have Visits table where every visit (from, to) dates are saved. Also sub-visits to some services saved too.
[{
"from": ISODate("2020-01-17T10:23:27.645Z"),
"to": ISODate("2020-01-17T12:23:28.760Z"),
"visits": [
{
"from": ISODate("2020-01-17T10:23:27.646Z"),
"to": ISODate("2020-01-17T10:30:28.760Z"),
"service": ObjectId("5e05f17d6b7f7920d4a62403")
},
{
"from": ISODate("2020-01-17T10:30:29.760Z"),
"to": ISODate("2020-01-17T12:23:28.760Z"),
"service": ObjectId("5d05f17dt57f7920d4a62404")
}
],
...
},
{
"from": ISODate("2020-01-17T10:40:00.000Z"),
"to": ISODate("2020-01-17T11:30:28.760Z"),
"visits": [
{
"from": ISODate("2020-01-17T10:40:00.000Z"),
"to": ISODate("2020-01-17T11:30:28.760Z"),
"service": ObjectId("h505f17s6b2f7920d4a6295y")
}
],
...
}
]
I wanted to reach the result for today from 00:00 if current time is 13:35 then get until 13:00 like :
[{
'date': '2020-01-18T00:00:0.000Z'
'visits': 0
},
{
'date': '2020-01-18T00:00:0.000Z'
'visits': 0
},
{
'date': '2020-01-18T00:00:0.000Z'
'visits': 0
},
{
'date': '2020-01-18T01:00:0.000Z'
'visits': 0
},
{
'date': '2020-01-18T02:00:0.000Z'
'visits': 0
},
{
'date': '2020-01-18T03:00:0.000Z'
'visits': 0
},
{
'date': '2020-01-18T04:00:0.000Z'
'visits': 0
},
{
'date': '2020-01-18T05:00:0.000Z'
'visits': 0
},
{
'date': '2020-01-18T06:00:0.000Z'
'visits': 0
},
{
'date': '2020-01-18T07:00:0.000Z'
'visits': 0
},
{
'date': '2020-01-18T08:00:0.000Z'
'visits': 0
},
{
'date': '2020-01-18T09:00:0.000Z'
'visits': 0
},
{
'date': '2020-01-18T010:00:0.000Z'
'visits': 2
},
{
'date': '2020-01-18T11:00:0.000Z'
'visits': 2
},
{
'date': '2020-01-18T12:00:0.000Z'
'visits': 1
},
{
'date': '2020-01-18T13:00:0.000Z'
'visits': 0
}]
Tried too many examples, but nothing worked, Please help !

EXPLANATION
If we use $$NOW, we get current date. If you setup currDate, today, nextDay manually, MongoDB aggregation will limit desired values.
Now, we calculate how many hours from today (yyyy-MM-dd 00:00:00) - currDate (yyyy-MM-dd 13:35:00) ~ 13 hours and with $range operator create array
We flatten interval and apply filtering
You may use such query:
db.collection.aggregate([
{
$addFields: {
nextDay: {
$add: [
{
$dateFromString: {
dateString: {
$substr: [
{
$toString: "$$NOW"
},
0,
10
]
},
format: "%Y-%m-%d"
}
},
{
$multiply: [
24,
60,
60,
1000
]
}
]
},
today: {
$dateFromString: {
dateString: {
$substr: [
{
$toString: "$$NOW"
},
0,
10
]
},
format: "%Y-%m-%d"
}
},
currDate: {
$dateFromString: {
dateString: {
$substr: [
{
$toString: "$$NOW"
},
0,
13
]
},
format: "%Y-%m-%dT%H",
timezone: "-01"
}
}
}
},
{
$addFields: {
interval: {
$range: [
0,
{
$add: [
{
$divide: [
{
$subtract: [
"$currDate",
"$today"
]
},
{
$multiply: [
60,
60,
1000
]
}
]
},
1
]
},
1
]
}
}
},
{
$unwind: "$interval"
},
{
$addFields: {
date: {
$add: [
"$today",
{
$multiply: [
"$interval",
60,
60,
1000
]
}
]
},
nextHour: {
$add: [
"$today",
{
$multiply: [
{
$add: [
"$interval",
1
]
},
60,
60,
1000
]
}
]
}
}
},
{
$project: {
_id: 0,
date: 1,
today: 1,
nextDay: 1,
visits: {
$reduce: {
input: "$visits",
initialValue: 0,
in: {
$add: [
"$$value",
{
$cond: [
{
$or: [
{
$and: [
{
$gte: [
"$$this.from",
"$date"
]
},
{
$lte: [
"$$this.from",
"$nextHour"
]
}
]
},
{
$and: [
{
$lte: [
"$date",
"$$this.to"
]
},
{
$gte: [
"$date",
"$$this.from"
]
}
]
}
]
},
1,
0
]
}
]
}
}
}
}
},
{
$match: {
$expr: {
$and: [
{
$gte: [
"$date",
"$today"
]
},
{
$lte: [
"$date",
"$nextDay"
]
},
]
}
}
},
{
$group: {
_id: "$date",
visits: {
$sum: "$visits"
}
}
},
{
$sort: {
_id: 1
}
}
])
MongoPlayground
For JS setup, click here
You may try this solution if you want more elegant way

Related

Mongo DB query to return count?

I have two collections Course and File.
This is my data from Course collection
[{
_id: 1,
name: "Course1",
price: "100"
},
{
_id: 2,
name: "Course2",
price: "200"
}]
This is my data from files collection
[{
_id: 1,
name: "File1",
isFree: true,
courseId: 1
}
{
_id: 2,
name: "File2",
isFree: false,
courseId: 1
}
{
_id: 3,
name: "File3",
isFree: false,
courseId: 1
}]
I want to retrieve Course 1 with Course name,Count of files which are free,Count of files which are not free.
My attempt:
Course.aggregate([
{$lookup:{
from:"files",
localField: "_id",
foreignField: "courseId",
as: "files"
}},
])
This is my response If I execute my above query:
[
{
"_id": "6161bdcc0aada09d81598840",
"name": "Course1",
"files": [
{
"_id": "6166a0814ad3f56b71232b9c",
"name": "File1.jpeg",
"isFree":true
},
{
"_id": "6166a0814ad3f56b71232b9d",
"name": "File2.jpeg",
"isFree":false
},
{
"_id": "6166a0814ad3f56b71232b9v",
"name": "File3.jpeg",
"isFree":false
}
]
}
]
I want my output as:
[
{
"_id": "6161bdcc0aada09d81598840",
"name": "Course1",
"files": {"free": 1,"paid": 2}
}
]
You can try lookup with pipeline,
$lookup with pipeline, pass courseId in let
$match courseId condition
$group by isFeee and get total count
$project to show required fields in k(key) and v(value) format
$cond to check if isFree true then return "free" otherwise return "paid"
$addFields to update files array
$arrayToObject convert files key-value array to an object
$ifNull to check if property is not exists then set 0
await Course.aggregate([
{
$lookup: {
from: "files",
let: { courseId: "$_id" },
pipeline: [
{ $match: { $expr: { $eq: ["$$courseId", "$courseId"] } } },
{
$group: {
_id: "$isFree",
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
k: { $cond: ["$_id", "free", "paid"] },
v: "$count"
}
}
],
as: "files"
}
},
{
$addFields: {
files: { $arrayToObject: "$files" }
}
},
{
$addFields: {
"files.free": { $ifNull: ["$files.free", 0] },
"files.paid": { $ifNull: ["$files.paid", 0] }
}
}
])
Playground
Try following:
db.course.aggregate([
{
$lookup: {
from: "files",
localField: "_id",
foreignField: "courseId",
as: "files",
},
},
{
$unwind: {
path: "$files",
preserveNullAndEmptyArrays: true,
},
},
{
$group: {
_id: "$_id",
name: {
$first: "$name",
},
price: {
$first: "$price",
},
free: {
$sum: {
$cond: [
{
$eq: ["$files.isFree", true],
},
1,
0,
],
},
},
paid: {
$sum: {
$cond: [
{
$eq: ["$files.isFree", false],
},
1,
0,
],
},
},
},
},
{
$addFields: {
"files.free": "$free",
"files.paid": "$paid",
},
},
{
$project: {
_id: 1,
name: 1,
files: 1,
price: 1,
},
},
]);

display week of day for date stored in mongoDB

I want to display for each day in the last 7 days mount of purchases in the following format:
{
"sunday":30,
"monday":20,
...
}
one purchase on the database looks like this:
{
_id: 603fcbcc691d8a5ecc320059
productId: "603fc917a569f565687e2626"
clientId: "1"
totalPrice: 50
date: 2021-03-02T00:00:00.000+00:00 // a date object
}
Purchase.aggregate([
{ "$match": { "date": {$gte: new Date((new Date().getTime() - (7 * 24 * 60 * 60 * 1000))) } } },
{ "$group": {
"_id": { "day": { $substrCP: [ "$date", 0, 10 ] } },
"count": { $sum: 1 }
}},
{ "$sort" : { "_id.day": 1}},
])
i run this code and i got:
[
{
"_id": {
"day": "2021-02-28"
},
"count": 30
},
{
"_id": {
"day": "2021-03-01"
},
"count": 20
}
]
instead of using switch statement i used this:
before agregate() i initialized an array with values:
const days = ['','Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
and did the agregate like that:
Purchase.aggregate([
{ "$match": { "date": { $gte: new Date((new Date().getTime() - (7 * 24 * 60 * 60 * 1000))) } } },
{
"$group": {
"_id": "$date",
"count": { $sum: 1 }
}
},
{ "$sort": { "_id": 1 } },
{
"$project": {
"_id": 0,
"count": 1,
"day": { $arrayElemAt: [ days, { $dayOfWeek: "$_id" } ] }
}
},
{
"$group": {
"_id": null,
"data": { $push: { k: "$day", v: "$count" } }
}
},
{
"$project": {
"data": { $arrayToObject: "$data" },
"_id": 0
}
}
])
i run this and i got the wanted answer:
[
{
"data": {
"Sun": 30,
"Mon": 20
}
}
]
This may help you, but I could not find any readymade method in mongodb which gives me Sunday for value 1. thats why keeping switch statement.
collection.aggregate([
{ "$match": { "date": { $gte: new Date((new Date().getTime() - (7 * 24 * 60 * 60 * 1000))) } } },
{
"$group": {
"_id": { $dayOfWeek: "$date" },
"count": { $sum: 1 }
}
},
{ "$sort": { "_id": 1 } },
{
$group: {
_id: null,
data: {
$push: {
k: {
$switch: {
branches: [
{
case: { $eq: ["$_id", 1] },
then: "monday"
},
{
case: { $eq: ["$_id", 2] },
then: "tuesday"
},
{
case: { $eq: ["$_id", 3] },
then: "wednesday"
},
{
case: { $eq: ["$_id", 4] },
then: "thursday"
},
{
case: { $eq: ["$_id", 5] },
then: "friday"
},
{
case: { $eq: ["$_id", 6] },
then: "saturday"
},
{
case: { $eq: ["$_id", 7] },
then: "sunday"
}]
}
},
v: "$count"
}
}
}
},
{
$project: {
data: { $arrayToObject: "$data" }
}
}
])

MongoDB $addFields, add boolean field based on condition

I have a list of places documents which have opening times, which is an object detailed below:
"openingTimes": {
"Wednesday": [ // array of shifts
{
"startTime": { "hour": 1, "minute": 0 }, // shift starts
"endTime": { "hour": 10, "minute": 0} //shift ends
},
{
"startTime": { "hour": 15, "minute": 0 },
"endTime": { "hour": 23, "minute": 59 }
}
],
"Thursday": [
{
"startTime": { "hour": 0, "minute": 0 },
"endTime": { "hour": 23, "minute": 59 }
}
],
}
I need to add a field to indicate if the place is open now or not,
{
$addFields: {
isOpenNow: {
$and: [
{
$or: [
{
$lt: ['$openingTimes.Wednesday.startTime.hour', 14]
},
{
$and: [
{
$lte: ['$openingTimes.Wednesday.startTime.hour', 14]
},
{
$lte: ['$openingTimes.Wednesday.startTime.minute', 0]
}
]
}
]
},
{
$or: [
{
$gt: ['$openingTimes.Wednesday.endTime.hour', 14]
},
{
$and: [
{
$gte: ['$openingTimes.Wednesday.endTime.hour', 14]
},
{
$gte: ['$openingTimes.Wednesday.endTime.minute', 0]
}
]
}
]
}
]
}
but it ends up with a result of false I think $gte: ['$openingTimes.Wednesday.endTime.hour', 14] compares the array of hours to 14, is there is a way to indicate if the place is open based on it's shifts.
You can try below aggregate:
{
isPlaceOPen: {
$cond: {
if: { $gt: [{ $size: {
$filter: {
input: '$openingTimes.Wednesday',
as: 'shift',
cond: {
$and: [
{
$or: [
{
$lt: [
'$$shift.startTime.hour',
14
]
},
{
$and: [
{
$lte: [
'$$shift.startTime.hour',
14
]
},
{
$lte: [
'$$shift.startTime.minute',
0
]
}
]
}
]
},
{
$or: [
{
$gt: [
'$$shift.endTime.hour',
14
]
},
{
$and: [
{
$gte: [
'$$shift.endTime.hour',
14
]
},
{
$gte: [
'$$shift.endTime.minute',
0
]
}
]
}
]
}
]
}
}
} }, 0] },
then: true,
else: false,
},
}
}

Mongobooster aggregate function running error

when i am trying to run the query using aggregate function in mongobooster on windows 7 32 bit pc...its giving me this error..please help me to resolve this error...
"message" : "not authorized on strict to execute command { aggregate: 'PermanentBookings', pipeline: [ { $match: { $or: [ { city: 'Torino' } ], init_date: { $gte: new Date(1506816000000), $lte: new Date(1509494399000) } } }, { $project: { _id: 0.0, moved: { $ne: [ { $arrayElemAt: [ '$origin_destination.coordinates', 0.0 ] }, { $arrayElemAt: [ '$origin_destination.coordinates', 1.0 ] } ] }, duration: { $devide: [ { $subtract: [ '$final_time', '$init_time' ] }, 60.0 ] }, hourDay: { $hour: '$init_date' }, day: { $dayOfMonth: '$init_date' } } }, { $match: { moved: true, duration: { $lte: 180.0, $gt: 2.0 } } }, { $sort: { day: 1.0, hourDay: 1.0 } }, { $skip: 0.0 } ], cursor: {} }",
"stack" : "MongoError: not authorized on strict to execute command { aggregate: 'PermanentBookings', pipeline: [ { $match: { $or: [ { city: 'Torino' } ], init_date: { $gte: new Date(1506816000000), $lte: new Date(1509494399000) } } }, { $project: { _id: 0.0, moved: { $ne: [ { $arrayElemAt: [ '$origin_destination.coordinates', 0.0 ] }, { $arrayElemAt: [ '$origin_destination.coordinates', 1.0 ] } ] }, duration: { $devide: [ { $subtract: [ '$final_time', '$init_time' ] }, 60.0 ] }, hourDay: { $hour: '$init_date' }, day: { $dayOfMonth: '$init_date' } } }, { $match: { moved: true, duration: { $lte: 180.0, $gt: 2.0 } } }, { $sort: { day: 1.0, hourDay: 1.0 } }, { $skip: 0.0 } ], cursor: {} }" +
"at Function.MongoError.create (C:\\Users\\Pinky\\AppData\\Local\\mongobooster\\app-3.5.7\\resources\\app.asar\\node_modules\\mongodb-core\\lib\\error.js:31:11)" +
"at queryCallback (C:\\Users\\Pinky\\AppData\\Local\\mongobooster\\app-3.5.7\\resources\\app.asar\\node_modules\\mongodb-core\\lib\\cursor.js:212:36)" +
"at C:\\Users\\Pinky\\AppData\\Local\\mongobooster\\app-3.5.7\\resources\\app.asar\\node_modules\\mongodb-core\\lib\\connection\\pool.js:469:18" +
"at _combinedTickCallback (internal/process/next_tick.js:67:7)" +
"at process._tickCallback (internal/process/next_tick.js:98:9)",
"name" : "MongoError",
"ok" : 0,
"errmsg" : "not authorized on strict to execute command { aggregate: 'PermanentBookings', pipeline: [ { $match: { $or: [ { city: 'Torino' } ], init_date: { $gte: new Date(1506816000000), $lte: new Date(1509494399000) } } }, { $project: { _id: 0.0, moved: { $ne: [ { $arrayElemAt: [ '$origin_destination.coordinates', 0.0 ] }, { $arrayElemAt: [ '$origin_destination.coordinates', 1.0 ] } ] }, duration: { $devide: [ { $subtract: [ '$final_time', '$init_time' ] }, 60.0 ] }, hourDay: { $hour: '$init_date' }, day: { $dayOfMonth: '$init_date' } } }, { $match: { moved: true, duration: { $lte: 180.0, $gt: 2.0 } } }, { $sort: { day: 1.0, hourDay: 1.0 } }, { $skip: 0.0 } ], cursor: {} }",
"code" : 13,
"codeName" : "Unauthorized"
}

how to write the single aggregation in the query

I am writing the first query greater than equal to CREATE_DATE and less then CREATE_DATE I got the answer ofter calculate the duration write another query but how to write the single query in the aggregation.
Query 1
db.lights.aggregate({
$match: {
CREATE_DATE: {
$gte: ISODate("2018-01-24T20:05:30.000Z"),
$lt: ISODate("2018-02-24T20:05:30.000Z")
}
}
});
Result
{
"_id": ObjectId("5a9a74843711955836a8b4b5"),
"SWITCHID": "Z4-W40-SS451A/4",
"CREATE_DATE": ISODate("2018-01-24T20:05:30Z"),
"RECEIVEDDATE": ISODate("2018-02-24T20:05:45Z"),
"STATUS": "LIGHTS ON"
}
Query 2
db.lights.aggregate([
{
$addFields: {
offduration: {
$divide: [
{
$subtract: [
"$RECEIVEDDATE",
"$CREATE_DATE"
]
},
3600000
]
}
}
}
]);
Result
{
"_id": ObjectId("5a9a75af3711955836a8b4c8"),
"SWITCHID": "Z4-W40-SS451A/5",
"CREATE_DATE": ISODate("2018-02-24T20:05:30Z"),
"RECEIVEDDATE": ISODate("2018-02-24T20:05:45Z"),
"STATUS": "LIGHTS ON",
"offduration": 0.004166666666666667
}
You need to improve your query like this :
db.lights.aggregate({
$match: {
CREATE_DATE: {
$gte: ISODate("2018-01-24T20:05:30.000Z"),
$lt: ISODate("2018-02-24T20:05:30.000Z")
}
}
},
{
$addFields: {
offduration: {
$divide: [
{
$subtract: [
"$RECEIVEDDATE",
"$CREATE_DATE"
]
},
3600000
]
}
}
},
{
$project: {
_id: 1,
SWITCHID: 1,
CREATE_DATE: 1,
RECEIVEDDATE: 1,
STATUS: 1,
offduration: '$offduration',
avgduration: { $avg: "$offduration" }
}
});

Categories

Resources