Коллекции, публикации и подписки - сложная область Meteor, которую в документации можно было бы обсудить более подробно, чтобы избежать частой путаницы , которая иногда усугубляется путаницей терминологии. .
Вот Саша Грейф (соавтор DiscoverMeteor ), объясняющий публикации и подписки на одном слайде:
Чтобы правильно понять, почему вам нужно звонить find()
более одного раза, вам необходимо понять, как работают коллекции, публикации и подписки в Meteor:
Вы определяете коллекции в MongoDB. Метеор еще не задействован. Эти коллекции содержат запись базы данных (также называемые «документы» на обоих Монго и Метеор , но «документ» является более общим , чем записи базы данных, например, спецификация обновления или селектор запроса документы тоже - JavaScript объектов , содержащих field: value
пары).
Затем вы определяете коллекции на сервере Meteor с помощью
MyCollection = new Mongo.Collection('collection-name-in-mongo')
Эти коллекции содержат все данные из коллекций MongoDB, и вы можете запускать MyCollection.find({...})
их, что вернет курсор (набор записей с методами для их перебора и возврата).
Этот курсор (большую часть времени) используется для публикации (отправки) набора записей (называемого «набором записей» ). При желании вы можете опубликовать только некоторые поля из этих записей. Клиенты подписываются на наборы записей (а не на коллекции) . Публикация выполняется функцией публикации , которая вызывается каждый раз, когда новый клиент подписывается, и которая может принимать параметры для управления тем, какие записи возвращать (например, идентификатор пользователя, чтобы возвращать только документы этого пользователя).
На клиенте у вас есть коллекции Minimongo, которые частично отражают некоторые записи с сервера. «Частично», потому что они могут содержать только некоторые поля, и «некоторые из записей», потому что вы обычно хотите отправлять клиенту только те записи, которые ему нужны, чтобы ускорить загрузку страницы, и только те, которые ему нужны и имеют разрешение на доступ.
Minimongo - это, по сути, непостоянная реализация Mongo в памяти на чистом JavaScript. Он служит локальным кешем, в котором хранится только подмножество базы данных, с которой работает этот клиент. Запросы на клиенте (поиск) обслуживаются непосредственно из этого кеша, без взаимодействия с сервером.
Эти коллекции Minimongo изначально пусты. Они заполнены
Meteor.subscribe('record-set-name')
звонки. Обратите внимание, что параметр для подписки не является именем коллекции; это имя набора записей, который сервер использовал в 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 сообщений в блоге
Meteor.publish('posts-recent', function publishFunction() {
return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
}
Meteor.subscribe('posts-recent');
подписка на публикации текущего пользователя
Meteor.publish('posts-current-user', function publishFunction() {
return BlogPosts.find({author: this.userId}, {sort: {date: -1}, limit: 10});
}
Meteor.publish('posts-by-user', function publishFunction(who) {
return BlogPosts.find({authorId: who._id}, {sort: {date: -1}, limit: 10});
}
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});
Это вернет курсор ко всем документам / записям, которые клиент получил на данный момент, как к верхним сообщениям, так и к сообщениям пользователя. ( спасибо Джеффри ).
BlogPosts.find({})
на клиенте после подписки на обе публикации, т. Е. Он вернет курсор всех документов / записей, находящихся в данный момент на клиенте, как верхних сообщений, так и сообщений пользователя. Я видел другие вопросы по SO, где спрашивающий был смущен этим.BlogPosts
это не коллекция, это возвращенный объект, который имеет такие методы, как «вставить», «обновить» "... и т. д., а настоящая коллекция находится иposts
на клиенте, и на сервере.Да, find () на стороне клиента возвращает только те документы, которые находятся на клиенте в Minimongo. Из документов :
Как вы говорите, publish () указывает, какие документы будут у клиента.
источник
Основное правило большого пальца здесь
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>
источник
// on the server Meteor.publish('posts', function() { return Posts.find(); }); // on the client Meteor.subscribe('posts');
источник