Given the following dataset:
category | date | amount |
---|---|---|
fruit | 2023-01-01 | 1 |
fruit | 2023-01-02 | 2 |
meat | 2023-01-03 | 3 |
nuts | 2023-01-04 | 4 |
nuts | 2023-01-05 | 5 |
nuts | 2023-01-06 | 6 |
Is it possible to do a multi-group aggregation in Mongo JPA that yields:
- One row per category
- Count of rows in that category
- Amount on
max(date)
category | count | last_amount |
---|---|---|
fruit | 2 | 2 |
meat | 1 | 3 |
nuts | 3 | 6 |
This should be possible in a relational database with some SQL grouping and possibly sub-queries but seems incredibly difficult to achieve in Mongo. I could get get the row count in one aggregation but seems like I would need multiple operations to achieve what I need.
2
Answers
This was genuinely surprisingly difficult to achieve. Charchit pointed me in the right direction but to get to the exact response I wanted required so much more effort.
Explanation of stages required:
$group
: group the collection bycategory
and obtain the$sum
and the$max($date)
of each category$lookup
: join the collection with itself to obtain theamount
at themaxDate
$lookup
pipeline
is required because a multi-field match needs to be performed ensuring the match is for themaxDate
only for that givencategory
$lookup
let
is required because$lookup
pipeline
somehow cannot reference a previous stage field directly. note the double$
required to referencelet
variables like$$maxDate
$project
is used because we are only interested in the amount, so the rest of the$lookup
document needs to be discarded$replaceRoot
+$mergeObjects
+$arrayElemAt
is used to merge the first entry in the$lookup
result up into the root document$project
removes the (now) unneededmaxDate
andlatestAmount
working fieldsHere's the resulting query that meets the exact specification in the question:
Playground
Try this:
In this query, we first convert the string date into a date object. This step is not required if your dates are already stored as dates. Next, we sort the documents, by date in ascending order. Finally, we group them by
category
, calculating thecount
andlast_amount
.Playground link.
You can use this if you don’t want to use sorting.
Playground.