Meteor Group Collection by Field and their sum

2019-04-02 18:42发布

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.

2条回答
再贱就再见
2楼-- · 2019-04-02 19:00

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 } ]
查看更多
爷的心禁止访问
3楼-- · 2019-04-02 19:02

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 :)

查看更多
登录 后发表回答