Algo for sorting/compiling (and summing) an array of objects

I couldn’t find an answer for this on stackoverflow although I’m sure it’s there. I need to create a chart showing summed values by the month (think summing sales totals where you have multiple orders in a given month). I need to be aware of the years too (e.g. I don’t want to sum orders/sales for december 2017 and december 2016).

I’m pulling in objects that look like the below schema:

import { Mongo } from 'meteor/mongo';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';

export const Orders = new Mongo.Collection('Orders');


Orders.allow({
  insert: () => false,
  update: () => false,
  remove: () => false,
});

Orders.deny({
  insert: () => true,
  update: () => true,
  remove: () => true,
});

Orders.schema = new SimpleSchema({
  amount: {
    type: String,
    optional: true
  },
  orderDate: {
    type: Date,
    optional: true
  },
});

If I have two order records for december 2016, one for $1 and another for $2, I need something like this:

[
{ total: 3, month: ‘December’, year: ‘2016’ },
{ total: 1, month: ‘January’, year: ‘2017’ },
{ total: 5, month: ‘February’, year: ‘2017’ }
]

The MongoDB Aggregation Pipeline is your best friend for this sort of thing: it’s a sequence of operations, defined as objects in an array. Each object contains one pipeline operation and the array is processed in sequence with documents being “piped” out of one operation and into the next. Sometimes, more documents come out of an operation than went in ($unwind is an example), sometimes less - it depends on the function each step performs.

Your basic pipeline would be something like this:

[
  {
    $match: {},
    $sort: { orderDate: 1 }
  },
  {
    $group: {
      _id: {
        year: { $year: '$orderDate' },
        month: { $month: '$orderDate' },
        total: { $sum: '$amount' }
      }
    }
]

In Meteor, you would do this on the server using either jcbernack:reactive-aggregate if you want reactive publication, or natively using myCollection.rawCollection().aggregate(). The easiest way to handle the latter is to use the Promise (no callback) form of that and async/await in your Meteor code.

I’m curious as to why your amount is defined as a string.

1 Like