First, some recommended reading:
Joins in meteor are currently tricky. It's easy to just join the collections in a publish function, but it isn't always straightforward to make them reactive (run again when things change).
Non-Reactive Options
You could publish both collections at the same time with:
Meteor.publish('projectsAndTasks', function() {
var projectsCursor = Projects.find({user_id: this.userId});
var projectIds = projectsCursor.map(function(p) { return p._id });
return [
projectsCursor,
Tasks.find({project_id: {$in: projectIds}});
];
});
The potential problem is that if tasks were added to a new project, they would not be published (see "The Naive Approach" from the first article above). Depending on how your application starts and stops its subscriptions, this may not matter. If you find that it does, keep reading.
Reactive Options
A simple option is just to denormalize the data. If you also added user_id
to your tasks, then no joins are necessary, and the publish function looks like:
Meteor.publish('projectsAndTasks', function() {
var projectsCursor = Projects.find({user_id: this.userId});
var tasksCursor = Tasks.find({user_id: this.userId});
return [projectsCursor, tasksCursor];
});
If that doesn't appeal to you and you are using iron-router, you can do a client-side join in your routes (see "Joining On The Client" from the first article above). It's a bit slower because you need a second round trip but it's clean in that no data needs to be modified and no external packages need to be added.
Finally, you can do a reactive join on the server, either manually using observeChanges (not recommended), or by using a package. I have used publish-with-relations in the past, but it has some issues as pointed out in the articles). For a more complete list of package options, you can see this thread.
Not being a core developer on meteor, I don't have a precise answer for why allow/deny doesn't have a "read" option, but I'll take an educated guess. Depending on how the allow/deny function was written, the publisher would potentially have to run an expensive callback for every single document or partial update. The allow/deny callbacks are easy to tolerate when a single document is being modified, but if you suddenly need to publish several hundred documents and each one needs to be separately evaluated before being transmitted, I don't think that would be practical. I'm pretty sure that's why publishers can act alone as the arbiter of document read authorization.