Reputation: 53521
In a Meteor application I am currently working on, some collections may have multiple simultaneous subscriptions (i.e. one to fetch initially, another when searching more data, etc.) Naturally, I do not want to have the entire collection downloaded to the client, so I'm relying on subscriptions manager and a need-to-have approach.
The problem is that, once the data is fetched, the collection on the client contains the entire content of all subscriptions, and I am not sure how I can discern which data was fetch from what subscription (i.e. which data was fetched initially, and which was searched, etc.)
I have thought adding a subscription UID to each fetched data (an array, since some data can be fetched from multiple subscriptions), but this seems hacky and prone to cleanup hell.
Is there a solution to associate and identify which subscription fetched which data?
So fare, this answer seem to solve part of the problem, however this is not the kind of solution I find "clean".
// ctx being the Metheor.subscribe context (i.e. const ctx = this;)
function transform(doc) {
if (searchKey) {
doc._searchKey = searchKey;
}
return doc;
}
const cursor = InventoryItems.find(filter, options);
const observer = cursor.observe({
added(doc) {
ctx.added(InventoryItems._name, doc._id, transform(doc));
},
changed(newDoc, oldDoc) {
ctx.changed(InventoryItems._name, newDoc._id, transform(newDoc));
},
removed(oldDoc) {
ctx.removed(InventoryItems._name, oldDoc._id);
}
});
ctx.onStop(() => {
observer.stop();
});
return cursor;
Upvotes: 0
Views: 141
Reputation: 16478
This answer describes a way to publish data to a client-only collection.
If you want to separate some of the data, you can publish it to a different collection:
//client
const TempCollection = new Meteor.Collection('temp', {defineMutationMethods: false});
Meteor.subscribe('temp.query', 'queryParams');
This defines a collection without setting mutation methods, so any changes done to it on the client are not sent to the server (as it would be pointless). Note that the collection name (temp
in this case) should be used in the server publication.
// server
const ActualCollection = new Mongo.Collection('real_collection_name');
function publishForTarget(sub, cursor, targetName) {
return Mongo.Collection._publishCursor(cursor, sub, targetName);
}
Meteor.publish('temp.query', function(/*params*/) {
let query = {/*...*/};
let targetName = 'temp'; // or get it from the client for dynamically-named client-side collections.
let cursor = ActualCollection.find(query);
publishForTarget(this, cursor, targetName);
return this.ready();
});
This uses the undocumented Mongo.Collection._publishCursor
method, which is also used by the standard publication when cursor(s) are returned.
Its implementation:
Mongo.Collection._publishCursor = function (cursor, sub, collection) {
var observeHandle = cursor.observeChanges({
added: function (id, fields) {
sub.added(collection, id, fields);
},
changed: function (id, fields) {
sub.changed(collection, id, fields);
},
removed: function (id) {
sub.removed(collection, id);
}
});
// We don't call sub.ready() here: it gets called in livedata_server, after
// possibly calling _publishCursor on multiple returned cursors.
// register stop callback (expects lambda w/ no args).
sub.onStop(function () {observeHandle.stop();});
// return the observeHandle in case it needs to be stopped early
return observeHandle;
};
It observes changes to the cursor and calls subscription life-cycle methods (which translate to DDP mutation messages) when things change. We are replacing the real collection name by the targetName
when using it.
It means that all of the results will be published to the 'temp'
collection on the client and will be separate from any other data that are queried normally and are published to 'real_collection_name'
.
Upvotes: 2