Grouping by two fields in MongoDB? - mongodb

I am attempting to get a count of records created on each day for the last 15 days. I came up with the query below:
db.users.aggregate(
{ $group: {
_id: {$dayOfYear: '$created'},
created: {$sum:1},
date: {$first: '$created'}
}
},
{$sort: {_id: 1}},
{$limit: 15}
);
Which almost works, but it will also count days from exactly a year ago. So if 2 records were created on 2/20/2014 and 3 were created on 2/20/2013 then a count of 5 would be returned (when all I want is 2). I was hoping to also group by the year like below:
db.users.aggregate(
{ $group: {
_id: {{$dayOfYear: '$created'},{$year: '$created'}},
created: {$sum:1},
date: {$first: '$created'}
}
},
{$sort: {_id: 1}},
{$limit: 15}
);
But this is a syntax error. How should I be attempting to count records created on a specific day?

db.users.aggregate(
{ $group: {
_id: {
day: { $dayOfYear: '$created' },
year: { $year: '$created' }
},
count: { $sum: 1 },
date: { $first: '$created' }
}
},
{ $project: { _id: 0, date: 1, count: 1 } }, // Only for more clear result.
{ $sort: { date: -1 } }, // If you want to view last 15 days - sort by descending.
{ $limit: 15 }
);

Related

How to calculate average records per month?

My records like this [{ createdAt }, {createdAt}, {createdAt} ]
I need average records per month.
january => 3 records
february => 2 records etc..
You can try to $group by month and year when counting and by month when averaging:
db.collection.aggregate([
{
$group: {
_id: {
month: {
$month: "$createdAt"
},
year: {
$year: "$createdAt"
},
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: {
month: "$_id.month"
},
average: {
$avg: "$count"
}
}
},
{
$project: {
_id: 0,
month: "$_id.month",
average: 1
}
}
])
Link to playground
Not fully clear what you mean by "average records per month" but I think it would be this:
db.collection.aggregate([
{
$group: {
_id: {
$dateTrunc: {
date: "$createdAt",
unit: "month"
}
},
count: { $count: {} }
}
},
{
$group: {
_id: null,
data: { $push: { k: { $toString: { $month: "$_id" } }, v: "$count" } }
}
},
{ $replaceWith: { $arrayToObject: "$data" } }
])
Getting the month name is not so easy, either you use a external library or build your own with $switch

mongoDb get array of filter in one shot

I have mongo collection look like
{ day: '1', hour: 10:40 },
{ day: '1', hour: 08:40 },
{ day: '2', hour: 10:10 },
{ day: '2', hour: 08:30 },
I want to get this result.
[{day:'1', hours:['10:40','08:40']},
{day:'2', hours:['10:10','08:30']}]
I try to do this with query with out success to put in hours more than one field.
days= Model.aggregate
([{ $group: { _id: '$day', } }])
and then for each day
Model.find({day:days[0]})
If it's possible to do this in any prettier way.
I would like to know how :)
Use Aggregation Pipeline with $group and $project stages.
db.collection.aggregate([
{
$group: {
_id: "$day",
hours: {
$push: "$hour"
}
}
},
{
$project: {
_id: 0,
day: "$_id",
hours: "$hours"
}
}
])
https://mongoplayground.net/p/GBmNqfQv4Wp

Mongodb: how to sum at breaks in 2 fields?

I have a document sample as follows:
{
_id: ObjectId("5dfa4f7b9254a519a5c7f166"),
Date: "12/11/19",
Description: "Amazon",
Amount: 32.01,
DebitCredit: "debit",
Category: "Shopping",
Month: 12,
Day: 11,
Year: 2019,
Account_Name: "Gold Delta SkyMiles"
}...
This is my query:
db.checks.aggregate([
{ $match: { Category: "Shopping" } },
{ $project: { Year: 1, Month: 1, Category: 1, Amount: { $sum: "$Amount" } } },
{
$group: {
_id: {
Year: "$Year",
Month: "$Month",
Category: "$Category",
Amount: { $sum: "$Amount" }
}
}
},
{ $sort: { Year: 1, Month: 1 } }
]);
I'm looking for a total by each year / month combination. How can I achieve this?
You're almost there, you just need to tweak your $group stage a little:
From the docs:
Groups input documents by the specified _id expression and for each distinct grouping
So all we have to do is take out the amount field outside of the _id field.
{
$group: {
_id: {
Year: "$Year",
Month: "$Month",
},
Amount: { $sum: "$Amount" }
}
}
I removed category as it was redundant but feel free to add it back to your query.
EDIT:
For the $sort stage the fields Year & Month do not exists after the $group which is the reason its failing.
After the $group stage your documents are of the form:
{
_id: {Year: number, Month: number},
Amount: number
}
So just change your $sort into:
{ $sort: { '_id.Year': 1, '_id.Month': 1 } }

Mongodb need count for specific member id per day

Collection name : activity
What I need is activity count
of "memberId" = 123
where activity "type" = 'xxx'
per day
between "11/01/2015" and "11/15/2015" // from date and to date range
Expected Output:
[
{date:"2015-02-22",count:10},
{date:"2015-02-22",count:5},
]
I have no idea how to perform aggregate between dates and for a specific member id
where I am at is far far away from the solution :
db.activity.aggregate(
{ $project: {
date: {
years: {$year: '$dateInserted'},
months: {$month: '$dateInserted'},
days: {$dayOfMonth: '$dateInserted'},
},
memberId: '$memberId'
}},
{ $group: {
_id: { memberId: '$memberId', date: '$date' },
number: { $sum: 1}
}})
db.activity.aggregate([
{$match : { memberId : "xxx",item:"xyz",dateInserted: {$gte: ISODate("2013-01-01T00:00:00.0Z"), $lt: ISODate("2016-02-01T00:00:00.0Z")}}},
{$project: {day: {day: {$dayOfMonth: '$dateInserted'}, month: {$month: '$dateInserted'}, year: {$year: '$dateInserted'}}}},
{$group: { _id: { day: '$day' }, count: { $sum: 1} }},{ $sort:{_id:1}}
]);
Try that
You may need add $match stage before $project:
$match:
{dateInserted:{{$gte:new Date("2015-01-11T00:00:00 -02:00"),{$lte:new Date("2015-01-15T23:59:59 -02:00")}}},
{memberId:123},
{type:"xxx"}

MongoDB Aggregation by Shifted Date

Is there a way to aggregate by day, but over a 24 hour period that does not go from 12am - 11:59pm? A sample document looks like this:
{
date: ISODate("2012-11-02T17:04:11.102Z"),
user: 'testUser',
orders: 50
}
I need to aggregate the # of orders per user per day between 5 pm and 4:59:59.999 pm the next day. I can get the # of orders per user per day (over a 2 day range) using this:
db.hs.aggregate([{
$match: {
user: 'testUser',
date: {
$gte: new Date(2015,0,4,17,0,0,0),
$lt: new Date(2015,0,6,17,0,0,0)
}
}
}, {
$group: {
_id: {
date: {
month: {$month: "$date"},
day: {$dayOfMonth: "$date"},
year: {$year: "$date"}
},
user: "$user",
},
totord: {$sum: "$orders"}
}
}])
But this returns 3 results, one for Jan 4 after 5pm, one for Jan 5 all day, and one for Jan 6 before 5pm. I just don't know how to shift the aggregation to be between 5pm - 4:59pm.
I believe that you could use the $add aggregation operator on your dates in a $project aggregation step to get the desired result.
db.hs.aggregate([
{
$match: {
user: 'testUser',
date: {
$gte: new Date(2015,0,4,17,0,0,0),
$lt: new Date(2015,0,6,17,0,0,0)
}
}
}, {
$project: {
orders: 1,
user: 1,
date: { $add: [ "$date", 7*60*60000 ] }
}
}, {
$group: {
_id: {
date: {
month: { $month: "$date" },
day: { $dayOfMonth: "$date" },
year: { $year: "$date" }
},
user: "$user"
},
totord: {$sum: "$orders"}
}
}
])
I believe this should add 7 hours to the $date which should make it so anything after 5PM ends up after midnight the next day.