Meteor: difference between names for collections, variables, publications, and subscriptions?
In the Discover Meteor examples, what's the diff between "posts" and "Posts"? Why is it that when we do an insert from the server we use "posts" but when querying from the browser we use "Posts"? Wouldn't the system be confused by the case differences?
I see the variable assignment for client Posts to the server posts in posts.js. Is it a conventional notation to capitalize client and use small caps for server?
Posts = new Meteor.Collection('posts')
Why does server/fixtures.js use "Posts"? I was under the assumption that we query "Posts" in the browser (client), and use "posts" in the server, like we did in meteor mongo. So why are we now using Posts in the server?
Let's distinguish between the different names you might have to deal with when programming Meteor:
-
Variable names, such as
Posts = new Meteor.Collection(...)
. These are used only so your code knows how to access this variable. Meteor doesn't know or care what it is, although the convention is to capitalize. -
Collection names, such as
new Meteor.Collection("posts")
. This maps to the name of a MongoDB collection (on the server) or a minimongo collection (on the client). -
Publication and subscription names, used in
Meteor.publish("foo", ...)
orMeteor.subscribe("foo")
. These have to match up for the client to subscribe to some data on the server.
There are two things you need to match up in the Meteor data model:
- Names of publications and their corresponding subscriptions
- (usually) Names of collections on the client and server, if using the default collection model
A subscription name needs to always match up with the name of a publication. However, the collections that are sent for a given subscription needn't have anything to do with the subscription name. In fact, one can send over multiple cursors in one publication or one collection over different publications or even multiple subscriptions per publication, which appear merged as one in the client. You can also have different collection names in the server and client; read on...
Let's review the different cases:
-
Simple subscription model. This is the one you usually see in straightforward Meteor demos.
On client and server,
Posts = new Meteor.Collection("posts");
On server only:
Meteor.publish("postsPub", function() { return Posts.find() });
On client only:
Meteor.subscribe("postsPub")
This synchronizes the
Posts
collection (which is namedposts
in the database) using the publication calledpostsPub
. -
Multiple collections in one publication. You can send multiple cursors over for a single publication, using an array.
On client and server:
Posts = new Meteor.Collection("posts"); Comments = new Meteor.Collection("comments");
On server only:
Meteor.publish("postsAndComments", function() { return [ Posts.find(), Comments.find() ]; });
On client only:
Meteor.subscribe("postsAndComments");
This synchronizes the
Posts
collection as well as theComments
collection using a single publication calledpostsAndComments
. This type of publication is well-suited for relational data; for example, where you might want to publish only certain posts and the comments associated only with those posts. See a package that can build these cursors automatically. -
Multiple publications for a single collection. You can use multiple publications to send different slices of data for a single collection which are merged by Meteor automatically.
On server and client:
Posts = new Meteor.Collection("posts");
On server only:
Meteor.publish("top10Posts", function() { return Posts.find({}, { sort: {comments: -1}, limit: 10 }); }); Meteor.publish("newest10Posts", function() { return Posts.find({}, { sort: {timestamp: -1}, limit: 10 }); });
On client only:
Meteor.subscribe("top10Posts"); Meteor.subscribe("newest10Posts");
This pushes both the 10 posts with the most comments as well as the 10 newest posts on the site to the user, which sees both sets of data merged into a single
Posts
collection. If one of the newest posts is also a post with the most comments or vice versa, thePosts
collection will contain less than 20 items. This is an example of how the data model in Meteor allows you to do powerful data merging operations without implementing the details yourself. -
Multiple subscriptions per publication. You can get multiple sets of data from the same publication using different arguments.
On server and client:
Posts = new Meteor.Collection("posts");
On server only:
Meteor.publish("postsByUser", function(user) { return Posts.find({ userId: user }); });
On client only:
Meteor.subscribe("postsByUser", "fooUser"); Meteor.subscribe("postsByUser", "barUser");
This causes the posts by
fooUser
andbarUser
to both show up in theposts
collection. This model is convenient when you have several different computations that are looking at different slices of your data and may be updated dynamically. Note that when you subscribe inside aDeps.autorun(...)
, Meteor callsstop()
on any previous subscription handle with the same name automatically, but if you are using these subscriptions outside of anautorun
you will need to stop them yourself. As of right now, you can't do two subscriptions with the same name inside anautorun
computation, because Meteor can't tell them apart. -
Pushing arbitrary data over a publication. You can completely customize publications to not require the same collection names on the server and client. In fact, the server can publish data that isn't backed by a collection at all. To do this, you can use the API for the publish functions.
On server only:
Posts = new Meteor.Collection("posts"); Meteor.publish("newPostsPub", function() { var sub = this; var subHandle = null; subHandle = Posts.find({}, { sort: {timestamp: -1}, limit: 10 }) .observeChanges({ added: function(id, fields) { sub.added("newposts", id, fields); }, changed: function(id, fields) { sub.changed("newposts", id, fields); }, removed: function(id) { sub.removed("newposts", id); } }); sub.ready(); sub.onStop(function() { subHandle.stop(); }) });
On client only:
NewPosts = new Meteor.Collection("newposts"); Meteor.subscribe("newPostsPub");
This synchronizes the newest 10 posts from the
Posts
collection on the server (calledposts
in the database) to theNewPosts
collection on the client (callednewposts
in minimongo) using the publication/subscription callednewPostsPub
. Note thatobserveChanges
differs fromobserve
, which can do a bunch of other things.The code seems complicated, but when you return a cursor inside a publish function, this is basically the code that Meteor is generating behind the scenes. Writing publications this way gives you a lot more control over what is and isn't sent to the client. Be careful though, as you must manually turn off
observe
handles and mark when the subscription is ready. For more information, see Matt Debergalis' description of this process (however, that post is out of date). Of course, you can combine this with the other pieces above to potentially get very nuanced and complicated publications.
Sorry for the essay :-) but many people get confused about this and I though it would be useful to describe all the cases.
You decide the naming conventions, and meteor doesn't care.
Posts becomes a collection of documents from the mongo server. You find posts by calling Posts.find({author: 'jim}). In the example you wrote, meteor is being told to internally call that collection 'posts'. Hopefully this is easy to remember if the names are similar...
There needs to be a way to express and track what info is available to clients. Sometimes there may be multiple sets of information, of varying detail. Example: a summary for a title listing, but detail for a particular document. These are often also named 'posts' so it can be initially confusing:
Meteor.publish "posts", -> # on server
Posts.find()
and then
dbs.subscriptions.posts = Meteor.subscribe 'posts' # on client
publication and subscription names must match, but it could all be named like this:
PostsDB = new Meteor.Collection('postdocumentsonserver')
so in mongo you'd need to type
db.postdocumentsonserver.find()
but otherwise you never need to care about 'postdocumentsonserver'. Then
Meteor.publish "post_titles", ->
PostsDB.find({},{fields:{name:1}})
matching
dbs.subscriptions.post_titles = Meteor.subscribe 'post_titles'