Перекрывающиеся публикации Meteor

У меня есть метеорное приложение, в котором есть 2 публикации для постов. Один для всех сообщений и один для избранных сообщений. Есть 2 избранных поста - "Пост 1" и "Пост 4". Я показываю избранные сообщения на всех страницах, а все сообщения (включая избранные сообщения) разбиваю на страницы, отсортированные по имени. Когда я перемещаюсь между страницами, данные из двух публикаций смешиваются и показывают неверные результаты.

Вот код:

Meteor.publish('posts', function(page) {
  const skip = parseInt(page && page !== '' ? page : 0) * 3
  return Posts.find({}, {
    limit: 3,
    skip,
    sort: {
      name: 1
    }
  });
});

Meteor.publish('featured', function() {
  return Posts.find({
    featured: true
  }, {
    sort: {
      name: 1
    }
  });
});

На клиенте я подписываюсь на оба и отображаю данные в 2 цикла

Template.hello.onCreated(function helloOnCreated() {
  const instance = this;
  instance.autorun(function() {
    instance.subscribe('posts', FlowRouter.getParam('page'))
    instance.subscribe('featured')
  });
});

Template.hello.helpers({
  posts() {
    return Posts.find({}, {
      limit: 3,
      sort: {
        name: 1
      }
    })
  },
  featured_posts() {
    return Posts.find({
      featured: true
    }, {
      sort: {
        name: 1
      }
    });
  }
});

HTML-шаблон выглядит следующим образом:

<template name="hello">
  <h2>Featured</h2>
  {{#each featured_posts}}
    {{> post}}
  {{/each}}
  <h2>Posts</h2>
  {{#each posts}}
    {{> post}}
  {{/each}}
</template>

Проблема в том, что данные двух подписок смешиваются на дисплее.

На странице 1 он показывает это правильно:

Page 1

Featured
  post 1
  post 4

All Posts
  post 1
  post 2
  post 3

но когда я перехожу на страницу 2

Page 2

Featured
  post 1
  post 4

All Posts  -- Should be
  post 1        post 4
  post 4        post 5
  post 5        post 6

Он показывает «сообщение 1» внутри «сообщений», которые показаны, но не должны быть на странице 2. Когда я перехожу на страницу 3, я вижу «сообщение 1» и «сообщение 4», но их там не должно быть.

Я понимаю, как работают публикации и подписки и почему это происходит — потому что публикации сливают набор данных. Мне интересно, есть ли работа, чтобы держать их отдельно?


person Addy    schedule 16.05.2017    source источник


Ответы (2)


Если я правильно понимаю, ваши страницы соответствуют нумерации страниц вашего списка «Все сообщения». Номер «страницы» отправляется в качестве параметра вашей подписки, так что вы получаете краткий список ваших сообщений.

Сложность здесь действительно в том, что в вашей клиентской коллекции нет всех ваших документов на руках (поскольку вы ограничиваете их в своей 'posts' публикации), поэтому вы не можете использовать аналогичную skip логику, как в публикации.

Как предлагается в Руководстве по метеорам > Подписка на страницы, вы можете использовать percolate:find-from-publication Пакет Atmosphere для простого извлечения документов из вашей 'posts' публикации и только их.

// Server
FindFromPublication.publish('posts', function(page) {
  // Same logic
  const skip = parseInt(page && page !== '' ? page : 0) * 3
  return Posts.find({}, {
    limit: 3,
    skip,
    sort: {
      name: 1
    }
  });
});

// Client (no change in subscription)
Template.hello.helpers({
  posts() {
      return Posts.findFromPublication('posts', {}, {
        sort: {
          name: 1
        }
      });
    } // (no change in featured_posts)
});
person ghybs    schedule 16.05.2017

Я бы рекомендовал использовать observe или observeChanges, чтобы данные публикуемые файлы могут быть изолированы на клиенте. Документы могут быть пугающими, но на самом деле это проще, чем кажется.

Это просто вопрос создания специальной коллекции на стороне клиента, а затем использования имени этой коллекции в обратных вызовах наблюдения. Обратите внимание, что имя коллекции на стороне клиента, "featured_posts", используется в вызовах self.added, self.changed и self.removed в публикации:

// client-side
const FeaturedPosts = new Mongo.Collection('featured_posts', {
  defineMutationMethods: false
});

// server-side
Meteor.publish('featured', function() {
    const self = this;

    const handle = collection.find(selector, opts).observe({
      added: function(doc) {
        self.added('featured_posts', doc._id, doc);
      },

      changed: function(newDoc, oldDoc) {
        self.changed('featured_posts', newDoc._id, newDoc);
      },

      removed: function(doc) {
        self.removed('featured_posts', doc._id);
      },
    });

    self.ready();

    self.onStop(function(err) {
      if (!err) {
        handle.stop();
      }
    });
  });
};
person Community    schedule 16.05.2017
comment
Я не могу сказать наверняка, так как я не использую помощники для коллекций, но, учитывая, что это просто регистрация функции преобразования, я ожидаю, что она будет работать везде, где работают функции преобразования, то есть с observe. Из документов. Преобразования не применяются для обратных вызовов наблюдаемых изменений или к курсорам, возвращаемым из функций публикации. - person ; 21.05.2017
comment
Кроме того... Если вам нужно объединить данные из других коллекций, имейте в виду, что вы можете выполнять любую пользовательскую логику, которая вам нужна, в обратных вызовах. Кроме того, вы можете передать функцию преобразования в find/findOne, чтобы переопределить преобразование на уровне коллекции. - person ; 21.05.2017