Публикация документов в коллекции клиенту meteor в зависимости от наличия конкретного документа в другой коллекции (publish-with-relations)

У меня две коллекции

  1. предложения (соответствующие поля: _id)
  2. ShareRelations (соответствующие поля:receiverId и offerId)

и я хотел бы опубликовать только предложения для зарегистрированного пользователя,которые были переданы ему.

на самом деле, я делаю это, используя вспомогательный массив (visibleOffers), который я заполняю циклом для каждого ShareRelations и использую этот массив позже Предложения.найти как $ в селекторе.

интересно, может ли это быть Метеор способом сделать это, или если бы я мог сделать с меньшим и / или более красивым кодом?

мой фактический код для публикации предложений следующим образом:

Meteor.publish('offersShared', function () {
  // check if the user is logged in
  if (this.userId) {
    // initialize helper array
    var visibleOffers = [];
    // initialize all shareRelations which the actual user is the receiver
    var shareRelations = ShareRelations.find({receiverId: this.userId});
    // check if such relations exist
    if (shareRelations.count()) {
      // loop trough all shareRelations and push the offerId to the array if the value isn't in the array actually
      shareRelations.forEach(function (shareRelation) {
        if (visibleOffers.indexOf(shareRelation.offerId) === -1) {
          visibleOffers.push(shareRelation.offerId);
        }
      });
    }
    // return offers which contain the _id in the array visibleOffers
    return Offers.find({_id:  { $in: visibleOffers } });
  } else {
    // return no offers if the user is not logged in
    return Offers.find(null);
  }
});

кроме того, фактическое решение имеет недостаток, что если создаются новые отношения акций, коллекция предложений на клиенте не обновляется с новым видимым предложением мгновенно (read: page reload required. Но я не конечно, если это происходит из-за этого метода публикации или из-за какого-то другого кода, этот вопрос не является основным из-за этой проблемы).

3 ответов


то, что вы ищете, - это реактивное соединение. Это можно сделать с помощью функции публикации observe или библиотеки. Метеор ядро, как ожидается,присоединиться к библиотеке в какой-то момент, но до тех пор я бы посоветовал использовать публикации-с-связей. Взгляните на документы, но я думаю, что функция публикации, которую вы хотите, выглядит примерно так:

Meteor.publish('offersShared', function() {
  return Meteor.publishWithRelations({
    handle: this,
    collection: ShareRelations,
    filter: {receiverId: this.userId},
    mappings: [{collection: Offers, key: 'offerId'}]
  });
});

Это должно реактивно публиковать все ShareRelations для пользователя и всех связанных Offers. Надеюсь, издательство не будет проблемой.

PWR-довольно законный пакет-некоторые из нас используют его в производстве, и Тома Коулмана способствует этому. Единственное, о чем я вас предупреждаю, это то, что на момент написания этой статьи текущая версия в atmosphere (v0.1.5) имеет ошибку, которая приведет к довольно серьезной утечке памяти. Пока он не наткнулся, см. my блоге о запуске обновленного местного копировать.

обновление 2/5/14:

блог discover meteor имеет отличный пост на реактивные соединения который я настоятельно рекомендую прочитать.


способ сделать это-вдоль линий этого вопроса, используя observeChanges(). Все еще пытаясь понять, как заставить все это работать для моего примера, см. Meteor, один ко многим отношениям и добавить поле только в коллекцию на стороне клиента в публикации?


можно использовать reactive-опубликовать пакета (я один из авторов):

Meteor.publish('offersShared', function () {
  // check if the user is logged in
  if (this.userId) {
    this.autorun(function (computation) {
      // initialize helper array
      var visibleOffers = [];
      // initialize all shareRelations which the actual user is the receiver
      var shareRelations = ShareRelations.find({receiverId: this.userId}, {fields: {offerId: 1}});
      // loop trough all shareRelations and push the offerId to the array if the value isn't in the array actually
      shareRelations.forEach(function (shareRelation) {
        if (visibleOffers.indexOf(shareRelation.offerId) === -1) {
          visibleOffers.push(shareRelation.offerId);
        }
      });
      // return offers which contain the _id in the array visibleOffers
      return Offers.find({_id:  { $in: visibleOffers } });
    });
  } else {
    // return no offers if the user is not logged in
    return Offers.find(null);
  }
});

вы можете просто обернуть существующий нереактивный код в autorun и он начнет работать. Просто будьте осторожны, чтобы быть точным, какие поля вы запрашиваете, потому что если вы запрашиваете все поля, то autorun будет перезапущен при любом изменении поля ShareRelations не только offerId.