MongoDB $addFields, add boolean field based on condition - javascript

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

Related

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 Aggregate - Grouping by hour for today

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

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

Getting only last field in mongoose $in Operator

I am trying to find all person records that time between start and end and have place id in [1,2] This is my query:
Person.find({
time: {
$gte: start,
$lt: end
},
"place.place_id": { $in: [1,2] }
}, {
"time":1,
"place.$": 1
},
function (err, docs) {
if (err) {
res.status(500).send(err);
} else {
res.status(200).send(docs);
}
}
);
But I am getting only one place. This is my output
[
{
"_id": "5bffc1e9bd35e42020c05cf1",
"time": "2018-11-29T10:38:01.401Z",
"places": [
{
"_id": "5bffc1e9bd35e42020c05de3",
"place_id": 1,
"place_name": "test1"
}
]
},
{
"_id": "5bffc256bd35e42020c05de4",
"time": "2018-11-29T10:40:01.324Z",
"places": [
{
"_id": "5bffc256bd35e42020c05ed6",
"place_id": 1,
"place_name": "test1",
}
]
}
]
I am getting only place which have id 1 my desired output is
[
{
"_id": "5bffc1e9bd35e42020c05cf1",
"time": "2018-11-29T10:38:01.401Z",
"places": [
{
"_id": "5bffc1e9bd35e42020c05de3",
"place_id": 1,
"place_name": "test1"
},
{
"_id": "5bffc1e9bd35e42020c05de3",
"place_id": 2,
"place_name": "test2"
}
]
},
{
"_id": "5bffc256bd35e42020c05de4",
"time": "2018-11-29T10:40:01.324Z",
"places": [
{
"_id": "5bffc256bd35e42020c05ed6",
"place_id": 1,
"place_name": "test1",
},
{
"_id": "5bffc256bd35e42020c05ed6",
"place_id": 2,
"place_name": "test2",
}
]
}
]
Update
Tried this but this gives me empty object
Persons.find({
time: {
$gte: start,
$lt: end
},
places: {
$filter: {
input: "$places",
as: "place",
cond: { $in: ["$$place.place_id",[ 1,2 ]] }
}
}
},
function (err, docs) {
if (err) {
res.status(500).send(err);
} else {
res.status(200).send(docs);
}
}
);
};
What am I doing wrong?? I also tried $elemMatch, $all Operator but same output.
How can I get all these places?
Thanks.
Update2
I am entrying data after every 2 minutes this is start and end values
var end = new Date();
var start = new Date(end);
start.setMinutes(end.getMinutes() - 10);
You need to use aggregation for this. Something like
db.collection.aggregate([
{ "$match": {
"time": { "$gte": start, "$lt": end },
"place.place_id": { "$in": [1, 2] }
}},
{ "$project": {
"time": 1,
"places": {
"$filter": {
"input": "$places",
"as": "place",
"cond": { "$in": ["$$place.place_id", [1, 2]] }
}
}
}}
])

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