MongoDB Nested GroupBy - mongodb

I have a sample data like below:
(Every floor has multiple sensor data)
{"Floor_Id": "Galileo_001",
"name": "Forklifts",
"Sensor_data": [{
"Floor_Id": "Galileo_001",
"Floor_name": "Forklifts",
"Name": "forkLift_002",
"Asset_Id": 123,
"Load": 1.7096133,
"Timestamp": 1537878750996
},
{
"Floor_Id": "Galileo_001",
"Floor_name": "Forklifts",
"Name": "forkLift_003",
"Asset_Id": 456,
"Load": 1.7096133,
"Timestamp": 1537878750996,
},
{
"Floor_Id": "Galileo_001",
"Floor_name": "Forklifts",
"Name": "forkLift_005 ",
"Asset_Id": 127,
"Load": 1.7096133,
"Timestamp": 1537878750996
},
{
"Floor_Id": "Galileo_001",
"Floor_name": "Forklifts",
"Name": "forkLift_001",
"Asset_Id": 157,
"Load": 1.7096133,
"Timestamp": 1537878750996,
}
]}
For the response, I need total load calculated for every floor and individual load for every day. The desired response is like below:
{
"TotalLoad": 3214,
"Floor_Id": "Galileo_001",
"LoadUnit": "Kgs",
"AssetStatus": [{
"TotalLoad": 200,
"LoadUnit": "Kgs",
"Date": "1539588994"
}, {
"TotalLoad": 400,
"LoadUnit": "Kgs",
"Date": "1539475200"
}, {
"TotalLoad": 100,
"LoadUnit": "Kgs",
"Date": "1539388800"
}]
}
I am writing the below Mongo aggregation:
db.sensordata.aggregate([{"$unwind" : "$Sensor_data" },
{"$group": {
"_id": {"Floor_Id": "$Floor_Id",
"DailyDate":{"$dateFromParts":{
"year":{"$year":{"$add": [new Date("1970-01-01"), "$Sensor_data.Timestamp"]}},
"month":{"$month":{"$add": [new Date("1970-01-01"), "$Sensor_data.Timestamp"]}},
"day":{"$dayOfMonth":{"$add": [new Date("1970-01-01"), "$Sensor_data.Timestamp"]}}
}
}
},
"AssetLoad": {"$sum": "$Sensor_data.Load" }
}
},
{
"$group" : {
"_id": {"Floor_Id": "$_id.Floor_Id"},
"TotalLoad": { "$sum": "$Sensor_data.Load" },
"AssetStatus":{
"$push":{
"TotalLoad": "$AssetLoad",
"LoadUnit": "Kgs",
"Date": "$_id.DailyDate"
}
}
}
}
])
Problem:
For the total load calculated for every floor, I am getting 0.
{
"_id" : {
"Floor_Id" : "Galileo_001"
},
"TotalLoad" : 0,
"AssetStatus" : [
{
"TotalLoad" : 8.5480665,
"LoadUnit" : "Kgs",
"Date" : ISODate("2018-09-25T00:00:00.000Z")
}
]
}
What am I doing wrong here?
How can I get the desired output?

You're getting zero because the second $group pipeline does not recognise the field Sensor_data.Load so it defaults to 0.
Replace the expression
"TotalLoad": { "$sum": "$Sensor_data.Load" },
with the pipeline-changed field AssetLoad
"TotalLoad": { "$sum": "$AssetLoad" },
To calculate the number of sensors each floor has in total, your first pipeline needs to calculate the size of the
sensor data using $size, store that in a new field using $addFields then retain the field in the preceding pipeline stages pipelines by using the $first operators.audit
Amend your pipeline to the following:
db.sensordata.aggregate([
{ "$addFields": {
"TotalSensors": { "$size": "Sensor_data" }
} },
{"$unwind" : "$Sensor_data" },
{ "$group": {
"_id": {
"Floor_Id": "$Floor_Id",
"DailyDate": {
"$dateFromParts": {
"year": { "$year": {
"$add": [new Date("1970-01-01"), "$Sensor_data.Timestamp"]
} },
"month": { "$month": {
"$add": [new Date("1970-01-01"), "$Sensor_data.Timestamp"]
} },
"day": { "$dayOfMonth": {
"$add": [new Date("1970-01-01"), "$Sensor_data.Timestamp"]
} }
}
}
},
"AssetLoad": {"$sum": "$Sensor_data.Load" },
"TotalSensors": { "$first": "$TotalSensors" }
} },
{ "$group" : {
"_id": {"Floor_Id": "$_id.Floor_Id"},
"TotalLoad": { "$sum": "$Sensor_data.Load" },
"AssetStatus":{
"$push": {
"TotalLoad": "$AssetLoad",
"LoadUnit": "Kgs",
"Date": "$_id.DailyDate"
}
},
"TotalSensors": { "$first": "$TotalSensors" }
} }
])

Related

How to do double group and push with two fields in MongoDB?

I have data like this:
[
{
"channel": "abc",
"date": "2019-01-01",
"revenue": 100,
"quantity": 100,
},
{
"channel": "xyz",
"date": "2019-02-10",
"revenue": 100,
"quantity": 100,
},
{
"channel": "def",
"date": "2020-01-01",
"revenue": 100,
"quantity": 100,
},
{
"channel": "abc",
"date": "2021-06-01",
"revenue": 100,
"quantity": 100,
},
{
"channel": "abc",
"date": "2021-06-12",
"revenue": 100,
"quantity": 100,
}
]
I want to group by channel and push data and again group by date (in month and year only) and push data and add a field after these pushes. The dates are all Date objects, not Strings. The avg_revenue is tot_revenue divided by tot_quantity.
[
{
"channel": "abc",
"dates": [
{
"date": "2019-01",
"totals": {
"tot_revenue": 100,
"tot_quantity": 100,
"avg_revenue": 1,
}
},
{
"date": "2019-01",
"totals": {
"tot_revenue": 200,
"tot_quantity": 200,
"avg_revenue": 1,
}
}
]
},
{
"channel": "def",
"dates": [
{
"date": "2020-01",
"totals": {
"tot_revenue": 100,
"tot_quantity": 100,
"avg_revenue": 1,
}
}
]
},
{
"channel": "xyz",
"dates": [
{
"date": "2019-02",
"totals": {
"tot_revenue": 100,
"tot_quantity": 100,
"avg_revenue": 1,
}
}
]
},
]
My attempt:
db.collection.aggregate([
{
"$set": {
"date": {
"$dateFromString": {
"dateString": "$date",
"format": "%Y-%m-%d"
}
}
}
},
{
$group: {
_id: {
channel: "$channel",
month: {
$month: "$date"
},
year: {
$year: "$date"
}
},
report_dates: {
$push: {
report_date: "$date",
revenue: "$revenue",
quantity: "$quantity",
}
},
}
},
{
$group: {
_id: {
month: "$month",
year: "$year",
},
values: {
$push: {
revenue: "$revenue",
quantity: "$quantity",
}
},
}
}
])
You need to create an aggregation pipeline that consists of two $group steps, the first to group all the documents by the channel and date fields whilst accumulating the tot_revenue and tot_quantity aggregates. The other $group stage will compute the dates list with the totals.
The following pipeline should give the desired output:
db.collection.aggregate([
{ '$group': {
'_id': {
'channel': '$channel',
'date': {
'$dateToString': {
'format': "%Y-%m", 'date': {
"$dateFromString": {
"dateString": "$date",
"format": "%Y-%m-%d"
}
}
}
}
},
'tot_revenue': { '$sum': '$revenue' },
'tot_quantity': { '$sum': '$quantity' },
} },
{ '$group': {
'_id': '$_id.channel',
'dates': {
'$push': {
'date': '$_id.date',
'totals': {
'tot_revenue': '$tot_revenue',
'tot_quantity': '$tot_quantity',
'avg_revenue': { '$divide': ['$tot_revenue','$tot_quantity'] }
}
}
}
} }
])

How to use aggregate and group by two fields

I'm trying to use aggregate group and match to get my data.
This is my code:
itemShell.aggregate(
[
{
$match: { shell_id_in_whareHouse: {$in:shelfIds}}
},
{
$group: {
_id: "$item",
position:{'$last':'$position'},
total: { $sum: "$amount" }
}
}
],
function(err, results) {
if (err) console.log(err)
else {
res.json(results);
}
}
);
This is how itemShell objects looks like:
{item:1313,position:'2A',amount:500},
{item:1313,position:'2A',amount:200},
{item:1414,position:'1A',amount:500},
{item:1414,position:'2A',amount:800},
{item:1313,position:'1A',amount:300}
My problem is that the outcome of results is: (because of $Last accumulator)
[
{_id:1313,position:'1A',total:1000},
{_id:1414,position:'2A',total:1300},
]
My desired outcome should be:
[
{_id:1313,position:'2A',total:700},
{_id:1414,position:'1A',total:500},
{_id:1414,position:'2A',total:800},
{_id:1313,position:'1A',total:300}
]
So it will group only the objects that item number and position string are the same.
any suggestions ?
You're on the right path, you need to group by two fields using $group stage with _id as an object like below:
ItemShell.aggregate([
{
$match: {}
},
{
$group: {
_id: { item: '$item', position: '$position' },
total: { $sum: '$amount' }
}
},
{
$project: {
_id: 0,
item: '$_id.item',
position: '$_id.position',
total: 1
}
}
]);
Output
[
{
"item": 1313,
"position": "1A",
"total": 300
},
{
"item": 1313,
"position": "2A",
"total": 700
},
{
"item": 1414,
"position": "2A",
"total": 800
},
{
"item": 1414,
"position": "1A",
"total": 500
}
]
Here's a mongo playground: https://mongoplayground.net/p/Ne5h9WkzjEm
I think this should work
itemShell.aggregate([
{
"$group": {
"_id": {
"item": "$item",
"positon": "$positon"
},
"items": {
"$first": "$$ROOT"
},
"amount": {
"$sum": "$amount"
}
}
},
{
"$project": {
"item": "$items.item",
"positon": "$items.position",
"amount": 1,
"_id": "$items._id"
}
}
],
function(err, results) {
if (err) console.log(err)
else {
res.json(results);
}
}
);
You can see the results here : https://mongoplayground.net/p/h7aa_so1Cok
Output:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"amount": 1000,
"item": 1313,
"positon": "2A"
},
{
"_id": ObjectId("5a934e000102030405000002"),
"amount": 1300,
"item": 1414,
"positon": "1A"
}
]

Mongodb multi level aggregation

Data in mongo
[{
"_id": "5d71d1432f7c8151c58c4481",
"payment": {
"transactions": [
{
"_id": "5d71d1ff2f7c8151c58c44cf",
"method": "paytm",
"amount": 100,
"paymentOn": "2019-09-06T03:26:44.959Z"
},
{
"_id": "5d71d1ff2f7c8151c58c44ce",
"method": "cash",
"amount": 650,
"paymentOn": "2019-09-06T03:26:55.531Z"
}
],
"status": "partial"
},
"customer": "5d66c434c24f2b1fb6772014",
"order": {
"orderNumber": "WP-ORD-06092019-001",
"total": 770,
"balance": 20
}
},
{
"_id": "5d71d1432f7c8151c58c4481",
"payment": {
"transactions": [
{
"_id": "5d71d1ff2f7c8151c58c44cf",
"method": "paytm",
"amount": 100,
"paymentOn": "2019-09-06T03:26:44.959Z"
}
],
"status": "partial"
},
"customer": "5d66c434c24f2b1fb6772014",
"order": {
"orderNumber": "WP-ORD-06092019-001",
"total": 200,
"balance": 100
}
}]
I want to aggregate payments by method.
So the result would look like below:
Output:
Paytm: 200
Cash : 650
Unpaid(Balance): 120
I have tried:
[
{
'$unwind': {
'path': '$payment.transactions',
'preserveNullAndEmptyArrays': true
}
}, {
'$project': {
'amount': '$payment.transactions.amount',
'method': '$payment.transactions.method'
}
}, {
'$group': {
'_id': '$method',
'amount': {
'$sum': '$amount'
}
}
}
]
But how to include balance calculation as well
Using the above dataset, use the aggregate pipeline for calculation using aggregate as:
db.collection.aggregate([
{
$facet: {
paidAmounts: [
{ '$unwind': { 'path': '$payment.transactions', 'preserveNullAndEmptyArrays': true } },
{
$group: {
_id: "$payment.transactions.method",
amount: {
$sum: "$payment.transactions.amount"
}
}
}
],
leftAmounts: [
{
$group: {
_id: null,
balance: {
$sum: "$order.balance"
}
}
}
]
}
}
])
giving output:
here leftAmounts has left balance and paidAmounts having grouped paid data on basis of payment type
[
{
"leftAmounts": [
{
"_id": null,
"balance": 120
}
],
"paidAmounts": [
{
"_id": "cash",
"amount": 650
},
{
"_id": "paytm",
"amount": 200
}
]
}
]
Working solution : https://mongoplayground.net/p/7IWELKKMsWe
db.collection.aggregate([
{
"$unwind": "$payment.transactions"
},
{
"$group": {
"_id": "$_id",
"balance": {
"$first": "$order.balance"
},
"paytm": {
"$sum": {
"$cond": [
{
"$eq": [
"$payment.transactions.method",
"paytm"
]
},
"$payment.transactions.amount",
0
]
}
},
"cash": {
"$sum": {
"$cond": [
{
"$eq": [
"$payment.transactions.method",
"cash"
]
},
"$payment.transactions.amount",
0
]
}
}
}
},
{
"$group": {
"_id": null,
"balance": {
"$sum": "$balance"
},
"cash": {
"$sum": "$cash"
},
"paytm": {
"$sum": "$paytm"
}
}
}
])

Get Highest Value By Hour in MongoDB for given Date Range?

I have the following document structure in MongoDB :
{
"_id" : ObjectId("5c1b7451b1829b69963029ea"),
"duration" : 92,
"accountId" : ObjectId("9aafe7b01cf4560c9bb5d68"),
"createdAt" : ISODate("2018-12-20T10:52:01.560Z"),
"__v" : 0,
},
{
"_id" : ObjectId("5c1b7451b1829b69963029ea"),
"duration" : 192,
"accountId" : ObjectId("9aafe7b01cf4560c9bb5d68"),
"createdAt" : ISODate("2018-12-20T11:52:01.560Z"),
"__v" : 0,
}
Now I want to get the highest sum of duration for the day with corresponding hour. Technically, something like this :
{
"readableDate" : "2018-12-20",
"hour" : 11,
"total" : 192
}
Where total is the hourly total which is HIGHEST for that particular day.
The query which I have tried is as follows :
db.getCollection('operational_details').aggregate(
{"$match": {"accountId": ObjectId("9aafe7b01cf4560c9bb5d68"),
"createdAt": {"$gte": ISODate("2019-06-01T10:30:29.725Z"),
"$lte": ISODate("2019-06-04T10:30:29.725Z")},
}},
{ "$project": {
"date": {"$dateToString": {"format": "%Y-%m-%d", "date": "$createdAt"}},
"hour": {"$hour":"$createdAt"},
"total":{"$sum": "$duration"} }
},
{ "$group":{
"_id": { "hour":"$hour","date":"$date"},
"max": {"$max": "$total"}
}})
Hope I am clear with my example. TIA
Please try the below
working playground link
//Array
[
{
"_id": ObjectId("5cf4f20243f560e1e0a77014"),
"duration": 92,
"accountId": ObjectId("6ef4f20243f560e1e0a77015"),
"createdAt": ISODate("2018-12-20T10:52:10.320Z")
},
{
"_id": ObjectId("5cf4f21843f560e1e0a7701a"),
"duration": 192,
"accountId": ObjectId("6ef4f20243f560e1e0a77015"),
"createdAt": ISODate("2018-12-20T11:52:11.123Z")
}
]
//Script
db.collection.aggregate([
{
"$match": {
"accountId": {
$eq: ObjectId("6ef4f20243f560e1e0a77015")
},
"createdAt": {
$gte: ISODate("2018-12-19T10:30:00.000Z"),
$lte: ISODate("2018-12-21T10:30:00.000Z")
}
}
},
{
"$project": {
"date": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$createdAt"
}
},
"hour": {
"$hour": "$createdAt"
},
"duration": "$duration"
}
},
{
"$group": {
"_id": {
"hour": "$hour",
"date": "$date"
},
"total": {
"$sum": "$duration"
}
}
},
{
"$project": {
"readableDate": "$_id.date",
"hour": "$_id.hour",
"total": "$total"
}
},
{
"$project": {
"_id": 0
}
},
{
"$sort": {
"total": -1
}
},
{
"$limit": 1
}
])
//Result
[
{
"hour": 11,
"readableDate": "2018-12-20",
"total": 192
}
]

How do I group by day/month in mongoDB?

Here's how one document looks like:
{
"login_Id": "c",
"name": "Abhishek Soni",
"location": "BLAHBLAH",
"work": [
{
"date":ISODate("2014-01-01"),
"total_time": 100,
},
{
"date":ISODate("2014-09-02"),
"total_time": 100,
},
{
"date":ISODate("2014-01-01"),
"total_time": 10,
},
]
}
What I want to do is to run a query that'll give an output like this:
{login_Id: 'c', work:{'01' : 110, '02': 100, ... and so on}}
Basically, I just want to group the work part month wise.
This is what I have tried:
db.employees.aggregate([
{
"$project": {
"_id": 0,
"login_Id": 1,
"time": {
"$sum": "$work.total_time"
}
}
},
{
"$group": {
"_id": {
"$dayOfYear": "$work.date"
},
"time": {
"$sum": "$work.total_time"
}
}
}
]);
But it outputs null. If I remove the group clause, I get the total sum (i.e., 210) What's wrong?
You can try below aggregation
db.collection.aggregate([
{ "$unwind": "$work" },
{ "$match": { "work.date": { "$type": "date" }}},
{ "$group": {
"_id": { "date": { "$dayOfMonth": "$work.date" }},
"time": { "$sum": "$work.total_time" },
"login_Id": { "$first": "$login_Id" }
}},
{ "$group": {
"_id": "$login_Id",
"data": {
"$push": {
"k": { "$toString": "$_id.date" },
"v": "$time"
}
}
}},
{ "$project": {
"work": { "$arrayToObject": "$data" },
"_id": 0,
"login_id": "$_id"
}}
])
Output
[
{
"login_id": "c",
"work": {
"1": 110,
"2": 100
}
}
]