$group:{
_id:{
status:"$dir_status",
directions:"$direction"
},
"X": {
"$sum": { $cond: [{ "$eq": ["$dir_status", "0"] }, 1, 0] }
},
"Y": {
"$sum": { $cond: [{ "$eq": ["$dir_status", "1"] }, 1, 0] }
},
"Z": {
"$sum": { $cond: [{ "$eq": ["$dir_status", "2"] }, 1, 0] }
},
count:{$sum:1}
}
after an aggregation i got this output
[
{
_id: { level: 'd1' },
X: 0,
Y: 0,
Z: 0,
count: 3
},
{
_id: { level: 'd2' },
X: 0,
Y: 0,
Z: 0,
count: 3
},
{
_id: { level: 'd3' },
X: 0,
Y: 0,
Z: 0,
count: 4
},
]
Now i want to group it as
[
level:"$_id.level",
range:[ { dir:"X", count:"$X"}, { dir:"Y", count:"$Y"}, { dir:"Z", count:"$Z"}]
]
i used $push method but its not working combining three objects.
Help me to resolve this to get output like this
[{level:"d1", range:[{dir:"X", count:0}, {dir:"Y", count:5}]]
Related
I have searched a lot to use groupby based on the array field value, but I didn't get proper results in google, so I'm posting here.
I have tried my best, it works 50% need to correct my query can anyone help me with this
I have a database value like
{"_id": "62b0bec8922dc767f8b933b4",
"seatSeletion": [{
"rowNo": 0,
"columnNo": 0,
"seatNo": 3
}, {
"rowNo": 0,
"columnNo": 1,
"seatNo": 4
}],
"movieId": "62af1ff6cb38656a4ffe36aa",
"movieDate": "2022-06-20T18:14:38.133+00:00",
"movieTiming": "1:30 p.m",
},
{"_id": "62b0b91560f57e0cb220db02","seatSeletion": [{
"rowNo": 0,
"columnNo": 0,
"seatNo": 1
}, {
"rowNo": 0,
"columnNo": 1,
"seatNo": 2
}],
"movieId": "62af1ff6cb38656a4ffe36aa",
"movieDate": "2022-06-20T18:14:38.133+00:00",
"movieTiming": "1:30 p.m",
}
expected output
{
"seatSeletion": [
{
"rowNo": 0,
"columnNo": 0,
"seatNo": 1
},
{
"rowNo": 0,
"columnNo": 1,
"seatNo": 2
},
{
"_id": "62b0b90e60f57e0cb220db00",
"rowNo": 0,
"columnNo": 0,
"seatNo": 3
},
{
"_id": "62b0b90e60f57e0cb220db01",
"rowNo": 0,
"columnNo": 1,
"seatNo": 4
}
],
"movieTiming": "1:30 p.m",
"movieId": "62af1ff6cb38656a4ffe36aa",
"movieDate": "2022-06-20T11:03:37.000Z"
},
this is how I tried in my query
Bookings.aggregate([
{
$match: {
$and: [{ movieId: ObjectId(bookingParam.movieId) },
{ movieTiming: bookingParam.movieTiming },
{ movieDate: dateQuery },
]
}
},
{
$group: {
_id: {
seatSeletion: '$seatSeletion', movieTiming: '$movieTiming',
movieId: '$movieId', movieDate: '$movieDate', createdBy: "$createdBy", updatedBy: "$updatedBy", movies: "$movies"
}
}
},
{
$project: {
seatSeletion: '$_id.seatSeletion', movieTiming: '$_id.movieTiming',
movieId: '$_id.movieId', movieDate: '$_id.movieDate', movies: "$_id.movies",
_id: 0
}
}
])
but i got it like this
{
"seatSeletion": [
{
"_id": "62b0b91560f57e0cb220db03",
"rowNo": 0,
"columnNo": 0,
"seatNo": 1
},
{
"_id": "62b0b91560f57e0cb220db04",
"rowNo": 0,
"columnNo": 1,
"seatNo": 2
}
],
"movieTiming": "1:30 p.m",
"movieId": "62af1ff6cb38656a4ffe36aa",
"movieDate": "2022-06-20T11:03:37.000Z"
},
{
"seatSeletion": [
{
"_id": "62b0b90e60f57e0cb220db00",
"rowNo": 0,
"columnNo": 0,
"seatNo": 3
},
{
"_id": "62b0b90e60f57e0cb220db01",
"rowNo": 0,
"columnNo": 1,
"seatNo": 4
}
],
"movieTiming": "1:30 p.m",
"movieId": "62af1ff6cb38656a4ffe36aa",
"movieDate": "2022-06-20T11:03:37.000Z"
}
can anyone help me to fix this issue.
One option is using $reduce after the $group. It is important NOT to group by the seatSeletion as the value of this field is not common to these movies:
db.collection.aggregate([
{
$match: {
$and: [
{movieId: "62af1ff6cb38656a4ffe36aa"},
{movieTiming: "1:30 p.m"},
{movieDate: "2022-06-20T18:14:38.133+00:00"},
]
}
},
{
$group: {
_id: {movieTiming: "$movieTiming", movieId: "$movieId", movieDate: "$movieDate"},
seatSeletion: {$push: "$seatSeletion"}
}
},
{
$project: {
seatSeletion: {
$reduce: {
input: "$seatSeletion",
initialValue: [],
in: {$concatArrays: ["$$value", "$$this"]}
}
},
movieTiming: "$_id.movieTiming",
movieId: "$_id.movieId",
movieDate: "$_id.movieDate",
_id: 0
}
}
])
See how it works on the playground example
Another option is using $unwind instead of $reduce, but it is generally considered slower:
db.collection.aggregate([
{
$match: {
$and: [
{movieId: "62af1ff6cb38656a4ffe36aa"},
{movieTiming: "1:30 p.m"},
{movieDate: "2022-06-20T18:14:38.133+00:00"},
]
}
},
{$unwind: "$seatSeletion"},
{
$group: {
_id: {movieTiming: "$movieTiming", movieId: "$movieId", movieDate: "$movieDate"},
seatSeletion: {$push: "$seatSeletion"}
}
},
{
$project: {
seatSeletion: 1,
movieTiming: "$_id.movieTiming",
movieId: "$_id.movieId",
movieDate: "$_id.movieDate",
_id: 0
}
}
])
See how it works on the playground example - unwind
more output nearly you expect
{
"_id": {
"movieId": "62af1ff6cb38656a4ffe36aa",
"movieDate": "2022-06-20T18:14:38.133+00:00",
"movieTiming": "1:30 p.m"
},
"seatSeletion": [
{ "rowNo": 0,"columnNo": 0,"seatNo": 3
},
{ "rowNo": 0,"columnNo": 1,"seatNo": 4
},
{ "rowNo": 0,"columnNo": 0,"seatNo": 1
},
{ "rowNo": 0,"columnNo": 1,"seatNo": 2
}
]
}
query
db.collection.aggregate(
{
$match: {}
},
{
$unwind: {
path: '$seatSeletion'
}
},
{
$group: {
_id:
{
movieId: '$movieId',
movieDate: '$movieDate',
movieTiming: '$movieTiming'
},
seatSeletion:
{ $push: '$seatSeletion' }
}
}
)
I'm trying to create an aggregation that will compute the distribution of values across an array of objects and return an array of computed values.
Here is a sample document
[
{
"duration": 1208,
"dataPoints": 2,
"binMin": 0,
"binMax": 5000
},
{
"duration": 25735,
"dataPoints": 3,
"binMin": 5000,
"binMax": 10000
},
{
"duration": 0,
"dataPoints": 0,
"binMin": 10000,
"binMax": 20000
},
{
"duration": 54088,
"dataPoints": 2,
"binMin": 20000,
"binMax": 28817
}
]
I need to add up the durations for each object, then compute the distribution across each object and return a new array like so:
[
{
"duration": 1208,
"dataPoints": 2,
"binMin": 0,
"binMax": 5000,
"ratio": 0.014907874763979
},
{
"duration": 25735,
"dataPoints": 3,
"binMin": 5000,
"binMax": 10000,
"ratio": 0.317594500870037
},
{
"duration": 0,
"dataPoints": 0,
"binMin": 10000,
"binMax": 20000,
"ratio": 0
},
{
"duration": 54088,
"dataPoints": 2,
"binMin": 20000,
"binMax": 28817,
"ratio": 0.667497624365983
}
]
I am able to calculate the total duration and divide to get the ratio value but it seems to be only doing it to the first element of the array.
This is my aggregation so far:
[{$project: {
_id: '$_id',
username: 1,
uuid: 1,
data: '$stats.dataHistogram'
}}, {$unwind: {
path: '$data'
}}, {$group: {
_id: '$_id',
data_bin: {
$first: '$data'
},
total_duration: {
$sum: '$data.duration'
}
}}, {$project: {
_id: '$_id',
total_duration: 1,
data_bin: 1,
ratio: {
$divide: [
'$data_bin.duration',
{
$add: [
'$total_duration',
1
]
}
]
}
}}]
(I'm adding a 1 to the $total_duration because it can be 0 some times and I get a "Cannot divide by zero" error)
I feel like I'm super close but not sure what the next steps should be. Thanks for the help!
You can use $reduce to compute the total duration first. Then apply element wise $divide by using $map
db.collection.aggregate([
{
"$addFields": {
"totalDuration": {
"$reduce": {
"input": "$stats.histogram",
"initialValue": 0,
"in": {
$add: [
"$$value",
"$$this.duration"
]
}
}
}
}
},
{
"$addFields": {
"totalDuration": {
"$cond": {
"if": {
$eq: [
"$totalDuration",
0
]
},
"then": 1,
"else": "$totalDuration"
}
}
}
},
{
"$addFields": {
"stats.histogram": {
"$map": {
"input": "$stats.histogram",
"as": "h",
"in": {
"duration": "$$h.duration",
"dataPoints": "$$h.dataPoints",
"binMin": "$$h.binMin",
"binMax": "$$h.binMax",
"ratio": {
"$divide": [
"$$h.duration",
"$totalDuration"
]
}
}
}
}
}
}
])
Here is the Mongo playground for your reference.
I want to retrieve data from mongodb, grouping and summing for a custom field based in a db field which can exist or not but I don't have the result I expect because I there is no data ggrupation (see attached file)enter image description here. The mongo statement is:
aggregate({
$match: {
owner: 'W99999',
creation_date: {
$gte: 1530748800,
$lte: 1531292133
}
},
$project: {
isWarm: {
$cond: [{
$not: ["$referral"] }, 1, 0 ]
},
isCold: {
$cond: [{
$not: ["$referral"] }, 0, 1 ]
},
daysBefore: {
$subtract: [6, {
$trunc: {
$divide: [{
$subtract: ['$creation_date', 1530748800]
}, 86400]
}
}]
}
},
$group: {
_id: {
isWarm: { $sum: "$isWarm" },
isCold: { $sum: "$isCold" },
daysBefore: '$daysBefore'
}
})
I think the problem is the "isWarm" and "isCold" condition for creating them. Thank you in advance.
UPDATE 05/07/2018.
Schema (trunked for security reasons):
{
"_id": "1",
"creation_date":"1515780901",
"referral: //This field is optional.
{
some_data: { }
},
more_data: { }
}
Result expected:
{ [
{ isCold: 3, isWarm: 2, daysBefore: 0 },
{ isCold: 2, isWarm: 5, daysBefore: 1 },
{ isCold: 5, isWarm: 0, daysBefore: 2 },
{ isCold: 1, isWarm: 2, daysBefore: 3 },
{ isCold: 1, isWarm: 1, daysBefore: 4 },
{ isCold: 1, isWarm: 0, daysBefore: 5 },
{ isCold: 0, isWarm: 0, daysBefore: 6 }
] }
I would like to have the object even if there is no documents to count (e.g. last line of the result).
RESOLVED: I need to test with real data.
I think I have a solution:
[{ $project: {
_id: 0,
daysBefore: {
$subtract: [6, {
$trunc: {
$divide: [{
$subtract: ['$creation_date', 1530748800] }, 86400]
}
}]
},
isWarm: {$cond: [{ $gte: ['$referral', null]}, 1, 0]},
isCold: {$cond: [{ $gte: ['$referral', null]}, 0, 1]}} },
{
$group:
{
_id: { creation_date: '$daysBefore' },
isWarm: { $sum: '$isWarm' },
isCold: { $sum: '$isCold' }
}
}]
The data in the database is like this, there are decimals, I need to sum.
the data:
[ { _id: 5ad16497f52e0e1160fb70ae,
state: 0,
percentage: 32,
serviceCharge: 1 },
{ _id: 5ad16600f52e0e1160fb70b1,
state: 0,
percentage: 0,
serviceCharge: 10.115384615384642 },
{ _id: 5ad167f6c782521402300b4a,
state: 0,
percentage: 0,
serviceCharge: 16.11538461538464 },
{ _id: 5ad167f9c782521402300b4c,
state: 0,
percentage: 0,
serviceCharge: 23.769230769230717 },
{ _id: 5ad49154eb7bc9401e0c469b,
state: 0,
percentage: 6,
serviceCharge: 6 },
{ _id: 5ad49154eb7bc9401e0c469c,
state: 2,
percentage: 0,
serviceCharge: 6 } ]
the code :
bet.aggregate(
{
$match: {
state: { $in: [0, 1, 2, 3] }
}
},
{
$group: {
_id: { state: "$state" },
serviceCharge: { $sum: "$serviceCharge" },
percentage: { $sum: "$percentage" },
income:{ $sum: { $subtract: [ "$serviceCharge","$percentage"] } }
},
},
{ $project: { "_id": 0, "state": "$_id.state", "serviceCharge": 1, "percentage": 1 ,"income":1} },
(err, ret) => {
console.log(ret)
})
Run, and the result is :
[
{ serviceCharge: 6, percentage: 0, income: 6, state: 2 },
{ serviceCharge: 57, percentage: 38, income: 19, state: 0 }
]
Where is my Decimals, and Why is the result of the calculation an integer?
MongoDB documents:
[{
_id: '123213',
elevation: 2300,
area: 25
},
{
_id: '343221',
elevation: 1600,
area: 35,
},
{
_id: '545322',
elevation: 500
area: 12,
},
{
_id: '234234',
elevation: null,
area: 5
}]
I want to group these on a given interval on elevation and summarize the area property.
Group 1: < 0
Group 2: 0 - 1500
Group 3: 1501 - 3000,
Group 4: > 3000
So the expected output would be:
[{
interval: '1501-3000',
count: 2,
summarizedArea: 60
},
{
interval: '0-1500',
count: 1,
summarizedArea: 12,
},
{
interval: 'N/A',
count: 1,
summarizedArea: 5
}]
If possible, I want to use the aggregation pipeline.
Maybe something with $range? Or a combination of $gte and $lte?
As Feliix suggested $bucket should do the job, but boundaries should be slightly different to play well with negative and N/A values:
db.collection.aggregate([
{
$bucket: {
groupBy: "$elevation",
boundaries: [ -Number.MAX_VALUE, 0, 1501, 3001, Number.POSITIVE_INFINITY ],
default: Number.NEGATIVE_INFINITY,
output: {
"count": { $sum: 1 },
"summarizedArea" : { $sum: "$area" }
}
}
}
])
The formatting stage below can be added to the pipeline to adjust shape of the response:
{ $group: {
_id: null,
documents: { $push: {
interval: { $let: {
vars: {
idx: { $switch: {
branches: [
{ case: { $eq: [ "$_id", -Number.MAX_VALUE ] }, then: 3 },
{ case: { $eq: [ "$_id", 0 ] }, then: 2 },
{ case: { $eq: [ "$_id", 1501 ] }, then: 1 },
{ case: { $eq: [ "$_id", 3001 ] }, then: 0 }
],
default: 4
} }
},
in: { $arrayElemAt: [ [ ">3000", "1501-3000", "0-1500", "<0", "N/A" ], "$$idx" ] }
} },
count: "$count",
summarizedArea: "$summarizedArea"
} }
} }
$group with _id: null $push es all groups into array of a single document.
$let maps $_id from previous stage to text labels of interval defined in the array [ ">3000", "1501-3000", "0-1500", "<0", "N/A" ]. For that it calculates idx index of the label using $switch.
It must be way simpler to implement the logic on application level unless you absolutely need to do it in the pipeline.
you can use $bucket introduced in MongoDB 3.4 to achive this:
db.collection.aggregate([
{
$bucket: {
groupBy: "$elevation",
boundaries: [
0,
1500,
3000,
5000
],
default: 10000,
output: {
"count": {
$sum: 1
},
"summarizedArea": {
$sum: "$area"
}
}
}
}
])
output:
[
{
"_id": 0,
"count": 1,
"summarizedArea": 12
},
{
"_id": 1500,
"count": 2,
"summarizedArea": 60
},
{
"_id": 10000,
"count": 1,
"summarizedArea": 5
}
]
you can try it here: mongoplayground.net/p/xFe7ZygMqaY