MongoDB Query to Find Latest Record Per Group
$sort by group key and date descending, then $group by the group key picking the first record with $first, like: db.collection.aggregate([{ $sort: { groupField: 1, dateField: -1 } }, { $group: { _id: "$groupField", latestRecord: { $first: "$$ROOT" } } }]).Examples
How to Think About It
Algorithm
Code
db.collection.aggregate([
{ $sort: { category: 1, date: -1 } },
{ $group: { _id: "$category", latestRecord: { $first: "$$ROOT" } } }
])Dry Run
Let's trace the example with records grouped by 'category' and sorted by 'date' descending.
Sort records
Sort by category ascending and date descending: [{_id:2, category:'A', date:'2023-01-05'}, {_id:1, category:'A', date:'2023-01-01'}, {_id:3, category:'B', date:'2023-01-03'}]
Group and pick first
Group by category: 'A' -> first record {_id:2,...}, 'B' -> first record {_id:3,...}
| category | date | picked latest _id |
|---|---|---|
| A | 2023-01-05 | 2 |
| B | 2023-01-03 | 3 |
Why This Works
Step 1: Sorting ensures latest first
Sorting by the date descending puts the newest record at the top for each group.
Step 2: Grouping collects by key
Grouping by the group key gathers all records of the same group together.
Step 3: Picking first record per group
Using $first after sorting picks the latest record for each group.
Alternative Approaches
db.collection.aggregate([
{ $group: { _id: "$category", maxDate: { $max: "$date" } } },
{ $lookup: {
from: "collection",
let: { cat: "$_id", maxD: "$maxDate" },
pipeline: [
{ $match: { $expr: { $and: [ { $eq: ["$category", "$$cat"] }, { $eq: ["$date", "$$maxD"] } ] } } }
],
as: "latestRecord"
} },
{ $unwind: "$latestRecord" }
])db.collection.aggregate([
{ $sort: { date: -1 } },
{ $group: { _id: "$category", latestRecord: { $first: "$$ROOT" } } }
])Complexity: O(n log n) time, O(n) space
Time Complexity
Sorting all records by group and date takes O(n log n) time, which dominates the aggregation.
Space Complexity
Aggregation stores intermediate results and groups, requiring O(n) space in worst case.
Which Approach is Fastest?
The $sort + $group with $first approach is usually faster than $group + $lookup because it avoids extra joins.
| Approach | Time | Space | Best For |
|---|---|---|---|
| $sort + $group with $first | O(n log n) | O(n) | Simple and efficient for large datasets |
| $group + $max + $lookup | O(n) + join cost | O(n) | When you need full record but sorting is expensive |
| $sortByCount + $first (legacy) | O(n log n) | O(n) | Simple cases with unique keys |