Как найти только новейший документ в коллекции с набором идентификаторов

У меня есть коллекция Feeds и коллекция FeedElements. В документах FeedElements имеется ссылка на коллекцию Feeds через поле feedId. Кроме того, все документы FeedElements имеют поле даты submitted.

Я хочу опубликовать только новый (определяемый полем submitted) FeedElements документ, который соответствует точно одному документу Feeds.

Вот что я пробовал:

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();
});

Проблема в том, что если я использую limit в сочетании с sort внутри запроса FeedElements.find(), я получаю только самые новые документы всех документов Feed. Тем не менее, я хочу иметь строгое отношение 1-1. Итак, один документ Feed → самый новый FeedElements документ с соответствующей ссылкой.

Ответы

Ответ 1

Если я правильно понимаю, вы хотите, чтобы в каждом фиде было поле lastActivity, которое содержит отметку времени последнего поданного элемента фида этого фида. Вы хотите, чтобы это поле было реактивным, и вы не хотите публиковать все элементы фида.

В этом случае агрегация не является решением, потому что Метеор не допускает реактивных агрегатов.

Вам необходимо использовать низкоуровневый API публикации: http://docs.meteor.com/#/full/meteor_publish (см. раздел примера).

Низкоуровневый публичный API (this.added/this.removed/this.changed/others) дает вам полный контроль над тем, какие данные вы отправляете клиенту с помощью метода Meteor.publish.

Вот как вы можете решить свою проблему (я использую синтаксис ES2015):


// 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();
    }
  });
});

Также я подготовил github repo https://github.com/imkost/feeds. Просто git clone и meteor run.

Ответ 2

Вы можете получить это по структуре агрегации $sort, $group и $first.

db.FeedElements.aggregate(
[
  { $match: { feedId: {$in:[1,2,3]}} },
  { $sort: { submitted: -1} },
  {
    $group:
    {
      _id: "$feedId",
      firstFeedElement: {$first : "$$ROOT"} // Get first whole document or you can take fields you want
    }
     }
   ]
)