0

I am following this answer on stackoverflow. In console, I get the correct aggregated sum.

Meteor.publish('hoursTotal', function() {
    var self = this;
    var pipeline = [
        {$group: {_id: "$userId", hours: {$sum: "$hours" }}}
    ];
    var result = Reports.aggregate(pipeline);

    _.each(result, function(result) {
        self.added("hoursTotalSum", result._id, {
            userId: result._id,
            hours: result.hours
        });
    });
    console.log(result);
    self.ready();
});

I've subscribed to the new collection created in client:

Meteor.subscribe('hoursTotalSum');

My template helper:

Template.statsBrief.helpers({
    hoursTotalSum: function() {
        var currentUserId = Meteor.userId();
        return Reports.find({userId: currentUserId});
    },
});

My template:

{{#each hoursTotalSum}} {{hours}} {{/each}}

Meteor console retunrs this:

[ { _id: 'F8ZEWeKMoRfXaEGNa', hours: 30 },
I20151221-09:57:09.097(0)?   { _id: 'ufALZHfhWyQ8pMkgD', hours: 85 } ]

Meteor templates return this:

50 20 15

Although the summation is happening right, and the backend console confirms that, I'm struggling to get the value into the template.

Community
  • 1
  • 1
KhoPhi
  • 9,660
  • 17
  • 77
  • 128

1 Answers1

1

I successfully use aggregates via the meteorhacks:aggregate package however I do it a bit differently from your example. Rather than debug your code I will show how I would achieve the same goal.

Note :The code below is in coffeescript

Write a Meteor Method

Create a server side Meteor Method to perform the aggregation. You do not need to publish your aggregation as all the processing happens on the server and the results are under your control via the $project step.

Meteor.methods 
    getHoursTotal: ->
        return null unless @userId
        check @userId, String
        pipeline = [
            {$group:{'_id': @userId, 'hours':{$sum:'$hours'}}},
            {$project: 
                _id:false, 
                userId:'$_id', 
                hours: '$hours', 
            }
        ]
        return reports.aggregate pipeline

In your code you were processing each item in the result. It looked like you were just wanting rename _id to userId and include the calculated hours field. I did this in the $project step instead.

Call the Meteor Method

Your code depends on the userId being available, so we only want to run it after the user has signed in. We can make this reactive using tracker.

Tracker.autorun ->
    return unless Meteor.userId()?
    Meteor.call 'getHoursTotal', (err, res) ->
        Session.set 'hoursTotal', res

Now the code will only tun if the Meteor.userId is not null and when it does run the hoursTotal will be stored in the Session ready for use.

Expose the Results

To expose the results nicely it is a good idea to put it on a Template helper.

Template.statsBrief.helpers
    hoursTotal: -> Session.get 'hoursTotal'

I think performing the aggregate and exposing the results in this way is simpler whilst also benefiting from being fully reactive.

biofractal
  • 18,963
  • 12
  • 70
  • 116
  • Your solution is working pretty well, reative et all, but I don't seem to still access in templates. I am able to `Session.get('hoursTotal')` in console in client, but in the templates, I'm struggling – KhoPhi Dec 21 '15 at 20:19
  • Okay, figured it out. the `res` object contains an index of `0`, thus I simply did `Session.set('hoursTotal', res[0]);` This your approach is more reactive and interesting. Although I could get the other approach to also work, I think I prefer this than the former – KhoPhi Dec 21 '15 at 20:39