Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

4 level subscription nesting in meteor

I am using meteor and this is my schema, each is a separate collection:

Courses has many lectures

Lectures have many questions

Questions have many answers

I want 1 page where I can display a given course's lectures, questions, and answers. I can display a course's lectures no problem but I have issues with displaying further nested items. I'd ideally like to have:

Lecture has courseId

Answer has lectureId (but not courseId)

Question has answerId (but not lectureId or courseId)

Is that wise or should I embed courseIds and lectureIds in all child components? This is my iron router, I tried to extend the same idea that worked with nesting lectures with questions but I hit a stumbling block with how to feed the subscriptions the lecturesId:

Router.route('/courses/:_id', {
    name: 'CoursePage',
    waitOn: function(){
        return [
            Meteor.subscribe('singleCourse', this.params._id),
            Meteor.subscribe('lectures', this.params._id),
            Meteor.subscribe('questions', this.params._id)
        ];
    },
    data: function() {
        return Courses.findOne(this.params._id);
    }
});

This is the subscriptions for the course page, again with my stumbling block of not really knowing how to feed in a lectureId:

Template.CoursePage.helpers({
    Lectures: function() {
        return Lectures.find({courseId: this._id});
    },
    Questions: function(lectureId) {
        return Questions.find({courseId: this._id, lectureId: lectureId});
    }
});

Can anyone recommend a good way to do this 4 level nesting for a single page? I think that I am missing something obvious but I can't quite find a good example with google searching.

Thanks!

like image 247
Coherent Avatar asked Dec 21 '25 03:12

Coherent


2 Answers

You can Publish Composite package for this. See the following sample code and edit as per your collection schemas,

Meteor.publishComposite('singleCourse', function (courseId) {
    return [{
        find: function() {
            return Courses.find({ id: courseId});
        }
    }, {
        find: function() {
            return Lectures.find({ courseId: courseId});
        },
        children: [{
            find: function(lecture) {
                return Questions.find({ lectureId: lecture.id });
            },
            children: [{
                find: function(question) {
                   return Answers.find({ questionId: question.id });
                }
            }]
        }}
    }]
});

Then in your router, you can simply make one subscription call,

Router.route('/courses/:_id', {
    name: 'CoursePage',
    waitOn: function(){
        return [
            Meteor.subscribe('singleCourse', this.params._id)
        ];
    },
    data: function() {
        return Courses.findOne(this.params._id);
    }
});

This is one of the best packages (if not the best) as of now to reactively publish set of related documents from different collections.

There are some known issues while doing these kind of reactive joins but for smaller datasets, this works without any problem.

Hope it helps.

like image 66
Kishor Avatar answered Dec 24 '25 10:12

Kishor


Mongo can support using aggregation. $lookup will let you connect and gather data between your collections like an SQL join.

Using this in meteor requires using an external mongo ($lookup is new as of Mongo 3.2, meteor's Mongo is still 2.6.7) and a package such as the meteorhacks:aggregate package. There are other packages that address this, as mentioned in the comments, aggregate is just what I've used; with it you call Courses.aggregate(...) per the mongo aggregation documentation to produce the data that you require.

In my use, I had a Meteor method defined that took filter parameters as arguments

'aggregateReport':function(filterPersonnel, filterCourse, filterQuarter){
return Personnel.aggregate([{$match: filterPersonnel}, {$unwind: "$courses"},
  {$lookup: {from: "courses", localField: "courses", foreignField: "_id", 
  as: "course_docs"}}, {$unwind: "$course_docs"}, {$match: filterCourse}, 
  {$match: filterQuarter}]);

The Personnel have: country, course date, lastname, fullname, ..., course #, course. (The ellipses covers non-relevant to the query). The above queries Personnel per the filter, spools it out to one record per course (this is a transcript type of view for many people in a program), then adds the information from Courses as course_docs to the returned Personnel, and then filters by course parameters and date parameters. code and dependencies were meteor 1.2; Feb 2016

like image 45
Greg Syme Avatar answered Dec 24 '25 10:12

Greg Syme



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!