Get sum of the collection


#1

I have a collection name transactions with this schema

Schema.Transactions = new SimpleSchema({
	/* other fields */
	payment: {
		type: Number,
		label: 'Payment amount',
		optional: true
	},
	deposit: {
		type: Number,
		label: 'Deposit amount',
		optional: true,
	},
	/* other fields */
});

Now I want to subscribe to the total balance of the collection using the formula total deposit - total payment. How can I achieve this without aggregation (since I heard meteor doesnt support reactive aggregation yet)? Also this schema can contain tens to hundreds of thousands of data.

I need enlightment. Thanks guys!


#2

True - there is no reactive aggregation. However, you can aggregate server-side within a method. For example doctorpangloss:mongodb-server-aggregation.


#3

However, I need to make the result reactive. Looping on a huge amount of collection is resource overkill.


#4

Save the aggregated data in a separate collection and update as new items are added/updated/deleted?


#5

That would make sense but then I could have just created another collection that increments/decrements the balance on transaction creation/update. But thanks tho. Appreciate the help :smile:


#6

The example from official docs is a reactive sum solution which does not loop nor persists:

// server: publish the current size of a collection
Meteor.publish("counts-by-room", function (roomId) {
  var self = this;
  check(roomId, String);
  var count = 0;
  var initializing = true;

  // observeChanges only returns after the initial `added` callbacks
  // have run. Until then, we don't want to send a lot of
  // `self.changed()` messages - hence tracking the
  // `initializing` state.
  var handle = Messages.find({roomId: roomId}).observeChanges({
    added: function (id) {
      count++;
      if (!initializing)
        self.changed("counts", roomId, {count: count});
    },
    removed: function (id) {
      count--;
      self.changed("counts", roomId, {count: count});
    }
    // don't care about changed
  });

  // Instead, we'll send one `self.added()` message right after
  // observeChanges has returned, and mark the subscription as
  // ready.
  initializing = false;
  self.added("counts", roomId, {count: count});
  self.ready();

  // Stop observing the cursor when client unsubs.
  // Stopping a subscription automatically takes
  // care of sending the client any removed messages.
  self.onStop(function () {
    handle.stop();
  });
});

// client: declare collection to hold count object
Counts = new Mongo.Collection("counts");

// client: subscribe to the count for the current room
Tracker.autorun(function () {
  Meteor.subscribe("counts-by-room", Session.get("roomId"));
});

// client: use the new collection
console.log("Current room has " +
            Counts.findOne(Session.get("roomId")).count +
            " messages.");

// server: sometimes publish a query, sometimes publish nothing
Meteor.publish("secretData", function () {
  if (this.userId === 'superuser') {
    return SecretData.find();
  } else {
    // Declare that no data is being published. If you leave this line
    // out, Meteor will never consider the subscription ready because
    // it thinks you're using the added/changed/removed interface where
    // you have to explicitly call this.ready().
    return [];
  }
});

What it does is calculate an initial sum/count/whatever aggregate you want, and then update as documents get added/removed/updated.

The only downside to this would be the initialization time but if you want real-time accurate data and you are already set on aggregation, this is much more efficient with continuous updates.