Meteor.Publish: Publish Collection Which Depends on Other Collection

Meteor.publish: publish collection which depends on other collection

Overview

As of this writing, reactive joins are an unsolved problem. For a complete overview see Reactive Joins In Meteor.

Recommendations

I strongly recommend against using observeChanges directly. It's incredibly hard to get right, and easy to develop a memory leak. If you don't believe me, watch this video on EventedMind. It will make your eyes bleed.

There are several package-based solutions to this problem. The meteor guide recommends publish-composite.

If you find the idea of using a package-based solution to be unacceptable, have a close look at the Joining On The Client section from Reactive Joins In Meteor. It's clean but requires more waiting on the user's part. Also see my post on template joins if you prefer to active your subscriptions at the template level.

Meteor: How to publish cursor that is depending on cursor of other collection

Solution

Lists = new Meteor.Collection('lists');

if (Meteor.isClient) {
Tracker.autorun(function() {
if (Meteor.userId()) {
Meteor.subscribe('lists');
Meteor.subscribe('myLists');
}
});
}

if (Meteor.isServer) {
Meteor.startup(function() {
if (Meteor.users.find().count() === 0) {
var user = {
username: 'test',
email: 'test@test.com',
password: 'test'
};

var userId = Accounts.createUser(user);
var listId = Lists.insert({data: 'content'});
Meteor.users.update(userId, {
$addToSet: {lists: listId}
});
}
});

Meteor.publish('lists', function() {
check(this.userId, String);
var lists = Meteor.users.findOne(this.userId).lists;
return Lists.find({_id: {$in: lists}});
});

Meteor.publish('myLists', function() {
check(this.userId, String);
return Meteor.users.find(this.userId, {fields: {lists: 1}});
});
}

Changes

  1. Declare the Lists collection outside of the client and server (no need to declare it twice).
  2. Ensure the user is logged in when subscribing. (performance enhancement).
  3. When inserting the test user, use the fact that all insert functions return an id (reduces code).
  4. Ensure the user is logged in when publishing.
  5. Simplified lists publish function.
  6. Fixed myLists publish function. A publish needs to return a cursor, an array of cursors, or a falsy value. You can't return an array of ids (which this code doesn't access anyway because you need to do a fetch or a findOne). Important note - this publishes another user document which has the lists field. On the client it will be merged with the existing user document, so only the logged in user will have lists. If you want all users to have the field on the client then I'd recommend just adding it to the user profiles.

Caution: As this is written, if additional list items are appended they will not be published because the lists publish function will only be rerun when the user logs in. To make this work properly, you will need a reactive join.

Publish documents in a collection to a meteor client depending on the existence of a specific document in another collection (publish-with-relations)

What you are looking for is a reactive join. You can accomplish this by directly using an observe in the publish function, or by using a library to do it for you. Meteor core is expected to have a join library at some point, but until then I'd recommend using publish-with-relations. Have a look at the docs, but I think the publish function you want looks something like this:

Meteor.publish('offersShared', function() {
return Meteor.publishWithRelations({
handle: this,
collection: ShareRelations,
filter: {receiverId: this.userId},
mappings: [{collection: Offers, key: 'offerId'}]
});
});

This should reactively publish all of the ShareRelations for the user, and all associated Offers. Hopefully publishing both won't be a problem.

PWR is a pretty legit package - several of us use it in production, and Tom Coleman contributes to it. The only thing I'll caution you about is that as of this writing, the current version in atmosphere (v0.1.5) has a bug which will result in a fairly serious memory leak. Until it gets bumped, see my blog post about how to run an updated local copy.

update 2/5/14:

The discover meteor blog has an excellent post on reactive joins which I highly recommend reading.

How to publish a view/transform of a collection in Meteor?

UPDATE You can transform a collection on the server like this:

Words = new Mongo.Collection("collection_name"); 

Meteor.publish("yourRecordSet", function() {

//Transform function
var transform = function(doc) {
doc.date = new Date();
return doc;
}

var self = this;

var observer = Words.find().observe({
added: function (document) {
self.added('collection_name', document._id, transform(document));
},
changed: function (newDocument, oldDocument) {
self.changed('collection_name', oldDocument._id, transform(newDocument));
},
removed: function (oldDocument) {
self.removed('collection_name', oldDocument._id);
}
});

self.onStop(function () {
observer.stop();
});

self.ready();

});

Publish a virtual collection in meteor

so I used publishVirtual function. thanks to @michel floyd

function publishVirtual(sub, name, cursor) {
var observer = cursor.observeChanges({
added : function(id, fields) { sub.added(name, id, fields) },
changed: function(id, fields) { sub.changed(name, id, fields) },
removed: function(id) { sub.remove(name, id) }
})

sub.onStop(function() {
observer.stop() // important. Otherwise, it keeps running forever
})
}

and added this into publish :

Meteor.publish('freeCourses', function () {
var cursor = Courses.find({}, {fields: {'Seasons.Episodes.paid_url': 0}});
publishVirtual(this, 'freeCourses', cursor);
this.ready();
});
Meteor.publish('premiumCourses', function () {
//userPremiumCourses contains array of course_ids
var userPremiumCourses = userCourses.find({'user_id': this.userId}, {fields: {course_id: 1, _id: 0}}).map(
function (doc) {
return doc.course_id;
}
);
var cursor = Courses.find({_id: {$in: userPremiumCourses}});
publishVirtual(this, 'premiumCourses', cursor);
this.ready();
});

and made two client-side collections for subscribe :

if (Meteor.isClient) {
freeCourses = new Mongo.Collection("freeCourses");
premiumCourses= new Mongo.Collection("premiumCourses");

Meteor.subscribe('freeCourses');
Meteor.subscribe('premiumCourses');
}

How to read a collection that depends on another one in Meteor

Server side code:

Meteor.publish("latestPost", function () {
var post = Posts.find({}, {sort:{created:-1}}).fetch()[0];
console.log("publish : " + post.title);
return [
Posts.find({_id: post._id}),
Comments.find({postId: post._id})
];
});

Client side code:

 this.route('home', {
path: '/',
template: 'home',
waitOn: function () {
return [
Meteor.subscribe('latestPost')
];
},
data:function(){
return {
post:Posts.findOne(),
comments:Comments.find()
};
}
});

Check this repository to see whole example.

After user changes to another route, then subcriptions are being automatically stopped.



Related Topics



Leave a reply



Submit