Понимание публикации Meteor/Подписка

У меня есть простое приложение, которое показывает список Projects. Я удалил пакет autopublish, чтобы я не отправлял все клиенту.

 <template name="projectsIndex">    
   {{#each projects}}      
     {{name}}
   {{/each}}
 </template>

Когда autopublish был включен, это отобразило бы все проекты:

if Meteor.isClient
  Template.projectsIndex.projects = Projects.find()

С его удалением я должен дополнительно выполнить:

 if Meteor.isServer
   Meteor.publish "projects", ->
     Projects.find()
 if Meteor.isClient
   Meteor.subscribe "projects"
   Template.projectsIndex.projects = Projects.find()

Итак, правильно ли сказать, что метод find() на стороне клиента ищет только записи, которые были опубликованы с серверной стороны? Это меня сбивало с толку, потому что я чувствовал, что мне нужно только позвонить find() один раз.

Ответ 1

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

Здесь Sacha Greif (соавтор DiscoverMeteor), объясняя публикации и подписки на одном слайде:

subscriptions

Чтобы правильно понять, почему вам нужно звонить find() более одного раза, вам нужно понять, как работают коллекции, публикации и подписки в Meteor:

  • Вы определяете коллекции в MongoDB. Метеор еще не участвует. Эти коллекции содержат записи базы данных (также называемые "документами" как Mongo и Meteor, но "документ" более общий, чем запись базы данных, например, спецификация обновления или селектор запросов - это документы - объекты JavaScript, содержащие пары field: value).

  • Затем вы определяете collections на сервере Meteor с помощью

    MyCollection = new Mongo.Collection('collection-name-in-mongo')
    

    Эти коллекции содержат all данные из коллекций MongoDB, и вы можете запустить MyCollection.find({...}) на них, что вернет cursor (набор записей, с помощью методов для их повторения и возврата их).

  • Этот курсор (большую часть времени) используется для publish (отправить) набор записей (называемый "набором записей). Вы можете по желанию опубликовать только несколько полей из этих записей. Это набор записей (а не коллекции), в которых клиенты subscribe. Публикация выполняется публикация функции, которая вызывается каждый раз, когда клиент подписывается, и который может принимать параметры для управления возвращаемыми записями (например, идентификатор пользователя, чтобы возвращать только те пользовательские документы).

  • На клиенте у вас есть Minimongo коллекции, которые частично отражают некоторые записи с сервера. "Частично", потому что они могут содержать только некоторые из полей и "некоторые записи", потому что вы обычно хотите отправить клиенту только нужные ему записи, ускорить загрузку страницы и только те, которые ей нужны, и имеют разрешение на доступ.

    Minimongo - это, по сути, встроенная в память, непостоянная реализация Mongo в чистом JavaScript. Он служит в качестве локального кеша, который хранит только подмножество базы данных, с которой работает этот клиент. Запросы на клиенте (поиск) подаются непосредственно из этого кеша, не разговаривая с сервером.

    Эти коллекции Minimongo изначально пусты. Они заполнены

    Meteor.subscribe('record-set-name')
    

    вызовы. Обратите внимание, что параметр subscribe не является именем коллекции; это имя набора записей, которое сервер использовал в вызове publish. Вызов subscribe() подписывает клиента на набор записей - подмножество записей из коллекции сервера (например, самые последние 100 сообщений в блоге) со всеми или подмножеством полей в каждой записи (например, только title и date). Как Minimongo знает, в какую коллекцию разместить входящие записи? Имя коллекции будет аргументом collection, используемым в обработчиках публикации added, changed и removed обратных вызовах, или если они отсутствуют (что больше всего времени), это будет имя коллекции MongoDB на сервере.

Изменение записей

Здесь Meteor делает вещи очень удобными: когда вы изменяете запись (документ) в коллекции Minimongo на клиенте, Meteor мгновенно обновляет все шаблоны, которые зависят от нее, а также отправляет изменения обратно на сервер, который, в свою очередь, сохранит изменения в MongoDB и отправит их соответствующим клиентам, которые подписались на набор записей, включая этот документ. Это называется компенсацией задержек и является одним из семи основных принципов Meteor.

Несколько подписей

У вас может быть множество подписчиков, которые выходят за разные записи, но все они попадают в одну коллекцию на клиенте, если они поступают из одной коллекции на сервере, основываясь на их _id. Это не объясняется четко, но подразумевается документами Meteor:

Когда вы подписываетесь на набор записей, он сообщает серверу отправлять записи клиенту. Клиент хранит эти записи в локальных коллекциях Minimongo с тем же именем, что и аргумент collection, используемый в обработчике публикации added, changed и removed обратных вызовах. Meteor будет помещать входящие атрибуты в очередь, пока вы не объявите Mongo.Collection на клиенте с соответствующим именем коллекции.

Что не объясняется, так это то, что происходит, когда вы явно не используете added, changed и removed, или вообще не публикуете обработчики - это большая часть времени. В этом наиболее распространенном случае аргумент коллекции (неудивительно) берется из имени коллекции MongoDB, которую вы объявили на сервере на шаге 1. Но это означает, что вы можете иметь разные публикации и подписки с разными именами, а все записи будут попадать в одну коллекцию на клиенте. До уровня полей верхнего уровня Meteor позаботится о том, чтобы выполнить объединение между документами, так что подписки могут перекрываться - публиковать функции, которые отправляют разные поля верхнего уровня для работы клиента бок о бок и на клиентом, документ в коллекции будет объединение двух наборов полей.

Пример: несколько подписей, заполняющих одну и ту же коллекцию на клиенте

У вас есть коллекция BlogPosts, которую вы объявляете одинаково на сервере и на клиенте, даже если он делает разные вещи:

BlogPosts = new Mongo.Collection('posts');

На клиенте BlogPosts может получать записи из:

  • подписка на самые последние 10 сообщений в блоге

    // server
    Meteor.publish('posts-recent', function publishFunction() {
      return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
    }
    // client
    Meteor.subscribe('posts-recent');
    
  • подписка на текущие сообщения пользователя

    // server
    Meteor.publish('posts-current-user', function publishFunction() {
      return BlogPosts.find({author: this.userId}, {sort: {date: -1}, limit: 10});
      // this.userId is provided by Meteor - http://docs.meteor.com/#publish_userId
    }
    Meteor.publish('posts-by-user', function publishFunction(who) {
      return BlogPosts.find({authorId: who._id}, {sort: {date: -1}, limit: 10});
    }
    
    // client
    Meteor.subscribe('posts-current-user');
    Meteor.subscribe('posts-by-user', someUser);
    
  • подписка на самые популярные сообщения

  • и др.

Все эти документы поступают из коллекции posts в MongoDB через коллекцию BlogPosts на сервере и заканчиваются в коллекции BlogPosts на клиенте.

Теперь мы можем понять, почему вам нужно вызывать find() более одного раза - во второй раз на клиенте, потому что документы из всех подписей попадут в один и тот же сборник, и вам нужно будет выбрать только те, которые вам интересны, Например, чтобы получать самые последние сообщения на клиенте, вы просто зеркалируете запрос с сервера:

var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});

Это вернет курсор ко всем документам/записям, которые клиент получил до сих пор, как к верхним сообщениям, так и к сообщениям пользователей. (благодарит Джеффри).

Ответ 2

Да, клиентская сторона find() возвращает документы, которые находятся на клиенте в Minimongo. Из docs:

На клиенте создается экземпляр Minimongo. Minimongo по существу является встроенной памятью, непостоянной реализацией Mongo в чистом JavaScript. Он служит в качестве локального кеша, который хранит только подмножество базы данных, с которой работает этот клиент. Запросы на клиенте (поиск) подаются непосредственно из этого кеша, не разговаривая с сервером.

Как вы говорите, publish() указывает, какие документы будет иметь клиент.

Ответ 3

Основное правило большого пальца: publish и subscribed имена переменных должны быть одинаковыми на стороне клиента и сервера.

Имена коллекций на Mongo DB и стороне клиента должны быть одинаковыми.

Предположим, что я использую публикацию и подписку на мою коллекцию с именем employees, тогда код будет выглядеть как


сторона сервера

Здесь использование ключевого слова var является необязательным (используйте это ключевое слово, чтобы сделать коллекцию локальной для этого файла).

CollectionNameOnServerSide = new Mongo.Collection('employees');   

Meteor.publish('employeesPubSub', function() { 
    return CollectionNameOnServerSide.find({});     
});

клиентский файл .js

CollectionNameOnClientSide = new Mongo.Collection('employees');
var employeesData = Meteor.subscribe('employeesPubSub');

Template.templateName.helpers({
  'subcribedDataNotAvailable' : function(){
        return !employeesData.ready();
    },
   'employeeNumbers' : () =>{
       CollectionNameOnClientSide.find({'empId':1});
  }
});

клиентский файл .html

Здесь мы можем использовать вспомогательный метод subcribedDataNotAvailable, чтобы узнать, готовы ли данные на стороне клиента, если данные готовы, затем распечатайте номера сотрудников с помощью employeeNumbers вспомогательного метода.

<TEMPLATE name="templateName">
{{#if subcribedDataNotAvailable}}
   <h1> data loading ... </h1>
 {{else}}
  {{#each employeeNumbers }}
     {{this}}
  {{/each}}
 {{/if}}
<TEMPLATE>

Ответ 4

// on the server
Meteor.publish('posts', function() {

    return Posts.find();

});

// on the client
Meteor.subscribe('posts');