Here is a sample document.
{"_id":{"$oid":"5e557779ed588826d84cef11"},
"meter_id":"1001",
"date":{"$date":{"$numberLong":"1509474600000"}},
"parameter_name":"hvac","voltage":{"unit":"V"},
"current":{"unit":"AMP"},
"powerFactor":{"unit":"phi"},
"angle":{"unit":"degree"},
"activePower":{"unit":"kwh"},
"reactivePower":{"unit":"kwh"},
"apparentPower":{"unit":"kwh"},
"frequency":{"unit":"hz"},
"thd":{"unit":"percentage"},
"energy":{"Energy":"5.7"},
"power":{"unit":"watt"},
And there are around 100 000 documents. I want to filter out the documents whose date is greater than the date i specify and calculate the total energy i.e energy.Energy
I used the below aggregation, but it doesn't seem to be working
const endDate = new Date(12-12-2018)
MeterData.aggregate([
{
$group: {
_id: "$meter_id",
total: { $sum: 1 },
totalEnergy: { $sum: { $toDouble: "$energy.Energy"
} },
dateSum: {
$sum: {
$toDouble: {
$not: [{
$cond: {
if: {
$gte: [
"$date", endDate
]
},
then: "$energy.Energy",
else: 0
}
}
]
}
}
}
}
}
])
Would be this:
db.collection.aggregate([
{ $match: { date: { $gte: ISODate("2016-12-12") } } },
{
$group: {
_id: "$meter_id",
total: { $sum: 1 },
totalEnergy: { $sum: "$energy.Energy" }
}
}
])
See Mongo playground
Related
i want to find profit of each account using mongodb and node.js i wrote this code but the main problem is i $match stage of aggregation pipeline i want to filter document according to accountNum and also rate but in my query filtering according to account number not work it return an empty array when i comment the account number and only filter by rate and grouping according to account number it work but i want to only find the profit of one person not all and filter data in $match state according to rate and accountNum both.
my code that return null value(filtering according to rate and accountNum in $match. i have some data with that account number):
const profit = await roznamcha.aggregate([
{
$match: {
rate: { $ne: 0 },
accountNum:{$eq:'$req.query.accountNum'}
}
},
{
$addFields: {
resultMultiply: {
$divide: [
{ $multiply: ["$credit", { $subtract: [100, "$rate"] }] }, 100
]
},
resultMultiplyde: {
$divide: [
{ $multiply: ["$debit", { $subtract: [100, "$rate"] }] }, 100
]
},
}
},
{
$group: {
_id: 0,
sumcredit: {
$sum: '$resultMultiply'
},
sumdebit: {
$sum: '$resultMultiplyde'
}
}
},
{
$addFields: {
finalProfit: { $subtract: ["$sumcredit", "$sumdebit"] }
}
}
])
res.status(201).json({
status: 'success',
data: {
profit
}
})
})
the code that work and return a value (filter according to rate only in $match stage)
const profit = await roznamcha.aggregate([
{
$match: {
rate: { $ne: 0 },
}
},
{
$addFields: {
resultMultiply: {
$divide: [
{ $multiply: ["$credit", { $subtract: [100, "$rate"] }] }, 100
]
},
resultMultiplyde: {
$divide: [
{ $multiply: ["$debit", { $subtract: [100, "$rate"] }] }, 100
]
},
}
},
{
$group: {
_id: '$accountNum',
sumcredit: {
$sum: '$resultMultiply'
},
sumdebit: {
$sum: '$resultMultiplyde'
}
}
},
{
$addFields: {
finalProfit: { $subtract: ["$sumcredit", "$sumdebit"] }
}
}
])
res.status(201).json({
status: 'success',
data: {
profit
}
})
})
is their a way i can sum only positive numbers and avoid those with negative in my MongoDB aggregate query i.e
aggregate([
{
$match: { },
},
{ $group: { _id: '$id', total: { $sum: '$amount' } } },
])
You can use $cond operator and simply $sum 0 when the number is negative:
{ $group: { _id: '$id', total: { $sum: { $cond: [ { $gt: [ '$amount', 0 ] }, '$amount', 0 ] } } } }
I have the below code snippet which will retrieve Date and count from a MongoDb collection from a specific date. Example: Retrieve date, count from 05-05-2020.
||Date||Count||
|05-06-2020|4|
|05-07-2020|25| and so on.
i want to add another logic to retrieve aggregate sum of 7 days instead of individual dates. Appreciate any help.
mongoClient.db().collection(COLLECTION.AUDIT).aggregate([
{
$match: { created_at: { $gt: date } }
},
{
$group: {
_id: {
$dateToString: { format: "%Y-%m-%d", date: "$created_at" }
},
count: { $sum: 1 }
}
},
{
$sort: { "_id": -1 }
}
])
The simplest way to do what I think you're asking would be to transform your group operator to $week instead of $dateToString. Since a week is 7 days, this will group all the documents from the same week, and return a count of the documents, along with the number of the week. To get both results from 1 query, combine them into a facet. So:
mongoClient.db().collection(COLLECTION.AUDIT).aggregate([
{
$match: { created_at: { $gt: date } }
},
{
$facet: {
by_week: {
$group: {
_id: { $week: $created_at},
count: { $sum: 1 }
},
{ $sort: { "_id": -1 }}
},
by_day: {
$group: {
_id: {
$dateToString: { format: "%Y-%m-%d", date: "$created_at" }
},
count: { $sum: 1 }
}
},
{ $sort: { "_id": -1 }}
}
},
])
I have a collection which contains half million data, and i want an average for all months between the date i enter. Right now i am getting the data for whole year, but i want it seperated by single month i.e 12 data range for every single month.
Below is the aggregation pipeline i am using.
let filter = 'y';
const date = new Date();
let checkDate = moment().subtract(1.5, 'years')._d;
MeterData.aggregate([
{
$group: {
_id: "$meter_id",
// total: { $sum: 1 },
totalEnergy: filter !== 'a' ? {
$sum: {
$toDouble: {
$cond: {
if: {
$gte: [
"$date", checkDate
]
},
then: "$energy.Energy",
else: 0
}
}
}
} : { $sum: { $toDouble:
"$energy.Energy"
} }
},
}
]);
Here i am getting totalEnergy for all year, in totalEnergy field, but now i want totalEnergy plus monthly calculations for the year i enter.
Any idea on how to do that. ?
Below is a sample document from the collection.
{"_id":{"$oid":"5e557779ed588826d84cef11"},
"meter_id":"1001",
"date":{"$date":{"$numberLong":"1509474600000"}},
"parameter_name":"hvac","voltage":{"unit":"V"},
"current":{"unit":"AMP"},
"powerFactor":{"unit":"phi"},
"angle":{"unit":"degree"},
"activePower":{"unit":"kwh"},
"reactivePower":{"unit":"kwh"},
"apparentPower":{"unit":"kwh"},
"frequency":{"unit":"hz"},
"thd":{"unit":"percentage"},
"energy":{"Energy":"5.7"},
"power":{"unit":"watt"},
As per suggested by Ryan Gunner, i got my answer which i am pasting below, i just have one more problem.
[
{
meter_id: '1001',
month: '2017-10',
totalEnergy: 0,
averageEnergy: 0
} + 11 more months......
]
Now what i need is the total of the energy for 12 months. For example total of totalEnergy field for all 12 months in a single variable.
how about something like this?
var startDate = new ISODate('2020-04-01');
var endDate = new ISODate('2019-04-01');
db.collection.aggregate(
{
$match: {
$expr: {
$and: [
{ $gt: ['$date', endDate] },
{ $lt: ['$date', startDate] }]
}
}
},
{
$group: {
_id: {
meter: '$meter_id',
month: { $dateToString: { format: '%Y-%m', date: '$date' } }
},
totalEnergy: { $sum: { $toDouble: '$energy.Energy' } },
averageEnergy: { $avg: { $toDouble: '$energy.Energy' } }
}
},
{
$project: {
meter_id: '$_id.meter',
month: '$_id.month',
totalEnergy: '$totalEnergy',
averageEnergy: '$averageEnergy',
_id: 0
}
},
{
$sort: { meter_id: 1 }
}
{
$group: {
_id: null,
grandTotalEnergy: { $sum: '$totalEnergy' },
monthlyData: { $push: '$$ROOT' }
}
},
{ $project: { _id: 0 } }
)
update: added grandTotalEnergy field and pushed monthlyData to an array.
I am trying to wrap my head around the query which I am trying to make with mongoose on Node JS. Here is my dataset:
{"_id":{"$oid":"5e49c389e3c23a1da881c1c9"},"name":"New York","good_incidents":{"$numberInt":"50"},"salary":{"$numberInt":"50000"},"bad_incidents":"30"}
{"_id":{"$oid":"5e49c3bbe3c23a1da881c1ca"},"name":"Cairo","bad_incidents":{"$numberInt":"59"},"salary":{"$numberInt":"15000"}}
{"_id":{"$oid":"5e49c42de3c23a1da881c1cb"},"name":"Berlin","incidents":{"$numberInt":"30"},"bad_incidents":"15","salary":{"$numberInt":"55000"}}
{"_id":{"$oid":"5e49c58ee3c23a1da881c1cc"},"name":"New York","good_incidents":{"$numberInt":"15"},"salary":{"$numberInt":"56500"}}
What I am trying to do is get these values:
The most repeated city in collection
The average of bad_incidents
The maximum value of good_incidents
Maximum salary where there are no bad_incidents
I am trying to wrap my head around how I can do this in one query, because I only need one value per field. I would be glad if somebody would lead me on the right track. No need for full solution
Regards!
You may perform MongoDB aggregation with $facet operator which allows compute several aggregation at once.
db.collection.aggregate([
{
$facet: {
repeated_city: [
{
$group: {
_id: "$name",
name: {
$first: "$name"
},
count: {
$sum: 1
}
}
},
{
$match: {
count: {
$gt: 1
}
}
},
{
$sort: {
count: -1
}
},
{
$limit: 1
}
],
bad_incidents: [
{
$group: {
_id: null,
avg_bad_incidents: {
$avg: {
$toInt: "$bad_incidents"
}
}
}
}
],
good_incidents: [
{
$group: {
_id: null,
max_good_incidents: {
$max: {
$toInt: "$good_incidents"
}
}
}
}
],
max_salary: [
{
$match: {
bad_incidents: {
$exists: false
}
}
},
{
$group: {
_id: null,
max_salary: {
$max: {
$toInt: "$salary"
}
}
}
}
]
}
},
{
$replaceWith: {
$mergeObjects: [
{
$arrayElemAt: [
"$repeated_city",
0
]
},
{
$arrayElemAt: [
"$bad_incidents",
0
]
},
{
$arrayElemAt: [
"$good_incidents",
0
]
},
{
$arrayElemAt: [
"$max_salary",
0
]
}
]
}
}
])
MongoPlayground
[
{
"_id": null,
"avg_bad_incidents": 34.666666666666664,
"count": 2,
"max_good_incidents": 50,
"max_salary": 56500,
"name": "New York"
}
]