Search code examples
javascripttypescriptmongodbmongoose

How to select the last record for each record type in a MongoDB with typescript and mongoose


I've got a mongoDB collection that looks like:

[
 {user: "joe",  event: "foo", timestamp: 1},
 {user: "meg",  event: "blah", timestamp: 2},
 {user: "rick", event: "bork", timestamp: 3},
 {user: "joe",  event: "bing", timestamp: 4},
 {user: "meg",  event: "biz",  timestamp: 5},
 {user: "tim",  event: "boz",  timestamp: 6}
]

and I want to find the latest event for each user, so get a set of results like:

[
 {user: "rick", event: "bork", timestamp: 3},
 {user: "joe",  event: "bing", timestamp: 4},
 {user: "meg",  event: "biz",  timestamp: 5},
 {user: "tim",  event: "boz",  timestamp: 6}
]

I've looked over the mongoose aggregation framework docs, and it seems like there should be a way to do this simply, but none of the examples I've seen do quite exactly this. Lots of them group by user, but then the resulting groupings lose some of the data, and I'm not sure how to reconstitute the full documents at the end.


Solution

    1. $setWindowFields - Partition/Group the document by the user field, order each partition by timestamp descending, and add the rank field with the $rank operator.

    2. $match - Filter the document with rank: 1 indicates getting the document with the latest timestamp for each partition.

    3. $sort - Order the document by timestamp ascending.

    4. $unset - Remove the rank field from the documents.

    db.collection.aggregate([
      {
        $setWindowFields: {
          partitionBy: "$user",
          sortBy: {
            timestamp: -1
          },
          output: {
            rank: {
              $rank: {}
            }
          }
        }
      },
      {
        $match: {
          rank: 1
        }
      },
      {
        $sort: {
          timestamp: 1
        }
      },
      {
        $unset: "rank"
      }
    ])
    

    Demo @ Mongo Playground