Get data for month wise in mongodb - mongodb

I want to get data to each month. in my table data is stored like this:-
"patient" : [
{
"status" : 'arrived',
start_time: '2017-08-17T09:17:00.000Z
},
{
"status" : 'arraived',
start_time: '2017-08-16T07:17:00.000Z
},
{
"status" : 'arrived',
start_time: '2017-07-12T09:17:00.000Z
},
{
"status" : 'arraived',
start_time: '2017-07-05T08:10:00.000Z
},
{
"status" : 'arrived',
start_time: '2017-06-02T09:17:00.000Z
},
{
"status" : 'arraived',
start_time: '2017-05-05T08:16:00.000Z
}
]
etc,
and I want to sum of patient of each month (jan to des), like this :-
{
"month" : 8,
"count" : 2
}and like this month 1 to 12

I assume, patient array is associated with a customer and the date is stored in mongo ISO format.
So, the actual document would look like :
{
name: "stackOverflow",
"patient" : [
{
"status" : 'arrived',
"start_time": ISODate("2017-08-17T09:17:00.000Z")
},
{
"status" : 'arraived',
"start_time": ISODate("2017-08-16T07:17:00.000Z")
},
{
"status" : 'arrived',
"start_time": ISODate("2017-07-12T09:17:00.000Z")
},
{
"status" : 'arraived',
"start_time": ISODate("2017-07-05T08:10:00.000Z")
},
{
"status" : 'arrived',
"start_time": ISODate("2017-06-02T09:17:00.000Z")
},
{
"status" : 'arraived',
"start_time": ISODate("2017-05-05T08:16:00.000Z")
}
]
}
here is a sample query which you can try -
db.test.aggregate([
{$unwind: "$patient"},
{ $group: {
_id: {name: "$name", month: {$month: "$patient.start_time"}},
count: { $sum: 1}
}},
{$group: {
_id: "$_id.name",
patient: {$push: {month: "$_id.month", count: "$count"}}
}}
])
Sample output:
{
"_id" : "stackOverflow",
"patient" : [
{
"month" : 5,
"count" : 1
},
{
"month" : 6,
"count" : 1
},
{
"month" : 7,
"count" : 2
},
{
"month" : 8,
"count" : 2
}
]
}
You can change query according to your use-case. hope this will help you!

This is my code:-
db.appointments.aggregate( [
{
$project:
{
"patient_id": 1,
"start_time": 1,
"status": 1
}
},
{
$match: {
'start_time' : { $gte: startdate.toISOString() },
'status': { $eq: 'arrived' }
} ,
},
{ $group: {
_id: {id: "$_id", start_time: {$month: "$appointments.start_time"}},
count: { $sum: 1}
}}
])
When I used this :-
{ $group: {
_id: {id: "$_id", start_time: {$month: "$start_time"}},
count: { $sum: 1}
}
}
its showing error message:-
{"name":"MongoError","message":"can't convert from BSON type missing to Date","ok":0,"errmsg":"can't convert from BSON type missing to Date","code":16006,"codeName":"Location16006"}
And when I comment this its showing this :-
Out Put here:-
:[{"count":{"_id":"595b6f95ab43ec1f6c92b898","patient_id":"595649904dbe9525c0e036ef","start_time":"2017-07-04T10:35:00.000Z","status":"arrived"}},
{"count":{"_id":"595dff870960d425d4f14633","patient_id":"5956478b4dbe9525c0e036ea","start_time":"2017-03-08T09:14:00.000Z","status":"arrived"}},{"count":{"_id":"595dffaa0960d425d4f14634","patient_id":"595649904dbe9525c0e036ef","start_time":"2017-03-17T09:15:00.000Z","status":"arrived"}},{"count":{"_id":"595dffcf0960d425d4f14635","patient_id":"595648394dbe9525c0e036ec","start_time":"2017-06-08T09:15:00.000Z","status":"arrived"}},{"count":{"_id":"595dfffb0960d425d4f14636","patient_id":"5956478b4dbe9525c0e036ea","start_time":"2017-06-20T09:16:00.000Z","status":"arrived"}},{"count":{"_id":"595e00160960d425d4f14637","patient_id":"5959ea7f80388b19e0b57817","start_time":"2017-08-17T09:17:00.000Z","status":"arrived"}}]}

const group = {
$group: {
_id: { month: { $month: "$createdAt" } },
count: { $sum: 1 },
},
};
const groups = {
$group: {
_id: null,
patient: { $push: { month: '$_id.month', count: '$count' } },
},
};
return db.Patient.aggregate([group, groups]);

Related

Grouping and summing after using $addToSet in MongoDB

Assume I have the following data:
[{
"type" : "DIVIDEND_OR_INTEREST",
"netAmount" : 2.43,
"transactionDate" : "2019-01-01T17:02:36+0000",
"transactionId" : 1,
"transactionItem" : {
"instrument" : {
"symbol" : "SPHD"
}
}
},
{
"type" : "DIVIDEND_OR_INTEREST",
"netAmount" : 5.00,
"transactionDate" : "2019-01-01T17:02:36+0000",
"transactionId" : 2,
"transactionItem" : {
"instrument" : {
"symbol" : "ATT"
}
}
},
{
"type" : "DIVIDEND_OR_INTEREST",
"netAmount" : 2.43,
"transactionDate" : "2019-02-01T17:02:36+0000",
"transactionId" : 3,
"transactionItem" : {
"instrument" : {
"symbol" : "SPHD"
}
}
},
{
"type" : "DIVIDEND_OR_INTEREST",
"netAmount" : 5.00,
"transactionDate" : "2019-02-01T17:02:36+0000",
"transactionId" : 4,
"transactionItem" : {
"instrument" : {
"symbol" : "ATT"
}
}
}]
I want to group the data by year and get a total sum for that year. I also want an array of the items used during the group, grouped by a field and summed, if that makes sense. This is ultimately what I want to end up with:
{
"year": [
{
"year": "2019",
"totalYear": 14.86,
"dividends": [
{
"symbol": "T",
"amount": 10.00
},
{
"symbol": "SPHD",
"amount": 4.86
}
]
}
]
}
Below is the code I have written so far using Mongoose. The problem is that I can't figure out how to group and sum the items that I added to the set. I could always do that in the application layer but I was hoping to accomplish this entirely inside of a query.:
const [transactions] = await Transaction.aggregate([
{ $match: { type: TransactionType.DIVIDEND_OR_INTEREST, netAmount: { $gte: 0 } } },
{
$facet: {
year: [
{
$group: {
_id: { $dateToString: { format: '%Y', date: '$transactionDate' } },
totalYear: { $sum: '$netAmount' },
dividends: {
$addToSet: {
symbol: '$transactionItem.instrument.symbol',
amount: '$netAmount',
},
},
},
},
{ $sort: { _id: 1 } },
{
$project: {
year: '$_id',
totalYear: { $round: ['$totalYear', 2] },
dividends: '$dividends',
_id: false,
},
},
],
},
},
]).exec();
It requires to do two group stages,
First group by year and symbol
Second group by only year
If the transactionDate field has date type value then just use $year operator to get the year
I would suggest you do $sort after the immediate $match stage to use an index if you have created or planning for future
const [transactions] = await Transaction.aggregate([
{
$match: {
type: TransactionType.DIVIDEND_OR_INTEREST,
netAmount: { $gte: 0 }
}
},
{ $sort: { transactionDate: 1 } },
{
$facet: {
year: [
{
$group: {
_id: {
year: { $year: "$transactionDate" },
symbol: "$transactionItem.instrument.symbol"
},
netAmount: { $sum: "$netAmount" }
}
},
{
$group: {
_id: "$_id.year",
totalYear: { $sum: "$netAmount" },
dividends: {
$push: {
symbol: "$_id.symbol",
amount: "$netAmount"
}
}
}
},
{
$project: {
_id: 0,
year: "$_id",
totalYear: 1,
dividends: 1
}
}
]
}
}
]).exec();
Playground

Using Mongo to calculate sum in Aggregator pipeline

I have a timeseries data in mongodb and I want to calculate the sum per day between two given dates of every sensor after I have calculated the difference between the max and min reading of the day by the sensor, using the below query
db.ts_events.aggregate([
{ $match: {
"metadata.assetCode": { $in: [
"h"
]
},
"timestamp": { $gte: ISODate("2022-07-01T02:39:02.000+0000"), $lte: ISODate("2022-07-01T06:30:00.000+0000")
}
}
},
{
$project: {
date: {
$dateToParts: { date: "$timestamp"
}
},
activeEnergy: 1,
"metadata.meterId": 1,
}
},
{
$group: {
_id: {
date: {
year: "$date.year",
month: "$date.month",
day: "$date.day"
},
meter: "$metadata.meterId",
},
maxValue: { $max: "$activeEnergy"
},
minValue: { $min: "$activeEnergy"
},
}
},
{
$addFields: {
differnce: { $subtract: [
"$maxValue",
"$minValue"
]
},
}
},
])
I get the following output
{
"_id" : {
"date" : {
"year" : NumberInt(2022),
"month" : NumberInt(7),
"day" : NumberInt(1)
},
"meter" : "B"
},
"maxValue" : 1979.78,
"minValue" : 1979.77,
"differnce" : 0.009999999999990905
}
{
"_id" : {
"date" : {
"year" : NumberInt(2022),
"month" : NumberInt(7),
"day" : NumberInt(1)
},
"meter" : "A"
},
"maxValue" : 7108.01,
"minValue" : 7098.18,
"differnce" : 9.829999999999927
}
I want to calculate the sum of both meter difference how can I do that?
Apart from this one more problem I am facing which I am putting forward in this edited version, as you can see date is in ISODate format but I will be getting a unix epoch format,
I tried to tweak the query but it is not working
db.ts_events.aggregate([
{
$project: {
date: {
$dateToParts: {
date: "$timestamp"
}
},
activeEnergy: 1,
"metadata.meterId": 1,
"metadata.assetCode": 1,
"timestamp": 1,
startDate: {
$toDate: 1656686342000
},
endDate: {
$toDate: 1656700200000
}
}
},
{
$match: {
"metadata.assetCode": {
$in: [
"h"
]
},
"timestamp": {
$gte: "$startDate", $lte: "$endDate"
}
}
},
{
$group: {
_id: {
date: {
year: "$date.year",
month: "$date.month",
day: "$date.day"
},
meter: "$metadata.meterId",
},
maxValue: {
$max: "$activeEnergy"
},
minValue: {
$min: "$activeEnergy"
},
}
},
{
$addFields: {
differnce: {
$subtract: [
"$maxValue",
"$minValue"
]
},
}
},
{
$group: {
_id: "$_id.date", res: {
$push: '$$ROOT'
}, differnceSum: {
$sum: '$differnce'
}
}
}
])
Can you help me solve the problem?
One option is to add one more step like this (depending on your expected output format):
This step will group together your separate documents, into one document, which will allow you to sum their values together. Be careful when grouping, since now it is a one big document and a document has a size limit.
We use $$ROOT to keep the original document structure (here inside a new array)
{$group: {_id: 0, res: {$push: '$$ROOT'}, differnceSum: {$sum: $differnce'}}}

In MongoDB Shell, How to query for day-by-day data for a time period (such as a month)

I want to get day wise data grouped by class (field from my collection) for a month of period.
This is what i have tried to query, but doesn't give the expected output.
query:
db.collection.aggregate([
// Get only records created in the last 30 days
{$match:{
"created_at":{$gt: new Date(ISODate().getTime() - 1000*60*60*24*30)},
}},
// Get the year, month and day from the created_at TimeStamp
{$project:{
"year":{$year:"$created_at"},
"month":{$month:"$created_at"},
"day": {$dayOfMonth:"$created_at"}
}},
// Group by year, month and day and get the count
{$group:{
_id:{year:"$year", month:"$month", day:"$day"},
"count":{$sum:1}
}},
// Group by class field
{ $group: {
_id: "$meta.class",
total: {$sum: 1}
}}
])
expected output:
{ "_id" : { "year" : 2021, "month" : 10, "day" : 01 }, "count" : {{ "_id" : "class1", "total" : 15 }, { "_id" : "class2", "total" : 25 }} }
{ "_id" : { "year" : 2021, "month" : 10, "day" : 02 }, "count" : {{ "_id" : "class2", "total" : 25 }, { "_id" : "class3", "total" : 10 }} }
...
{ "_id" : { "year" : 2021, "month" : 10, "day" : 30 }, "count" : {{ "_id" : "class3", "total" : 50 }} }
So, could someone please tell me how to attain the desired result or what I'm doing wrong?
Thanks.
---EDIT---
Sample Data:
mongoplayground link : here
[
{
"key": 1,
"_id": ObjectId("615602280000000000000000"),
"created_at": ISODate("2021-09-30T18:30:00.000Z"),
"meta": {
"class": "class1",
}
},
{
"key": 2,
"_id": ObjectId("615753a80000000000000000"),
"created_at": ISODate("2021-10-01T18:30:00.000Z"),
"meta": {
"class": "class1",
}
},
{
"key": 3,
"_id": ObjectId("615764100000000000000000"),
"created_at": ISODate("2021-10-01T19:40:00.000Z"),
"meta": {
"class": "class1",
}
},
{
"key": 4,
"_id": ObjectId("615776d00000000000000000"),
"created_at": ISODate("2021-10-01T21:00:00.000Z"),
"meta": {
"class": "class2",
}
}
]
sample o/p:
[
{
"_id":{
"year":2021,
"month":10,
"day":1
},
"count":[
{
"_id":"class1",
"total":1
}
]
},
{
"_id":{
"year":2021,
"month":10,
"day":2
},
"count":[
{
"_id":"class1",
"total":2
},
{
"_id":"class2",
"total":1
}
]
}
]
Try this one:
db.collection.aggregate([
{ $match: { created_at: { $gt: moment().startOf('day').subtract(30, 'day').toDate() } } },
{
$group: {
_id: {
day: { $dateTrunc: { date: "$created_at", unit: "day" } },
class: "$meta.class"
},
total: { $count: {} }
}
},
{
$group: {
_id: "$_id.day",
count: { $push: { total: "$total", class: "$_id.class" } }
}
}
])
Whenever I have to work with date/time values then I suggest to use the moment.js library.
$dateTrunc() requires MongoDB version 5.0. If you prefer or require year/month/day parts it should be fairly obvious how to modify the aggregation pipeline.

Summarize documents by day of the week

For each student in a collection, I have an array of absences. I want to summarize the data by displaying the number of absences for each day of the week.
Given the following input:
{
"_id" : 9373,
"absences" : [
{
"code" : "U",
"date" : ISODate("2021-01-17T00:00:00.000+0000"),
"full_day" : false,
"remote" : false,
"dayNumber" : 1,
"dayName" : "Sunday"
}
]
}
{
"_id" : 9406,
"absences" : [
{
"code" : "E",
"date" : ISODate("2020-12-09T00:00:00.000+0000"),
"full_day" : false,
"remote" : false,
"dayNumber" : 4,
"dayName" : "Wednesday"
},
{
"code" : "U",
"date" : ISODate("2021-05-27T00:00:00.000+0000"),
"full_day" : false,
"remote" : false,
"dayNumber" : 5,
"dayName" : "Thursday"
}
]
}
How can I achieve the following output:
[
{
"_id": 9373,
"days": [
{
"dayNumber": 1,
"dayName": "Sunday",
"count": 1
}
]
},
{
"_id": 9406,
"days": [
{
"dayNumber": 4,
"dayName": "Wednesday",
"count": 1
},
{
"dayNumber": 5,
"dayName": "Thursday",
"count": 1
}
]
}
]
I've pushed all the required fields to this stage of the pipeline. I'm just not clear how to roll up the data in the nested absences array.
$unwind deconstruct absences array
$group by _id and dayNumber, and get count of grouped documents
$group by _id and reconstruct days array
db.collection.aggregate([
{ $unwind: "$absences" },
{
$group: {
_id: {
_id: "$_id",
dayNumber: "$absences.dayNumber"
},
dayName: { $first: "$absences.dayName" },
count: { $sum: 1 }
}
},
{
$group: {
_id: "$_id._id",
days: {
$push: {
dayName: "$dayName",
dayNumber: "$_id.dayNumber",
count: "$count"
}
}
}
}
])
Playground

MongoDB nested aggregation based on time values

I have a structure like this coming from IOT sensor device
[{
"_id" : ObjectId("5e8e1af3f1563046e084cf65"),
"value" : 462.2382850719,
"start" : 1586293200001,
"year" : 2020,
"month" : "04",
"day" : "07",
"hour" : "21",
"channelId" : 3462
},
{
"_id" : ObjectId("5e8e1af3f1563046e084cf64"),
"value" : 1636.8770905333,
"start" : 1586289600001,
"year" : 2020,
"month" : "04",
"day" : "07",
"hour" : "19",
"channelId" : 3462
},
{
"_id" : ObjectId("5e8e1af3f1563046e084cf63"),
"value" : 1665.4116577475,
"start" : 1586286000001,
"year" : 2020,
"month" : "04",
"day" : "07",
"hour" : "20",
"channelId" : 3462
}]
I want to group this structure first by channelId, then by year,month,day and hour. I want to develop this sort of nested structure
{"channel_id":XXX,"aggregates":[2020:[04:[01:[00:AVG_VALUE,01:AVG_VALUE...],...],...]}
Similar like this
output = [
{
channelId: 3462,
"value": '',
aggregates: [
{
year: 2020,
months: [
{
month: 4,
value: '',
days: [
{
day: 7,
value: '',
hours: [
{ hour: 19, value: '' },
{ hour: 20, value: '' },
{ hour: 21, value: '' }
]
}
]
}
]
}
]
}
]
I can do one level $group by and then push the $$ROOT in it, but don't know how to go nested with groups. Do I have to use reduce logic or any.
Need help
Check if the solution meets your requirements:
db.collection.aggregate([
{
$group: {
_id: {
"channelId": "$channelId",
"year": "$year",
"month": "$month",
"day": "$day",
"hour": "$hour"
},
value: {
$avg: "$value"
}
}
},
{
$group: {
_id: {
"channelId": "$_id.channelId",
"year": "$_id.year",
"month": "$_id.month",
"day": "$_id.day"
},
value: {
$avg: "$value"
},
hours: {
$push: {
hour: "$_id.hour",
value: "$value"
}
}
}
},
{
$group: {
_id: {
"channelId": "$_id.channelId",
"year": "$_id.year",
"month": "$_id.month"
},
value: {
$avg: "$value"
},
days: {
$push: {
day: "$_id.day",
value: "$value",
hours: "$hours"
}
}
}
},
{
$group: {
_id: {
"channelId": "$_id.channelId",
"year": "$_id.year"
},
value: {
$avg: "$value"
},
months: {
$push: {
month: "$_id.month",
value: "$value",
days: "$days"
}
}
}
},
{
$group: {
_id: {
"channelId": "$_id.channelId"
},
channelId: {
$first: "$_id.channelId"
},
value: {
$avg: "$value"
},
aggregates: {
$push: {
year: "$_id.year",
value: "$value",
months: "$months"
}
}
}
},
{
$project: {
_id: 0
}
}
])
MongoPlayground