EDIT: this question, some of the answers, and some of the comments, contain a lot of misinformation. See how Meteor collections, publications and subscriptions work for an accurate understanding of publishing and subscribing to multiple subsets of the same server collection.
How does one go about publishing different subsets (or "views") of a single collection on the server as multiple collections on the client?
Here is some pseudo-code to help illustrate my question:
items
collection on the serverAssume that I have an items
collection on the server with millions of records. Let's also assume that:
enabled
property set to true
, and;processed
property set to true
.All others are set to false
.
items:
{
"_id": "uniqueid1",
"title": "item #1",
"enabled": false,
"processed": false
},
{
"_id": "uniqueid2",
"title": "item #2",
"enabled": false,
"processed": true
},
...
{
"_id": "uniqueid458734958",
"title": "item #458734958",
"enabled": true,
"processed": true
}
Let's publish two "views" of the same server collection. One will send down a cursor with 50 records, and the other will send down a cursor with 100 records. There are over 458 million records in this fictitious server-side database, and the client does not need to know about all of those (in fact, sending them all down would probably take several hours in this example):
var Items = new Meteor.Collection("items");
Meteor.publish("enabled_items", function () {
// Only 50 "Items" have enabled set to true
return Items.find({enabled: true});
});
Meteor.publish("processed_items", function () {
// Only 100 "Items" have processed set to true
return Items.find({processed: true});
});
In order to support the latency compensation technique, we are forced to declare a single collection Items
on the client. It should become apparent where the flaw is: how does one differentiate between Items
for enabled_items
and Items
for processed_items
?
var Items = new Meteor.Collection("items");
Meteor.subscribe("enabled_items", function () {
// This will output 50, fine
console.log(Items.find().count());
});
Meteor.subscribe("processed_items", function () {
// This will also output 50, since we have no choice but to use
// the same "Items" collection.
console.log(Items.find().count());
});
My current solution involves monkey-patching _publishCursor to allow the subscription name to be used instead of the collection name. But that won't do any latency compensation. Every write has to round-trip to the server:
// On the client:
var EnabledItems = new Meteor.Collection("enabled_items");
var ProcessedItems = new Meteor.Collection("processed_items");
With the monkey-patch in place, this will work. But go into Offline mode and changes won't appear on the client right away -- we'll need to be connected to the server to see changes.
What's the correct approach?
What it comes down to is that I misunderstood the publish-subscribe relationship. I thought that when you published a cursor, it would land on the client as a separate collection from other published cursors that originated from the same server collection. This is simply not how it works. The idea is that both the client and server have the same collections, but it's what is in the collections that differs. The pub-sub contracts negotiate which documents end up on the client. Tom's answer is technically correct, but was missing a few details to turn my assumptions around. I answered a similar question to mine in another SO thread based on Tom's explanation, but keeping in mind my original misunderstanding of Meteor's pub-sub: Meteor publish/subscribe strategies for unique client-side collections
Hope this helps those who run across this thread and come away more confused than anything!
Could you not just use the same query client-side when you want to look at the items?
In a lib directory:
enabledItems = function() {
return Items.find({enabled: true});
}
processedItems = function() {
return Items.find({processed: true});
}
On the server:
Meteor.publish('enabled_items', function() {
return enabledItems();
});
Meteor.publish('processed_items', function() {
return processedItems();
});
On the client
Meteor.subscribe('enabled_items');
Meteor.subscribe('processed_items');
Template.enabledItems.items = function() {
return enabledItems();
};
Template.processedItems.items = function() {
return processedItems();
};
If you think about it, it is better this way as if you insert (locally) an item which is both enabled and processed, it can appear in both lists (a opposed to if you had two separate collections).
I realised I was kind of unclear, so I've expanded this a little, hope it helps.