0

I've been searching for answer to this question but sadly I don't see that will answer my question. I have an expense collection that looks like this

 { subcategory: 'Category-1', amount: 320 },
 { subcategory: 'Category-2', amount: 148 },
 { subcategory: 'Category-1', amount: 500 },
 { subcategory: 'Category-3', amount: 672 },
 { subcategory: 'Category-3', amount: 196 },
 { subcategory: 'Category-1', amount: 298 },
... etc

and I wanted to show in my summary report through my table like this

     **Category Name**         **Amount**

     1. Category-1                 1118      
     2. Category-2                 148
     3. Category-3                 868

which I am then going to render these data on my table template

{{#each expensesentries}}
  <tr>
     <td>{{category}}</td>
     <td>{{formatAmount amount}}</td>
  </tr>
{{/each}} `

Thanks.

ralphie
  • 132
  • 1
  • 9

2 Answers2

2

You should take care, that your approved solution requires that all data is stored on the client. What does it mean?

If you have a collection with about 1000 or even more entries than you have to publish all these entries and store these entries on the client before you can do a group. That requires a lot of space and costs lot of performance. So if your collection must not be reactive than you should look for a server side group via aggregation. Check this stackoverflow-post:

Aggregation via meteor

EDIT: Basically the linked article shows everything you need. I hope you are already common with publish and subscribe?

Meteor.publish("expensesentries", function (args) {
    var sub = this;
    var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;

    var pipeline = [
        { $group: {
            _id: subcategory,
            count: { $sum: amount }
        }}
    ];

    db.collection("server_collection_name").aggregate(        
        pipeline,

        Meteor.bindEnvironment(
            function(err, result) {
                _.each(result, function(e) {
                  sub.added("client_collection_name", Random.id(), {
                    category: item._id,
                    amount: item.count
                  });
                });
                sub.ready();
            },
            function(error) {
                Meteor._debug( "Error doing aggregation: " + error);
            }
        )
    );
});

Check mongodb aggregation pipeline

If you use iron router than you should subscribe in there.

  waitOn: function () {
    return [Meteor.subscribe('expensesentries')];
  },

In your Template:

Template.myTemplate.helpers({
  expensesentries: function() {
    return client_collection_name.find();
  }
});

This query could also be definded in your iron-router data callback. Dont expect this to work out of the box. Read manuals of iron-router, mongodb aggregation and try to understand whats going on. Happy coding :)

Community
  • 1
  • 1
chaosbohne
  • 2,454
  • 2
  • 22
  • 27
  • 1
    I have seen the link you provided before I posted my question here. The problem is I am still new to programming and can't figure out how to make it work with my needs. Can you answer my question using that method they way @David Weldon does? – ralphie Jul 14 '14 at 07:48
  • Check my update. I cant deliver you a full example but maybe this is a good entry point for you. – chaosbohne Jul 14 '14 at 10:41
  • okay. it's clearer to me now. =) let my try integrate this to my code. – ralphie Jul 15 '14 at 01:21
  • still not working.`var sub = this; var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db; var pipeline = [ { $group: { _id: "$subcat", totalamount: {$sum: "$amount"} }}, ]; db.collection("gifts").aggregate( pipeline, Meteor.bindEnvironment( function(err,result) { _.each(result, function(e) { sub.added("Gifts", Random.id(), { subcat: e._id, amount: e.totalamount }); }); sub.ready(); },` – ralphie Jul 15 '14 at 04:39
  • Does your aggregate function returns a dataset? – chaosbohne Jul 16 '14 at 13:27
  • this is what it shows when i do web console Gifts.find(): `LocalCollection.Cursor {collection: LocalCollection, sorter: null, _selectorId: undefined, matcher: Minimongo.Matcher, skip: undefined…}` and if I I do Gifts.find().fetch().. it just show `[]` – ralphie Jul 17 '14 at 08:13
  • Make a console.log(result) in your serverside aggregate function to debug if you query returns something. – chaosbohne Jul 17 '14 at 08:28
  • console.log(result) actually gives the result.. this is my whole code for this matter [linl] (http://pastebin.com/Q7tV7gq9).. but in the client side I don't know why its not showing the same – ralphie Jul 17 '14 at 08:47
  • Cant find a fault. Nice idea to push a paste. I put in a paste for you of my working code. I didnt short it up (no time) but this code is working for me. Maybe you can find what is missing. http://pastebin.com/qTy8rVW8 – chaosbohne Jul 17 '14 at 09:11
  • got it working.. i forgot to define a new collection to pass on after the aggregation results. – ralphie Jul 21 '14 at 04:41
  • Yeah good job, but dont forget it is not reactive for now but maybe in future. – chaosbohne Jul 21 '14 at 06:54
0

There may be a more elegant way to do this, but give this a try:

Template.myTemplate.helpers({
  expensesentries: function() {
    var subs = {};
    Expenses.find().forEach(function(e) {
      if (subs[e.subcategory] == null)
        subs[e.subcategory] = 0;
      subs[e.subcategory] += e.amount;
    });

    var results = [];
    _.each(subs, function(value, key) {
      results.push({category: key, amount: value});
    });

    return results;
  }
});

The first part aggregates the amounts by category into a single object. So subs will look like:

{ 'Category-1': 1118, 'Category-2': 148, 'Category-3': 868 }

The second part builds an array (results) with the necessary key-value pairs for your template. When done, it should look like:

[ { category: 'Category-1', amount: 1118 },
  { category: 'Category-2', amount: 148 },
  { category: 'Category-3', amount: 868 } ]
David Weldon
  • 63,632
  • 11
  • 148
  • 146
  • can this be run on server during publish or on meteor.call command? – ralphie Jul 15 '14 at 06:49
  • Yes, that's recommended if you have a lot of data and you only need the aggregated version on the client. The easiest way is to turn it into a method - see [this question](http://stackoverflow.com/questions/22147813/how-to-use-meteor-methods-inside-of-a-template-helper) for more details. – David Weldon Jul 15 '14 at 17:07