Count of groups of certain groups of documents - mongodb

Documents in this mongodb has the property createdAt = Date.now() which is the epoch time as well as the properties userid and name.
What is needed is the count of documents for name='abc' which are created less or equal to 4 months ago, but counting those documents which were created by the same userId and on the same day as one count.
What is the best way to go about it? aggregate or map or combination or ...

You can try below aggregation.
var date = new Date(Date.UTC(2017, 2, 3, 0, 0, 0));
date.setMonth(date.getMonth() - 4);
db.collection.aggregate([{
$match: {
createdAt: {
$gte: date,
$lte: new Date(Date.UTC(2017, 2, 3, 0, 0, 0))
},
name: "abc"
}
}, {
$group: {
_id: {
date: {
$dayOfMonth: "$createdAt"
},
month: {
$month: "$createdAt"
},
year: {
$year: "$createdAt"
},
userid: "$userid"
},
count: {
$sum: 1
}
}
}]);

Related

How to handle unordered bucket boundaries in mongodb?

I need to retrieve all profile views a user received within 15 days. I made a working query that group all views by day.
A view is a very simple document:
{
viewerId: string,
username: string,
profileId: string,
day: 4
}
The day key matches the current day of the month when the view was stored. So I made this query:
static getProfileViews(userId: string) {
const fifteenDaysRange = this.getDaysRange(15);
return ProfileViewsDB.aggregate([
{ $match: { profileId: userId } },
{
$bucket: {
groupBy: "$day",
boundaries: fifteenDaysRange,
default: fifteenDaysRange[fifteenDaysRange.length - 1] + 1,
output: {
count: {
$sum: 1,
},
viewers: {
$push: {
_id: "$viewerId",
username: "$username",
},
},
},
},
},
]);
}
It works, except when the days range is across two months. So the boundaries are: [29 ,30, 31, 1, 2, 3, 4], instead of a regular incremental list. In this case, mongo crashes, claiming that the boundaries must be properly ordered.
How to fix this? Is there a trick here to handle this edge-case?
Not clear what you are looking for, but I assume it should work with this:
db.ProfileViewsDB.aggregate([
{ $match: { profileId: userId } },
{ $match: { date: { $gte: moment().startOf('day').subtract(15, 'day').toDate() } } },
{
$group: {
_id: { y: { $year: "$date" }, m: { $month: "$date" }, d: { $dayOfMonth: "$date" } },
count: { $sum: 1 },
viewers: {
$push: { _id: "$viewerId", username: "$username", },
}
}
}
])
Whenever one has to work with date/times then I recommend the moment.js library, it makes your life easier.
Note, you may add the timezone, i.e.
_id: {
y: { $year: "$date", timezone: "Europe/Zurich" },
m: { $month: "$date", timezone: "Europe/Zurich" },
d: { $dayOfMonth: "$date", timezone: "Europe/Zurich" }
}

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 } }

How to Create Spring Data Aggregate Query

I am new to Spring-Data-Mongo. I have created following query for getting birthday count from each month. Can anyone help me to convert in Spring Data aggreate query.
db.getCollection('users').aggregate(
[
{ $group:
{ _id: { month: { $month: "$birthDate" }, },
count: { $sum:1 },
date: { $first: "$birthDate" }
}
},
{
$project:
{
month:
{
$dateToString: { format: "%m", date: "$date" }
},
count: 1,
_id: 0
}
}])
Here what I have tried so far however it's not giving output what I want.
Aggregation agg = Aggregation.newAggregation(
Aggregation.group("month1").first("birthDate").as("date").count().as("count"),
Aggregation.project("_id").and("date").dateAsFormattedString("%m").as("month").and("count").as("count").andExpression("month(date)").as("month1")

Grouping by two fields in 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 }
);

Counting user activity with MongoDB Aggregate Framework

I will try to keep this simple. I have user objects, inside my user objects I have a field that is an array which just contains ISODates of the the days a user has logged in. I would like to count how many users logged in on a particular date for all dates that exist.
Sample user:
{
"_id": "some_id",
"name": "bob",
"logins": [isodate, isodate, isodate...],
//...
}
I'd like an output that tells me something like:
{
"date": ISODate,
"number_of_users_logged_in": 10
}
Is this possible? How would I go about doing it?
You need to use $unwind operation explode array, then $group by date (using the granularity that you want) and $project only the date and count, as below:
db.user.aggregate({
$unwind: "$logins"
},
{
$group: {
_id: {
year: {
$year: "$logins"
},
month: {
$month: "$logins"
},
day: {
$dayOfMonth: "$logins"
},
hour: {
$hour: "$logins"
}
},
date: {
$first: "$logins"
},
count: {
$sum: 1
}
}
},
{
$project: {
_id : 0,
date: "$date",
number_of_users_logged_in: "$count"
}
})
I grouped by year/month/day/hour.