Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find only newest document in collection with set of IDs

I have a collection Feeds and a collection FeedElements. FeedElements documents have a reference to the Feeds collection via the feedId field. Furthermore, all FeedElements documents have the date field submitted.

I want to publish only the newest (determined by the field submitted) FeedElements document which corresponds to exactly one Feeds document.

Here is what I have tried:

Meteor.publish('recentFeedElements', function (userId) {
    var feedIds = Feeds.find({'userId': userId}).map(function(feed) {
        return feed._id;
    });
    if (feedsIds.length > 0) return FeedElements.find({feedId: {$in: feedIds}}, {sort: {submitted: -1});
    else this.ready();
});

The problem is, if I use limit in combination with sort inside the FeedElements.find() query, I only get the newest documents of all Feed documents. However, I want to have a strict 1-1 relation. So, one Feed document -> the newest FeedElements document with the appropriate reference.

like image 512
user3475602 Avatar asked Nov 08 '15 10:11

user3475602


1 Answers

If I understand you right, you want every feed to have lastActivity field which contains timestamp of last submitted feed element of this feed. You want this field to be reactive and you don't want to publish all feed elements.

In this case, aggregation is not a solution, because Meteor does not allow reactive aggregations.

You need to use low-level publish API: http://docs.meteor.com/#/full/meteor_publish (see example section).

Low-level publish API (this.added/this.removed/this.changed/others) gives you full control of what data you send to client via Meteor.publish method.

Here is how you can solve your problem (I use ES2015 syntax):


// client/client.js
Meteor.subscribe('userFeeds', { userId: 1 });

// lib/lib.js
Feeds = new Mongo.Collection('feeds');
FeedElements = new Mongo.Collection('FeedElements');

// server/server.js

// When setting `submitted` field, automatically set `submittedAt` field
// (I use matb33:collection-hooks here)
FeedElements.before.update((userId, elem, fieldNames, modifier) => {
  if (modifier.$set.submitted) {
    modifier.$set.submittedAt = new Date();
  }
});

function getLastActivity(feedId) {
  const lastSubmittedElem = FeedElements.findOne(
    {
      feedId,
      submitted: true,
    },
    {
      sort: { submittedAt: -1 }
    }
  );

  return lastSubmittedElem ? lastSubmittedElem.submittedAt : null;
}

Meteor.publish('userFeeds', function({ userId }) {
  const elemsObservers = {};

  // Observe all user feeds
  var feedObserver = Feeds.find({ userId: userId }).observeChanges({
    added: (feedId, fields) => {
      // Observe feed elements of the feed
      elemsObservers[feedId] = FeedElements.find({ feedId }).observeChanges({
        changed: (elemId, fields) => {
          // Update `lastActivity` field when new element is submitted
          if (fields.submitted) {
            this.changed('feeds', feedId, { lastActivity: fields.submittedAt });
          }
        },
      });

      fields.lastActivity = getLastActivity(feedId);

      this.added('feeds', feedId, fields);
    },

    changed: (feedId, fields) => {
      this.changed('feeds', feedId, fields);
    },

    removed: (feedId) => {
      elemsObservers[feedId].stop();
      delete elemsObservers[feedId];

      this.removed('feeds', feedId);
    },
  });

  this.ready();

  this.onStop(function() {
    feedObserver.stop();

    for (const feedId in elemsObservers) {
      elemsObservers[feedId].stop();
    }
  });
});

Also I prepared github repo https://github.com/imkost/feeds. Just git clone and meteor run.

like image 165
imkost Avatar answered Sep 26 '22 22:09

imkost