How to Create Spring Data Aggregate Query - mongodb

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")

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 group ISODate but maintain date format

I know I can group data inside a collection by
{
$group: {
_id: {
day: {
$dayOfMonth: '$timeplay'
},
month: {
$month: '$timeplay'
},
year: {
$year: '$timeplay'
}
},
listeners: {
$sum: '$count'
}
}
However, it does not show the date in ISOdate format anymore which is what I need it to still do.
Is there a way to create ISOdate based on this information? As the output currently looks like the following
You can use $dateFromParts operator to convert to date from parts,
{
$group: {
_id: {
$dateFromParts: {
day: { $dayOfMonth: "$timeplay" },
month: { $month: "$timeplay" },
year: { $year: "$timeplay" }
}
},
listeners: { $sum: '$count' }
}
}
Playground
Use this
db.collection.aggregate([
{
$group: {
_id: { $dateTrunc: { date: "$timeplay", unit: "day" } },
listeners: { $sum: '$count' }
}
}
])

Group documents with MongoDB

I come from the world of relational databases and am having difficulty with a MongoDB query.
In my database I have telemetry documents with minute-by-minute records for each monitored system.
I'm tending to do a query that groups records by hour.
Below is a demonstration of the structure of my documents:
{
_id: ObjectId("61847b83618fc8a6fb368ab7"),
system_code: 22,
date: ISODate("2021-08-01T00:00:01.000Z"),
value1: 22.2372973251,
value2: 20
}
Here's the structure of the query I'm trying to run:
db.telemetry.aggregate([
{
$match: {
$and: [
{ system_code: 22 },
{ date: { $gte: ISODate("2021-08-01 00:00:00") } },
{ date: { $lte: ISODate("2021-08-01 02:00:00") } }
]
}
},
{
$addFields: {
italianDate: {
$dateToString: {
format: "%d/%m/%Y %H:00:00",
date: "$date"
}
}
}
},
{
$sort: {
italianDate: 1
}
},
{
$group: {
_id: {
year: { $year: "$date" },
month: { $month: "$date" },
day: { $dayOfMonth: "$date" },
hour: { $hour: "$date" }
}
}
}
]);
The SQL query with PostgreSQL is exactly this:
SELECT TO_CHAR(t.date, 'YYYY-MM-DD HH24:00:00') AS italianDate, AVG(value1), AVG(value2) FROM telemetry t WHERE t.system_code = 22 AND t.date BETWEEN '2021-08-01 00:00:00' AND '2021-08-01 02:00:00' GROUP BY italianDate ORDER BY italianDate ASC
Thank you so much if you can help me.
You are actually pretty close. You just need to put the $avg inside your $group stage. Another thing is you need to pay attention to the date formats. Refer to this official MongoDB document for the formats.
db.telemetry.aggregate([
{
$match: {
system_code: 22,
date: {
$gte: ISODate("2021-08-01T00:00:00Z"),
$lte: ISODate("2021-08-01T02:00:00Z")
}
}
},
{
$addFields: {
italianDate: {
$dateToString: {
format: "%Y-%m-%d %H:00:00",
date: "$date"
}
}
}
},
{
$group: {
_id: "$italianDate",
avgValue1: {
$avg: "$value1"
},
avgValue2: {
$avg: "$value2"
}
}
},
{
$sort: {
_id: -1
}
}
])
Here is the Mongo playground for your reference.

mongo Aggregate and return results by months

I have this...
$group: {
'_id': this.userId,
'hours': {
$sum: '$hours'
},
'magazines': {
$sum: '$magazines'
},
'brochures': {
$sum: '$brochures'
},
'books': {
$sum: 'books'
}
}
}, {
$project: {
hours: '$hours',
magazines: '$magazines',
brochures: '$brochures',
books: '$books'
}
... which returns the sum of the above fields.
What I want to do is, return the total sum of, say, hours for a particular user, grouped into months.
Something like:
January:
userId:
hours: 10
magazines: 10
....
February:
userId:
hours:2
magazines: 2
etc
Use the following aggregation pipeline which uses the date aggregation operators $year and $month to get the total sums for the given attributes per user grouped into months:
var pipeline = [
{
"$group": {
"_id": {
"userId": "$userId"
"year": { "$year": "$date" }
"month" { "$month": "$date" }
},
'hours': {
$sum: '$hours'
},
'magazines': {
$sum: '$magazines'
},
'brochures': {
$sum: '$brochures'
},
'books': {
$sum: 'books'
}
]
}
]
db.collection.aggregate(pipeline);
To get the exact schema where you convert the month and userId values to keys, use the map() method from the cursor returned by the aggregate() method:
var result = db.collection.aggregate(pipeline).map(function (doc){
var obj = {};
obj[doc._id.month][doc._id.userId]["hours"] = doc.hours;
obj[doc._id.month][doc._id.userId]["magazines"] = doc.magazines;
obj[doc._id.month][doc._id.userId]["brochures"] = doc.brochures;
obj[doc._id.month][doc._id.userId]["books"] = doc.books;
return obj;
});
printjson(result);
This is the solution that works. Answer on MongoDB-User group:
[{
$group: {
_id: {
$month: "$createdAt"
},
hours: {
$sum: "$hours",
},
magazines: {
$sum: "$magazines"
},
brochures: {
$sum: "$brochures"
},
books: {
$sum: "$books"
}
}
}]

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.