Understanding Meteor Publication / Subscription

I have a simple application that shows a list of Projects . I uninstalled the autopublish package autopublish that I would not send everything to the client.

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

When autopublish was turned on, it would display all projects:

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

With its removal, I must additionally perform:

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

So, is it right to say that the find() method on the client side only searches for records that have been published on the server side? This baffled me because I felt that I only needed to call find() once.

+80
javascript mongodb meteor publish-subscribe
Nov 07 '13 at 2:19
source share
4 answers

Collections, publications, and subscriptions are a complex area of ​​Meteor that documentation can be discussed in more detail to avoid frequent ones that are sometimes amplified by confusing terminology .

Here's Sacha Greif (co-author of DiscoverMeteor ), explaining publications and subscriptions on one slide:

subscriptions

To correctly understand why you need to call find() more than once, you need to understand how collections, publications and subscriptions work in Meteor:

  • You define collections in MongoDB. The meteor is not yet involved. These collections contain database records (also called “documents” like Mongo and Meteor , but a “document” is more general than a database record, for example, an update specification or a query selector — these are documents — JavaScript objects containing field: value pairs).

  • Then you define collections on the Meteor server with

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

    These collections contain all the data from MongoDB collections, and you can run MyCollection.find({...}) on them, which will return a cursor (a set of records, using methods to repeat them and return them).

  • This cursor (most of the time) is used to publish a set of records (called a "set of records ). You can optionally publish only a few fields from these records. This is a set of records (not collections) in which subscribe clients. Publishing publication of a function that is called every time a client subscribes, and which can take parameters for managing returned records (for example, a user identifier to return only those user documents).

  • On the client, you have Minimongo collections that partially reflect some records from the server. Partially because they can contain only some of the fields and some records, because you usually want to send the client only the records he needs, speed up the page loading and only those that she needs, and have permission to access.

    Minimongo is essentially an in-memory, non-persistent implementation of Mongo in pure JavaScript. It serves as a local cache that only stores a subset of the database this client is working with. Requests on the client (search) are submitted directly from this cache without talking to the server.

    These Minimongo collections are initially empty. They are filled

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

    challenges. Note that the subscribe parameter is not a collection name; This is the name of the recordset that the server used in the publish call. The subscribe() call subscribe() client to a set of entries — a subset of the entries in the server’s collection (for example, the most recent 100 blog posts) with all or a subset of the fields in each entry (for example, only title and date ). How does Minimongo know which collection to place incoming records in? The collection name will be the collection argument used in the added , changed and removed publication handlers of the callbacks, or if they are absent (most of the time), this will be the name of the MongoDB collection on the server.

Editing Records

Here Meteor makes things very convenient: when you change the record (document) in the Minimongo collection on the client, Meteor instantly updates all the templates that depend on it, and also sends the changes back to the server, which, in turn, saves the changes to MongoDB and will send them to relevant customers who have subscribed to a set of records, including this document. This is called delay compensation and is one of Meteor ’s seven core principles .

Several signatures

You can have many subscribers who go for different records, but they all fall into the same collection on the client if they come from the same collection on the server based on their _id . This is not explained clearly, but is implied by Meteor docs:

When you subscribe to a recordset, it tells the server to send records to the client. The client stores these records in local Minimongo collections with the same name as the collection argument used in the publication handler added , changed and removed callbacks. Meteor will queue incoming attributes until you declare Mongo.Collection on the client with the corresponding collection name.

What isn’t explained is what happens when you don’t explicitly use added , changed and removed , or don’t publish handlers at all - this is most of the time. In this most common case, the collection argument (not surprisingly) is taken from the name of the MongoDB collection that you declared on the server in step 1. But this means that you can have different publications and subscriptions with different names, and all entries will fall into the same collection on the client. To the level of top- level fields, Meteor will take care of combining between documents, so that subscriptions can overlap - publish functions that send different top-level fields for the client to work side by side and on the client, the document in the collection will combine two sets of fields .

Example: multiple signatures filling in the same collection on the client

You have a BlogPosts collection that you advertise the same on the server and on the client, even if it does different things:

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

On a client, BlogPosts can receive posts from:

  • Subscribe to the most recent 10 blog posts

     // server Meteor.publish('posts-recent', function publishFunction() { return BlogPosts.find({}, {sort: {date: -1}, limit: 10}); } // client Meteor.subscribe('posts-recent'); 
  • subscription to current user messages

     // 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); 
  • subscription to the most popular posts

  • and etc.

All of these documents come from the posts collection in MongoDB through the BlogPosts collection on the server and end in the BlogPosts collection on the client.

Now we can understand why you need to call find() more than once - the second time on the client, because documents from all signatures will fall into the same collection, and you will need to select only those that are of interest to you, For example, to receive the latest messages on the client, you simply mirror the request from the server:

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

This will return the cursor to all documents / records that the client has received so far, both to the top messages and to the messages of users. ( thanks Jeffrey ).

+270
Feb 18 '14 at 12:07
source share

Yes, the client side find () returns documents that are on the client in Minimongo. From docs :

An instance of Minimongo is created on the client. Minimongo is essentially built-in memory, a mutable implementation of Mongo in pure JavaScript. It serves as a local cache that only stores a subset of the database this client is working with. Requests on the client (search) are submitted directly from this cache without talking to the server.

As you say, publish () indicates which documents the client will have.

+26
Nov 07 '13 at 2:28
source share

The basic rule of thumb: publish and subscribed variable names must be the same on the client and server side.

Collection names on Mongo DB and client side must be the same.

Suppose I use publish and subscribe to my collection called employees , then the code will look like




server side

Here, the use of the var keyword is optional (use this keyword to make the collection local to this file).

 CollectionNameOnServerSide = new Mongo.Collection('employees'); Meteor.publish('employeesPubSub', function() { return CollectionNameOnServerSide.find({}); }); 



client .js file

 CollectionNameOnClientSide = new Mongo.Collection('employees'); var employeesData = Meteor.subscribe('employeesPubSub'); Template.templateName.helpers({ 'subcribedDataNotAvailable' : function(){ return !employeesData.ready(); }, 'employeeNumbers' : () =>{ CollectionNameOnClientSide.find({'empId':1}); } }); 



client .html file

Here we can use the subcribedDataNotAvailable helper method to find out if the client-side data is ready, if the data is ready, then print the employee numbers using the employeeNumbers helper method.

 <TEMPLATE name="templateName"> {{#if subcribedDataNotAvailable}} <h1> data loading ... </h1> {{else}} {{#each employeeNumbers }} {{this}} {{/each}} {{/if}} <TEMPLATE> 
+1
Feb 27 '17 at 9:47 on
source share
 // on the server Meteor.publish('posts', function() { return Posts.find(); }); // on the client Meteor.subscribe('posts'); 
0
Jan 27 '17 at 13:59 on
source share



All Articles