Aggregate and calculate with mongoDB - mongodb

Hello I heard that mongoDB was very good at aggregating data compared to, for example SQL server.
I tried to translate an SQL query to a mongoDB query but it's a complete failure :
For SQL server we had like 4 minutes which is manageable and now we top at 34 minutes for 128 days using this request in mongoDB :
function(thresholdObs, days)
{
var name = "period-" + days + "j"
var startDate = new Date('2020', '00', '01');
var endDate = new Date(startDate.getTime() + 1000 * 60 * 60 * 24 *[days]);
db.getCollection('measures').aggregate([
{
$match: {
$and: [
{
measureDate: {
$gte: startDate
}
},
{
measureDate: {
$lte: endDate
}
}
]
}
},
{
$addFields: {
multiplyIahobs: {
$cond: [
{$or: [
{$eq: ["$averageIAH", null]},
{$eq: ["$obs", null]}
]},
0,
{ $multiply: ["$averageIAH", "$obs"] }
]
},
multiplyPressionobs: {
$cond: [
{$or: [
{$eq: ["$averagePressure", null]},
{$eq: ["$obs", null]}
]},
0,
{ $multiply: ["$averagePressure", "$obs"] }
]
},
multiplyFuitesobs: {
$cond: [
{$or: [
{$eq: ["$averageLeakage", null]},
{$eq: ["$obs", null]}
]},
0,
{ $multiply: ["$averageLeakage", "$obs"] }
]
},
multiplyinspiratoryPressureobs: {
$cond: [
{$or: [
{$eq: ["$inspiratoryPressure", null]},
{$eq: ["$obs", null]}
]},
0,
{ $multiply: ["$inspiratoryPressure", "$obs"] }
]
},
multiplyexpiratoryPressureobs: {
$cond: [
{$or: [
{$eq: ["$expiratoryPressure", null]},
{$eq: ["$obs", null]}
]},
0,
{ $multiply: ["$expiratoryPressure", "$obs"] }
]
},
enoughDays: {
$cond: [ { $gte: ["$obs", thresholdObs ] }, 1, 0]
},
missingDays: {
$cond: [ { $lt: ["$obs", thresholdObs ] }, 1, 0]
},
daysWithoutUsage: {
$cond: [ { $eq: ["$obs", 0 ] }, 1, 0]
},
daysWithData: { $sum: 1 }
}
},
{
$group: {
_id: "$deviceID",
obsUsage: { $avg: "$obs" },
enoughDays: { $sum: "$enoughDays" },
missingDays: { $sum: "$missingDays" },
daysWithoutUsage: { $sum: "$daysWithoutUsage" },
daysWithData: { $sum: "$daysWithData" },
sumobs: { $sum: "$obs" },
sumMultiplyIahobs: { $sum: "$multiplyIahobs" },
sumMultiplyPressureObs: { $sum: "$multiplyPressionobs" },
sumMultiplyLeakageObs: { $sum: "$multiplyFuitesobs" },
sumMultiplyinspiratoryPressureobs: { $sum: "$multiplyinspiratoryPressureobs" },
sumMultiplyexpiratoryPressureobs: { $sum: "$multiplyexpiratoryPressureobs" },
}
},
{
$addFields: {
fullObs: { $divide: [ "$sumobs", days ] },
daysWithoutData: { $subtract: [ days, "$daysWithData" ]},
averageIAH: { $cond: [ { $eq: [ "$sumobs", 0 ] }, 0, { $divide: ["$sumMultiplyIahobs", "$sumobs"] } ] },
averagePressure: { $cond: [ { $eq: [ "$sumobs", 0 ] }, 0, { $divide: ["$sumMultiplyPressureObs", "$sumobs"] } ] },
averageLeakage: { $cond: [ { $eq: [ "$sumobs", 0 ] }, 0, { $divide: ["$sumMultiplyLeakageObs", "$sumobs"] } ] },
inspiratoryPressure: { $cond: [ { $eq: [ "$sumobs", 0 ] }, 0, { $divide: ["$sumMultiplyinspiratoryPressureobs", "$sumobs"] } ] },
expiratoryPressure: { $cond: [ { $eq: [ "$sumobs", 0 ] }, 0, { $divide: ["$sumMultiplyexpiratoryPressureobs", "$sumobs"] } ] },
}
},
{
$project: {
_id: 1,
[name] : {
obsUsage: "$obsUsage",
enoughDays: "$enoughDays",
missingDays: "$missingDays",
daysWithoutUsage: "$daysWithoutUsage",
daysWithData: "$daysWithData",
sumobs: "$sumobs",
fullObs: "$fullObs",
daysWithoutData: "$daysWithoutData",
averageIAH: "$averageIAH",
averagePressure: "$averagePressure",
averageLeakage: "$averageLeakage",
inspiratoryPressure: "$inspiratoryPressure",
expiratoryPressure: "$expiratoryPressure"
}
}
},
{
$merge: {
into: "periods",
on: "_id",
whenMatched: "merge",
whenNotMatched: "insert"
}
}
],{allowDiskUse: true})
}
Before throwing out the baby (mongoDB) with the bathwater (the query). I came here asking if my understanding on how to write a good query is off. Should I try to alter this query to make it work under 4 minutes ? Is it possible ? How ?
NB : measures collection contains 374.670.449 documents.
Sample documents :
{
_id: ObjectId('6127a15fef44a9ed52a5bf62'),
deviceId: 5,
measureDateAdded: ISODate('2013-03-15T10:30:35.753Z'),
measureDate: ISODate('2012-03-20T06:00:00.000Z'),
obs: 20,
averageIAH: NumberDecimal('5.50'),
averagePressure: NumberDecimal('6.30'),
averageLeakage: NumberDecimal('13.00'),
inspiratoryPressure: null,
expiratoryPressure: null
},
{
_id: ObjectId('6127a15fef44a9ed52a5bfc6'),
deviceId: 5,
measureDateAdded: ISODate('2013-03-15T10:30:40.063Z'),
measureDate: ISODate('2012-06-28T05:00:00.000Z'),
obs: 197,
averageIAH: NumberDecimal('5.30'),
averagePressure: NumberDecimal('6.90'),
averageLeakage: NumberDecimal('15.00'),
inspiratoryPressure: null,
expiratoryPressure: null
},
{
_id: ObjectId('6127aa2bef44a9ed52a0922a'),
deviceId: 367959,
measureDateAdded: ISODate('2019-01-19T14:13:19.620Z'),
measureDate: ISODate('2019-01-16T11:00:00.000Z'),
obs: 375,
averageIAH: NumberDecimal('2.00'),
averagePressure: NumberDecimal('9.60'),
averageLeakage: NumberDecimal('6.00'),
inspiratoryPressure: null,
expiratoryPressure: null
}
What cost the most seems to be the group with deviceId 4+minutes

Related

mongodb - Subtracts two numbers total to return the difference

Consider I have the following collection:
[
{
"total": 48.0,
"status": "CO"
},
{
"total": 11.0,
"status": "CA"
},
{
"total": 15916.0,
"status": "PE"
}
]
I need to realize the difference of PE status - (CO + CA).
The expected result is:
{
"_id" : null,
"total" : 15857.0
}
Use $switch to cater for different cases for your sum. Use $subtract to flip the sign for the partial sum.
db.collection.aggregate([
{
$group: {
_id: null,
total: {
"$sum": {
"$switch": {
"branches": [
{
"case": {
$eq: [
"$status",
"PE"
]
},
"then": "$total"
},
{
"case": {
$eq: [
"$status",
"CO"
]
},
"then": {
$subtract: [
0,
"$total"
]
}
},
{
"case": {
$eq: [
"$status",
"CA"
]
},
"then": {
$subtract: [
0,
"$total"
]
}
}
],
default: 0
}
}
}
}
}
])
Mongo Playground
Assuming these are the only status options, one way is to $group using $cond:
db.collection.aggregate([
{$group: {
_id: 0,
total: {
$sum: {$cond: [{$eq: ["$status", "PE"]}, "$total", {$multiply: ["$total", -1]}]}
}
}}
])
See how it works on the playground example

Check if current date between two dates in french MongoDB aggregation

I have a collection of restaurant documents in my MongoDB database with an hours field having the format below.
How can I check if a restaurant is open now using MongoDB aggregation?
My hours field has data like this (with french days):
{
"Lundi": [
"08:00",
"23:00"
],
"Mardi": [
"08:00",
"23:00"
],
"Mercredi": [
"08:00",
"23:00"
],
"Jeudi": [
"08:00",
"23:00"
],
"Vendredi": [
"08:00",
"23:00"
],
"Samedi": [
"08:00",
"23:00"
],
"Dimanche": [
"08:00",
"23:00"
]
}
Query
uses the system variable "$$NOW" to get the current time of server
converts each day open hours into a minutes range(to work on minutes also)
open = 01:00 close = 02:30 limits=[60,150]
if current hour is 01:30 then min-now=90
and then filters day=dayNow min-now inside the limits
(for example in our example case 90 is in the limit [60,150])
if at least 1 passed the filter store is open, else closed
Test code here
Query
aggregate(
[{"$project":{"_id":0}},
{"$project":
{"open":
{"$filter":
{"input":
{"$map":
{"input":{"$objectToArray":"$$ROOT"},
"in":["$$this.k", "$$this.v"]}},
"cond":
{"$let":
{"vars":
{"info":
{"day":{"$arrayElemAt":["$$r", 0]},
"limits":
[{"$add":
[{"$multiply":
[{"$toInt":
{"$arrayElemAt":
[{"$split":
[{"$arrayElemAt":[{"$arrayElemAt":["$$r", 1]}, 0]},
":"]},
0]}},
60]},
{"$toInt":
{"$arrayElemAt":
[{"$split":
[{"$arrayElemAt":[{"$arrayElemAt":["$$r", 1]}, 0]},
":"]},
1]}}]},
{"$add":
[{"$multiply":
[{"$toInt":
{"$arrayElemAt":
[{"$split":
[{"$arrayElemAt":[{"$arrayElemAt":["$$r", 1]}, 1]},
":"]},
0]}},
60]},
{"$toInt":
{"$arrayElemAt":
[{"$split":
[{"$arrayElemAt":[{"$arrayElemAt":["$$r", 1]}, 1]},
":"]},
1]}}]}],
"day-now":
{"$arrayElemAt":
[["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi",
"Samedi", "Dimanche"],
{"$subtract":[{"$dayOfWeek":"$$NOW"}, 1]}]},
"min-now":
{"$add":
[{"$multiply":[{"$hour":"$$NOW"}, 60]},
{"$minute":"$$NOW"}]}}},
"in":
{"$and":
[{"$eq":["$$info.day", "$$info.day-now"]},
{"$gte":
["$$info.min-now", {"$arrayElemAt":["$$info.limits", 0]}]},
{"$lte":
["$$info.min-now",
{"$arrayElemAt":["$$info.limits", 1]}]}]}}},
"as":"r"}}}},
{"$project":{"open":{"$ne":["$open", []]}, "date-now":"$$NOW"}}])
Really an ugly data model. You have to translate french day names into number and the time values into Date objects. Then you can filter by day and times:
db.collection.aggregate([
{ $unset: "_id" },
{
$project: {
opening_times: {
$map: {
input: { $objectToArray: "$$ROOT" },
in: {
day: {
$switch: {
branches: [
{ case: { $eq: ["Lundi", "$$this.k"] }, then: 1 },
{ case: { $eq: ["Mardi", "$$this.k"] }, then: 2 },
{ case: { $eq: ["Mercredi", "$$this.k"] }, then: 3 },
{ case: { $eq: ["Jeudi", "$$this.k"] }, then: 4 },
{ case: { $eq: ["Vendredi", "$$this.k"] }, then: 5 },
{ case: { $eq: ["Samedi", "$$this.k"] }, then: 6 },
{ case: { $eq: ["Dimanche", "$$this.k"] }, then: 7 }
]
}
},
open: {
$dateFromParts: {
year: { $year: "$$NOW" }, month: { $month: "$$NOW" }, day: { $dayOfMonth: "$$NOW" },
hour: { $toInt: { $first: { $split: [{ $first: "$$this.v" }, ":"] } } },
minute: { $toInt: { $last: { $split: [{ $first: "$$this.v" }, ":"] } } },
timezone: "Europe/Paris"
}
},
close: {
$dateFromParts: {
year: { $year: "$$NOW" }, month: { $month: "$$NOW" }, day: { $dayOfMonth: "$$NOW" },
hour: { $toInt: { $first: { $split: [{ $last: "$$this.v" }, ":"] } } },
minute: { $toInt: { $last: { $split: [{ $last: "$$this.v" }, ":"] } } },
timezone: "Europe/Paris"
}
}
}
}
}
}
},
{
$project: {
open_today: {
$first: {
$filter: {
input: "$opening_times",
cond: { $eq: ["$$this.day", { $isoDayOfWeek: "$$NOW" }] }
}
}
}
}
},
{
$project: {
restaurant: {
$cond: {
if: {
$and: [
{ $gte: ["$$NOW", "$open_today.open"] },
{ $lt: ["$$NOW", "$open_today.close"] },
]
},
then: "open",
else: "close"
}
}
}
}
])
See Mongo playground

How to group data by every hour

How do I get counts data grouped by every hour in 24 hours even if data is not present i.e. IF 0 will select 0
MonogDB 3.6
Input
[
{
"_id": ObjectId("5ccbb96706d1d47a4b2ced4b"),
"date": "2019-05-03T10:39:53.108Z",
"id": 166,
"update_at": "2019-05-03T02:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2ced4c"),
"date": "2019-05-03T10:39:53.133Z",
"id": 166,
"update_at": "2019-05-03T02:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2ced4d"),
"date": "2019-05-03T10:39:53.180Z",
"id": 166,
"update_at": "2019-05-03T20:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2ced7a"),
"date": "2019-05-10T10:39:53.218Z",
"id": 166,
"update_at": "2019-12-04T10:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2ced7b"),
"date": "2019-05-03T10:39:53.108Z",
"id": 166,
"update_at": "2019-05-05T10:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2cedae"),
"date": "2019-05-03T10:39:53.133Z",
"id": 166,
"update_at": "2019-05-05T10:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2cedad"),
"date": "2019-05-03T10:39:53.180Z",
"id": 166,
"update_at": "2019-05-06T10:45:36.208Z",
"type": "image"
},
{
"_id": ObjectId("5ccbb96706d1d47a4b2cedab"),
"date": "2019-05-10T10:39:53.218Z",
"id": 166,
"update_at": "2019-12-06T10:45:36.208Z",
"type": "image"
}
]
Implementation
db.collection.aggregate({
$match: {
update_at: {
"$gte": "2019-05-03T00:00:00.0Z",
"$lt": "2019-05-05T00:00:00.0Z"
},
id: {
"$in": [
166
]
}
}
},
{
$group: {
_id: {
$substr: [
"$update_at",
11,
2
]
},
count: {
"$sum": 1
}
},
},
{
$project: {
_id: 0,
hour: "$_id",
count: "$count"
}
},
{
$sort: {
hour: 1
}
})
Actual Output:
{
"count": 2,
"hour": "02"
},
{
"count": 1,
"hour": "20"
}
My expectation code show 24 hours event data is 0 or null and convert from example "02" as "02 AM" , "13" as "01 PM":
Expected Output
{
"count": 0,
"hour": "01" // 01 AM
},
{
"count": 2,
"hour": "02"
},
{
"count": 0,
"hour": "03"
},
{
"count": 0,
"hour": "04"
},
{
"count": 0,
"hour": "05"
},
{
"count": 1,
"hour": "20" // to 08 pm
}
Try this solution:
Explanation
We group by hour to count how many images are uploaded.
Then, we add extra field hour to create time interval (if you had v4.x, there is a better solution).
We flattern hour field (will create new documents) and split first 2 digits to match count and split last 2 digits to put AM / PM periods.
db.collection.aggregate([
{
$match: {
update_at: {
"$gte": "2019-05-03T00:00:00.0Z",
"$lt": "2019-05-05T00:00:00.0Z"
},
id: {
"$in": [
166
]
}
}
},
{
$group: {
_id: {
$substr: [
"$update_at",
11,
2
]
},
count: {
"$sum": 1
}
}
},
{
$addFields: {
hour: [
"0000",
"0101",
"0202",
"0303",
"0404",
"0505",
"0606",
"0707",
"0808",
"0909",
"1010",
"1111",
"1212",
"1301",
"1402",
"1503",
"1604",
"1705",
"1806",
"1907",
"2008",
"2109",
"2210",
"2311"
]
}
},
{
$unwind: "$hour"
},
{
$project: {
_id: 0,
hour: 1,
count: {
$cond: [
{
$eq: [
{
$substr: [
"$hour",
0,
2
]
},
"$_id"
]
},
"$count",
0
]
}
}
},
{
$group: {
_id: "$hour",
count: {
"$sum": "$count"
}
}
},
{
$sort: {
_id: 1
}
},
{
$project: {
_id: 0,
hour: {
$concat: [
{
$substr: [
"$_id",
2,
2
]
},
{
$cond: [
{
$gt: [
{
$substr: [
"$_id",
0,
2
]
},
"12"
]
},
" PM",
" AM"
]
}
]
},
count: "$count"
}
}
])
MongoPlayground
There's no "magic" solution, you'll have to hardcode it into your aggregation:
Heres an example using Mongo v3.2+ syntax with some $map and $filter magic:
db.collection.aggregate([
{
$match: {
update_at: {
"$gte": "2019-05-03T00:00:00.0Z",
"$lt": "2019-05-05T00:00:00.0Z"
},
id: {"$in": [166]}
}
},
{
$group: {
_id: {$substr: ["$update_at", 11, 2]},
count: {"$sum": 1}
}
},
{
$group: {
_id: null,
hours: {$push: {hour: "$_id", count: "$count"}}
}
},
{
$addFields: {
hours: {
$map: {
input: {
$concatArrays: [
"$hours",
{
$map: {
input: {
$filter: {
input: ["00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"],
as: "missingHour",
cond: {
$not: {
$in: [
"$$missingHour",
{
$map: {
input: "$hours",
as: "hourObj",
in: "$$hourObj.hour"
}
}
]
}
}
}
},
as: "missingHour",
in: {hour: "$$missingHour", count: 0}
}
}
]
},
as: "hourObject",
in: {
count: "$$hourObject.count",
hour: {
$cond: [
{$eq: [{$substr: ["$$hourObject.hour", 0, 1]}, "0"]},
{$concat: ["$$hourObject.hour", " AM"]},
{
$concat: [{
$switch: {
branches: [
{case: {$eq: ["$$hourObject.hour", "13"]}, then: "1"},
{case: {$eq: ["$$hourObject.hour", "14"]}, then: "2"},
{case: {$eq: ["$$hourObject.hour", "15"]}, then: "3"},
{case: {$eq: ["$$hourObject.hour", "16"]}, then: "4"},
{case: {$eq: ["$$hourObject.hour", "17"]}, then: "5"},
{case: {$eq: ["$$hourObject.hour", "18"]}, then: "6"},
{case: {$eq: ["$$hourObject.hour", "19"]}, then: "7"},
{case: {$eq: ["$$hourObject.hour", "20"]}, then: "8"},
{case: {$eq: ["$$hourObject.hour", "21"]}, then: "9"},
{case: {$eq: ["$$hourObject.hour", "22"]}, then: "10"},
{case: {$eq: ["$$hourObject.hour", "23"]}, then: "11"},
],
default: "None"
}
}, " PM"]
}
]
}
}
}
}
}
},
{
$unwind: "$hours"
},
{
$project: {
_id: 0,
hour: "$hours.hour",
count: "$hours.count"
}
},
{
$sort: {
hour: 1
}
}
]);
A short explanation of the $addFields stage: we first add hours that we're missing, we then merge the two arrays (of the original found hours and the "new" missing hours), finally we convert to the required output ("01" to "01 AM").
If you're using Mongo v4+ I recommend you change the $group _id stage to use $dateFromString as its more consistent.
_id: {$hour: {$dateFromString: {dateString: "$update_at"}}}
If you do do that, you'll have to update the $filter and $map section to use numbers and not strings and eventually using $toString to cast into the format you want, hence the v4+ requirement.
You should store date values as Date objects instead of strings. I would do the formatting like this:
db.collection.aggregate(
[
{ $match: { ... } },
{
$group: {
_id: { h: { $hour: "$update_at" } },
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
hour: {
$switch: {
branches: [
{ case: { $lt: ["$_id.h", 10] }, then: { $concat: ["0", { $toString: "$_id.h" }, " AM"] } },
{ case: { $lt: ["$_id.h", 13] }, then: { $concat: [{ $toString: "$_id.h" }, " AM"] } },
{ case: { $lt: ["$_id.h", 22] }, then: { $concat: ["0", { $toString: { $subtract: ["$_id.h", 12] } }, " PM"] } },
{ case: { $lt: ["$_id.h", 24] }, then: { $concat: [{ $toString: { $subtract: ["$_id.h", 12] } }, " PM"] } }
]
}
},
hour24: "$_id.h",
count: 1
}
},
{ $sort: { hour24: 1 } }
])
As non-American I am not familiar with AM/PM rules, esp. for midnight and midday but I guess you get the principle.
Here is the query you can test it out, for MongoDB 4.0+
i will be improving query and update
const query = [{
$match: {
update_at: {
"$gte": ISODate("2019-05-03T00:00:00.0Z"),
"$lt": ISODate("2019-05-05T00:00:00.0Z")
},
id: {
"$in": [
166
]
}
}
},
{
$group: {
_id: { $hour: "$update_at" },
count: {
"$sum": 1
}
},
},
{
$addFields: {
hourStr: { $toString: { $cond: { if: { $gte: ["$_id", 12] }, then: { $subtract: [12, { $mod: [24, '$_id'] }] }, else: "$_id" } } },
}
},
{
$project: {
formated: { $concat: ["$hourStr", { $cond: { if: { $gt: ["$_id", 12] }, then: " PM", else: " AM" } }] },
count: "$count",
hour: 1,
}
}]
If you want to output in Indian Time formate. then below code work!
const query = [
{
$match: {
update_at: {
"$gte": ISODate("2019-05-03T00:00:00.0Z"),
"$lt": ISODate("2019-05-05T00:00:00.0Z")
},
id: {
"$in": [
166
]
}
}
},
{
$project: {
"h": { "$hour": { date: "$update_at", timezone: "+0530" } },
}
},
{
$group:
{
_id: { $hour: "$h" },
count: { $sum: 1 }
}
}
];

how to calculate avg, median, min, max in mongodb query?

I have ListPrice field in collection on that price Ii have to calculate min, max, median, avg of all data, active standardStatus , sold standardStatus.
I have tried to calculate using aggregation and for loop but it won't work
db.collection('selected_properties').aggregate([
{ presentation_id : ObjectId(req.body.presentation_id),
checked_status : true}
},
{
$lookup : { from :'properties', localField : 'property_id', foreignField : '_id', as : 'property_info'}
},
{
$unwind : {path : '$property_info', preserveNullAndEmptyArrays : true}
},
{
$sort : {'property_info.ListPrice' : 1}
},
{
$group:{
_id: "$user_id",
minActiveListPrice: { $min: { $cond: [ {
$eq: [ "$property_info.StandardStatus", "A" ]},
'$property_info.ListPrice','' ] } },
maxActiveListPrice: { $max: { $cond: [ {
$eq: [ "$property_info.StandardStatus", "A" ]},
'$property_info.ListPrice',0 ] } },
avgActiveListPrice: { $avg: { $cond: [ {
$eq: [ "$property_info.StandardStatus", "A" ]},
'$property_info.ListPrice','' ] } },
medianActiveListprice: { $push: { $cond: [ {
$eq: [ "$property_info.StandardStatus", "A" ]},
'$property_info.ListPrice','' ] } },
minsoldListPrice: { $min: { $cond: [ {
$eq: [ "$property_info.StandardStatus", "S" ]},
'$property_info.ListPrice','' ] } },
maxsoldListPrice: { $max: { $cond: [ {
$eq: [ "$property_info.StandardStatus", "S" ]},
'$property_info.ListPrice',0 ] } },
avgsoldListPrice: { $avg: { $cond: [ {
$eq: [ "$property_info.StandardStatus", "S" ]},
'$property_info.ListPrice','' ] } },
avgPrice: { $avg: "$property_info.ListPrice" },
maxPrice: { $max: "$property_info.ListPrice" },
minPrice: { $min: "$property_info.ListPrice" },
}
median: { $push: "$property_info.ListPrice"}
}
},
db.collection('selected_properties').aggregate([
{
$match : { presentation_id : ObjectId(req.body.presentation_id),
checked_status : true}
},
{
$lookup : { from :'properties', localField : 'property_id',
foreignField : '_id', as : 'property_info'}
},
{
$unwind : {path : '$property_info', preserveNullAndEmptyArrays : true}
},
{
$sort : {'property_info.ListPrice' : 1}
},
{
$group:
{
_id: "$user_id",
minActiveListPrice: { $min: { $cond: [ {
$eq: [ "$property_info.StandardStatus", "A" ]},
'$property_info.ListPrice','' ] } },
maxActiveListPrice: { $max: { $cond: [ {
$eq: [ "$property_info.StandardStatus", "A" ]},
'$property_info.ListPrice',0 ] } },
avgActiveListPrice: { $avg: { $cond: [ {
$eq: [ "$property_info.StandardStatus", "A" ]},
'$property_info.ListPrice','' ] } },
medianActiveListprice: { $push: { $cond: [ {
$eq: [ "$property_info.StandardStatus", "A" ]},
'$property_info.ListPrice',null ] } },
avgPrice: { $avg: "$property_info.ListPrice" },
maxPrice: { $max: "$property_info.ListPrice" },
minPrice: { $min: "$property_info.ListPrice" },
median: { $push: "$property_info.ListPrice"}
}
},
{ "$project": {
"minActiveListPrice":1,
"maxActiveListPrice":1,
"avgActiveListPrice":1,
"avgPrice": 1,
"maxPrice": 1,
"minPrice": 1,
"medianActiveListpricevalue": {
$let: {
vars: {
arr: { $filter: {
input: "$medianActiveListprice",
as: "aa",
cond: {$ne:["$$aa",null]}
}},
},
in: { "$cond": {
"if": {
"$eq": [{$mod: [ {$size:"$$arr"}, 2 ]}, 0]
},
"then": {
$avg:[
{ $arrayElemAt: [ "$$arr", {$subtract:[{$divide: [ {$size:"$$arr"}, 2 ]},1]}]},
{ $arrayElemAt: [ "$$arr", {$divide: [ {$size:"$$arr"}, 2 ]}]}
]
},
"else": {
$arrayElemAt: [ "$$arr",{$floor : {$divide: [ {$size:"$$arr"}, 2 ]}}]
}
}}
}
},
"medianvalue":{ "$cond": {
"if": {
"$eq": [{$mod: [ {$size:"$median"}, 2 ]}, 0]
}
"then": {
$avg:[
{ $arrayElemAt: [ "$median", {$subtract:[{$divide: [ {$size:"$median"}, 2 ]},1]}]},
{ $arrayElemAt: [ "$median", {$divide: [ {$size:"$median"}, 2 ]}]}
]
},
"else": {
$arrayElemAt: [ "$median",{$floor : {$divide: [ {$size:"$median"}, 2 ]}}]
}
}}
} }
])

MongoError: The $cond accumulator is a unary operator

Consider this pipeline.
let Pipeline = [
{ $match: {
}},
{ $group: {
workHours: { $sum: { $divide: [ { $subtract: ['$workTime.end', '$workTime.start'] } , { $multiply: [3600, 1000] }] }},
breakHours: { $sum: { $divide: [ { $subtract: ['$breakTime.end', '$breakTime.start'] } , { $multiply: [3600, 1000] }] }},
weekEndHours: {
$cond: [
{ $or : [ { $eq : [{ $dayOfWeek : '$workTime.start' }, 1 ] }, {$eq : [{ $dayOfWeek : '$workTime.start' }, 7 ] }] },
{ $sum: { $divide: [ { $subtract: ['$workTime.end', '$workTime.start'] } , { $multiply: [3600, 1000] }] }},
0
]
}
}},
];
I'm trying to get sum of work hours, break hours and weekend hours. Work hours and break hours works fine. Now I want to use condition inside weekend hours which is if the day of the date is either 1 or 7 only then count the total hours in the key.
Then it is showing me the error 'The $cond accumulator is a unary operator'. I don't know what is wrong with my group query.
Got Solution and also extended pipeline conditions.
let Pipeline = [
{ $match: {
}},
{ $group: {
_id: '$memberId',
workHours: { $sum: { $divide: [ { $subtract: ['$workTime.end', '$workTime.start'] } , { $multiply: [3600, 1000] }] }},
breakHours: { $sum: { $divide: [ { $subtract: ['$breakTime.end', '$breakTime.start'] } , { $multiply: [3600, 1000] }] }},
weekEndHours: {
$sum: {
$cond: [
{ $or : [ { $eq : [{ $dayOfWeek : { date: '$workTime.start', timezone: timezoneOffset}}, 1 ] }, {$eq : [{ $dayOfWeek : { date: '$workTime.start', timezone: timezoneOffset}}, 7 ] }] },
{ $divide: [ { $subtract: ['$workTime.end', '$workTime.start'] } , { $multiply: [3600, 1000] }] },
0
]
}
},
weekDayHours: {
$sum: {
$cond: [
{ $or : [
{ $eq : [{ $dayOfWeek : { date: '$workTime.start', timezone: timezoneOffset}}, 2 ] },
{ $eq : [{ $dayOfWeek : { date: '$workTime.start', timezone: timezoneOffset}}, 3 ] },
{ $eq : [{ $dayOfWeek : { date: '$workTime.start', timezone: timezoneOffset}}, 4 ] },
{ $eq : [{ $dayOfWeek : { date: '$workTime.start', timezone: timezoneOffset}}, 5 ] },
{ $eq : [{ $dayOfWeek : { date: '$workTime.start', timezone: timezoneOffset}}, 6 ] }
]
},
{ $divide: [ { $subtract: ['$workTime.end', '$workTime.start'] } , { $multiply: [3600, 1000] }] },
0
]
}
},
}},
{ $addFields:{
totalWorkedHours: { $subtract: [ '$workHours', '$breakHours'] }
}}
];