Multiple unique aggregations using same pipeline


#1

I fear I may have included too many issues in one post, so slimming this question down. Really hoping someone can help as this is stopping me cold. Thank you.

This is what I am trying to accomplish.

  • On click, user inserts new document in Valuations collection
  • User then adds company tickers through input (array saved in valuationSelections field of Valuations document)

For each Valuation, I need to

  1. aggregate data across those companies from the Companies collection whose value for ticker is in valuationSelections. No user edits are made directly to Companies collection. valuationSelections can contain similar companies across instances
  2. observe Valuations collection for changes and update the aggregation

The code below returns the correct values on the server, however:

  1. Since $out will clean out the collection before adding the new results (which doesn’t help for multiple aggregations), how can I instead copy results to a document in the Valuation collection?
  2. I’ve tried to replicate @levid’s great publication example in this post to take advantage of the reactivity. Can I call a method within a publication, or vice versa?

Guidance on any part would be greatly appreciated.

packages

meteorhacks:aggregate

server/methods.js

Meteor.methods({
    valuationAggregate: function(valuationId, valuationSelections) {
        //Aggregate and publish average of company valuation data for a user-selected series of companies./
        //Selections are saved in Valuations collection for reference and data for aggregation comes from Companies collection.//
        check(valuationId, String);
        check(valuationSelections, Array);
        var pipelineSelections = [
            //Match documents in Companies collection where the 'ticker' value was selected by the user.//
            {$match: {ticker: {$in: valuationSelections}}},
            {
                $group: {
                    _id: null,
                    avgEvRevenueLtm: {$avg: {$divide: ["$capTable.enterpriseValue", "$financial.ltm.revenue"]}},
                    avgEvRevenueFy1: {$avg: {$divide: ["$capTable.enterpriseValue", "$financial.fy1.revenue"]}},
                    avgEvRevenueFy2: {$avg: {$divide: ["$capTable.enterpriseValue", "$financial.fy2.revenue"]}},
                    avgEvEbitdaLtm: {$avg: {$divide: ["$capTable.enterpriseValue", "$financial.ltm.ebitda"]}},
                    //more//
                }
            },
            {
                $project: {
                    _id: 0,
                    resultsId: {$literal: valuationId},
                    avgEvRevenueLtm: 1,
                    avgEvRevenueFy1: 1,
                    avgEvRevenueFy2: 1,
                    avgEvEbitdaLtm: 1,
                    //more//
                }
            }
        ];

        var results = Companies.aggregate(pipelineSelections);
        console.log(results);
        }
});

client/templates/valuationCalc.js

Template.ValuationCalc.events({
    'click .submit': function(e) {
        e.preventDefault();

        //Get id of Valuation to use in new collection of results.//
        var valuationId = this._id;
        console.log(valuationId);
        //Identify user selections//
        var valuationSelections = this.valuationSelections;
        console.log(valuationSelections);

        Meteor.call('valuationAggregate', valuationId, valuationSelections, function () {
        });
    }

});

Returns on the server

I20150924-15:44:51.902(-4)?     avgEvRevenueLtm: 3.988137239679733,
I20150924-15:44:51.902(-4)?     avgEvRevenueFy1: 3.8159564713187155,
I20150924-15:44:51.902(-4)?     avgEvRevenueFy2: 3.50111769838031,
I20150924-15:44:51.902(-4)?     avgEvEbitdaLtm: 11.176476895728268
//more//
I20150924-15:44:51.903(-4)?     valuationId: 'Qg4EwpfJ5uPXyxe62' } ]

#2

I was able to resolve this with the following… Needed to add the forEach to unwind the array in the same way as $out.

lib/collections

ValuationResults = new Mongo.Collection('valuationResults');

server/methods

    var results = Companies.aggregate(pipelineSelections);
    results.forEach(function(valuationResults) {
        ValuationResults.update({'result.valuationId': valuationId}, {result:valuationResults}, {upsert: true});
    });
    console.log(ValuationResults.find({'result.valuationId': valuationId}).fetch());
}
});