Javascript

Reactive Aggregation in Meteor

Jump to the next heading if you don’t wanna read any of my how-I-got-into-Meteor intro

Aggregation of data is such an important part of the software, and when there exist packages or helpers allowing one to make full use of the possible aggregation mechanisms, its such a breeze.

Django, for instance, has some pretty useful baked-in aggregation classes and functions, that make summation and averaging, for instance, a breeze.

For my next project, I needed a bit of reactivity, as in, changes happening on the server, immediately pushing to the client browser, without the user explicitly asking for, and vice versa.

Perhaps there is a way to do so in Django that I haven’t found yet. But, I wanted to move out of my comfort zone for a bit and try to learn a better way of getting such ‘reactivity’. Javascript seemed the real solution for real-time updates between server and client.

Looking for another serious database reactivity? Checkout of Firebase from Google.

Meteor seems to be ruling the Javascript Full-stack Framework Stadium full of spectators, just as Django overlords the Jungle of Python Full Stack Web Frameworks. The Meteor ruling, I’m not so sure about, but the Django part, I’m certain.

With the extensive support behind Meteor, and the pretty relatively extensive documentation behind it, with hundreds of packages available for use on Atmosphere (who gave it that name? ;p), I felt it was a good choice.

I jumped into Meteor, and within a week, the stars were falling in-line (instead of Meteors)

“Stars falling inline” reminds me of “Under the Dome”. Deserves cancelling!

Although I quickly got up to speed, because of a bit of experience in javascript over the years and my passion for having a feel of how to be reactive, it wasn’t for long as I realized, something was missing the party.

Aggregation Unreactive? Cause the Enthalpy change!

By default, out of the box, Meteor doesn’t support Aggregation, and so you will need a package for that. The package is called Meteorhacks Meteor Aggregate. It is such a brilliant package until you read:

“this only works on server side and there is no oberserving support or reactivity built in”

Duh!

In case you don’t understand what that means, Meteor Collections (start thinking Meteoric now. Collection is, in other words, Database) are reactive, unlike traditional Databases. Therefore, when the changes are made to the database portion on the server, the change automatically is pushed down to the client and reflects. That concept is what makes Meteor amazing in a way, as there’s little clicks, as the changes are pushed down and up seamlessly.

The Meteorhacks Meteor Aggregate doesn’t reactively aggregate, as in, whenever changes happen on the Collection the aggregation is run on, they aren’t automatically pushed to the client to reflect changes. To get this reactivity, you might have to open up the tubes at the bottom of the sink, or use the remote control on your table that’ll do the fixing for you.

I choose to use the remote control to fix the sink leakage instead. It’s simple: install another package that adds the Reactivity, called Meteor Reactive Aggregate.

Its code time

“A blog about code without some code is like an Icecream without a flavor.” – Two Scoops of Meteor (don’t google that)

So let’s add some flavor. The code below is found in my Meteor project I said earlier that I am working on. All the heavy lifting have been done by great minds like JcBernack and Meteor hacks, plus help in setting them up.

In the part below, I’m gonna share how I’m using the two in tandem in my project for aggregation, specifically, returning the sum of numbers from a specific column in all rows for a particular user.

Meteor believes in reactivity, and something else called, ‘Publish and Subscribe’. Sounded crazy at first for me, but made sense later on. So, we’ll be adding the reactivity to our Publish method on the fly.

// server/publication.js
// The name of the publish and the function to run
Meteor.publish("reportTotals", function() {
    // This does this: Run aggregation of the fields below on the Reports
    // Collection...
    ReactiveAggregate(this, Reports, [{
        $group: {
            '_id': this.userId,
            'hours': {
                $sum: '$hours'
            },
            'magazines': {
                $sum: '$magazines'
            },
            'brochures': {
                $sum: '$brochures'
            },
            'books': {
                $sum: 'books'
            }
        }
    }, {
        // ... and return them in an iterable form as this:
        $project: {
            hours: '$hours',
            magazines: '$magazines',
            brochures: '$brochures',
            books: '$books'
        }
    }], { 
        // and send the results to another collection called below
        clientCollection: "clientReport" 
    });
});

I have four fields; hours, magazines, brochures, and books, in my Reports collection I created as

// lib/collections/reports.js
Reports = new Meteor.Collection('reports');

// the Above creates a new Meteor collection called 'reports' 
// and adds that into the global variable 'Reports'

This part ain’t necessary but worth knowing, as in how the Reports collection schema looks like.

Collections in Meteor schema type are kinda created on the fly. Its like creating a Database in Django, and not saying ANYTHING as to what type of data will be expected and where they’ll be put. All you do is simply create the table, and start inserting, updating things. No schema, nothing! Kinda weird

// lib/collections/reports.js
// If you use Autoform from aldeed, then creating 
// a schema is necessary
Reports.attachSchema(new SimpleSchema({
    hours: {
        type: Number,
        label: "Number of hours",
    },
    magazines: {
        type: Number,
        label: "Magazines",
        optional: true
    },
    brochures: {
        type: Number,
        label: "Brochures",
        optional: true
    },
    books: {
        type: Number,
        label: "Books",
        optional: true
    },
    "userId": {
        type: String,
        autoform: {
            type: "hidden",
        }
    },
}));

I only added the above snippet to give you and idea how my schema looks like.

That out of the way, the next step was to subscribe to what we published. Without subscribing, everything we’ve done so far is useless. We need to explicitly tell Meteor what we want in client and how we want it. The word is, Explicitly!

 

[wp_ad_camp_1]

 

So, subscribe:

// lib/router.js
// wrapping mine in an autorun because I'm using GroundDB
if (Meteor.isClient) {
  subscribed = false;
  Tracker.autorun(function() {
    if (Meteor.user() && !subscribed) {
      ....
      // Subscribing to the name we gave the publish above
      Meteor.subscribe('reportTotals');
      return subscribed = true;
    }
  });
}

“Can I see the aggregation in templates yet?”. Sorry, you can’t. The moving parts in Meteor are that many that, for a simple object from a database, it might pass through many stops before actually reaching the end point, the template for example.

We need to take the aggregated value that was added to our ‘clientReport’ collection when the ‘reportsTotal’ publish function was executed, and inject it into our template view, using a little Template helper from Meteor.

// client/views/stats/stats.js
Template.statsBrief.helpers({
    // Create a reportTotals function
    // we're on client, don't get confused. No function clashes)
    // and return something, in this case ...
    reportTotals: function() {
        // console.log('success');
        // ... the aggregated values added to our client-side
        // collection
        return clientReport.find();
    },
});


//client/views/stats/stats.html
// part truncated for brevity.
....
<li class="collection-item">
    Current hours 
    <span class="badge">
        <!-- Iterate through the reportTotals function return
             which is a cursor from clientReport collection -->
        {{#each reportTotals}}
            {{hours}}
        {{/each}}
    </span>
</li>
...

 

Conclusion

So enjoy reactive aggregation in Meteor. I hope to add averaging and other aggregation tips later on.

 

Related Articles

Check Also
Close
Back to top button