Tj Gienger
Tj Gienger

Reputation: 1405

Meteor: find 5 most recent documents without repeating one of the fields

I have a posts collection. Each post has a thread as part of the document.

Here is some pseudo code for a post document:

{
  '_id':...,
  'date': date,
  'thread': 'thread name is a string',
  'message': 'whatever the person wrote',
}

I want to receive the 5 most recent posts, but none of the 5 returned can be from the same thread. I hope that explain it good enough. How would I do this? Keeping it reactive preferred, but not completely necessary.

Update: Just wanted to thank Tom for the code and pointing out rawCollection, very useful.

Upvotes: 0

Views: 242

Answers (1)

Tom Kelsey
Tom Kelsey

Reputation: 143

Just as a disclaimer, I took this as a bit of a learning exercise so whilst it should get you the result you're after I cannot comment as to how efficient it is!

MongoDB has a distinct() function that returns unique results but you cannot then sort/limit by what it returns. Not much use.

Instead, I think you'll have to use MongoDB's aggregate() function.

You cannot natively access aggregate() via Meteor, hence MurWade's suggestion of using a package. However, if you're using Meteor 1.0.4 or above you can make use of rawCollection() to access the aggregate() function.

rawCollection() can only be called server-side, so my example below is a Meteor method that returns the _ids of the five most recent, distinct, posts.

if (Meteor.isServer) {
  Meteor.methods({
    recentDistinctPostIds: function() {
      var rawPosts = Posts.rawCollection();
      var aggregateQuery = Meteor.wrapAsync(rawPosts.aggregate, rawPosts);
      return aggregateQuery([{
        $group: {
          _id: '$thread',
          date: {$max: '$date'},
          postId: {$first: '$_id'}
        }},
        {$sort: {date: -1}},
        {$limit: 5},
      ]);
    } 
  });
}

You would then call the method client side with a callback, e.g.

Meteor.call('recentDistinctPostIds', function(err, result) {
 console.log(result)
});

I hope that makes sense but just shout if it doesn't work as intended!

Upvotes: 1

Related Questions