[Solved] How to draw "Bar Chart" using HighCharts and MongoDB data reactively?

I learned another important lesson that instead of over complicating stuff, we NEED to stick to basics. Like, I almost bypassed power of UnderscoreJs over using aggregation function.

Just out of curiosity, Now we are playing with data on front-end. But what happens when there is huge set of docs matching to our query, will this approach will still render? (Considering meteor data caching ) Or here Aggregation is recommended?

This example by @jhuenges is no doubt beautiful one.

You should only ever publish the data you need, so for example your age distribution chart (as shown) only needs 10 data points published. If those 10 points need to come out of 1000000 documents in your collection, then you will clearly need to do something to aggregate your original data.

However, whether you need to use a ā€œformalā€ aggregation process (like MongoDBā€™s aggregation pipeline) is debatable in a real time interface. Certainly for the age distribution chart Iā€™d probably go with a separate collection which is updated as docs are added/modified/deleted from the main collection. Doing it like that means that you continue to use pub/sub and reactivity is maintained to your chart.

Yes, I do think the same way. Having separate collection consisting updated result of query, is reliable approach to follow when data is huge. Above exapmle by @jhuenges is clearly awesome upto medium sized data. But as data will grow limitations from front-end libraries and Meteor caching, will slowdown browser. But it no way suggests that above example wonā€™t work, itā€™s in fact limitation on client side due to memory/libraries.

I have to agree with @robfallows on this. Maybe you could use something like collection hooks to insert the data for the age distribution to a separate collection.

Hi @jhuenges,

I know this thread is old but I wonder if what is the best approach if you replace the age to date?

xAxis = Months (Jan, Febā€¦)

I have a similar requirement but the differences is I have to count the number of data in a given month:

ex.
dateFiled: Date - the date the enrollee claimed his insurance
Cause of Death: Cancer - reason of claiming
Medical: Accident - other reason of claiming

I like your approach that it didnā€™t need to use aggregation.

Curently, I have this aggregation on the server:

  casesCount: function () {
   var pipeline = [ {
     $project: {
     dateFiled: 1,
     causeOfDeath: 1,
     medical: 1
  }
  }, {
     $group: {
       _id: {
       dateFiled: "$fileDate",
       life: "$causeOfDeath",
       nonLife: "$medical"
    },
    count: {
      $sum: 1
     }
    }
   }
 ];
 var result = Claim.aggregate( pipeline );
 console.log( "Explain Report:", JSON.stringify( result ) );
  return result;
}

parts of my schema:

causeOfDeath: {
 type: String,
 label: "Life - Cause of Death",
 allowedValues: [
   'Cardiovascular',
   'Respiratory',
   'Renal failure',
   'Cancer',
  'Accident',
  'Diabetes',
  'Animal/Insect bite',
  'Liver illness',
  'others'
],
medical: {
 type: String,
 label: "Non-life - Medical",
  allowedValues: [
   'Animal/Insect bite',
   'Hypertension',
   'Diabetes',
   'CVA,Chronic Kidney disease',
   'Abration',
   'Hypersensitivity',
   'Community acquired pneumonia',
  'others'
],

I can see the result on console but Iā€™m having a hard time passing directly the result to render a chart. :frowning:

Iā€™ve been parked this feature for a while to find a suitable solution and work on the other parts of my app.

I came across this thread and I see that it is possible to use non-aggregation approach plus bonus, it is reactive :smile:

Hi @ajaxsoap,

I am would love to help. Just to understand you better: You already got the correct data on the client and you dont know how to create the chart, am I right? If that is the case, can you share some of the result data? If you like, you can also just create some fake data, but with the correct object structure :smile:

What type of chart do you want? Line, bar, ā€¦

I wont be able to answer in the next couple of hours - have to catch up on some sleep :stuck_out_tongue: But I am going to take another look tomorrow.

Cheers!

Hi @jhuenges,

Thanks a lot for taking to look!

Iā€™m trying to draw a column chart with this chart data:

Template.claimChart.onRendered( function () {
 
 Tracker.autorun( function () {
  var JanClaim = ( Claim.find( {
  dateFiled: {
    $gt: new Date( '01/01/2015' ),
    $lt: new Date( '02/01/2015' )
  }
} ).count() );
var FebClaim = ( Claim.find(  {
  dateFiled: {
    $gt: new Date( '02/01/2015' ),
    $lt: new Date( '03/01/2015' )
  }
} ).count() );
var MarClaim = ( Claim.find( {
  dateFiled: {
    $gt: new Date( '03/01/2015' ),
    $lt: new Date( '04/01/2015' )
  }
} ).count() );
var AprClaim = ( Claim.find( {
  dateFiled: {
    $gt: new Date( '04/01/2015' ),
    $lt: new Date( '05/01/2015' )
  }
} ).count() );
var MayClaim = ( Claim.find(  {
  dateFiled: {
    $gt: new Date( '05/01/2015' ),
    $lt: new Date( '06/01/2015' )
  }
} ).count() );
var JuneClaim = ( Claim.find(  {
  dateFiled: {
    $gt: new Date( '06/01/2015' ),
    $lt: new Date( '07/01/2015' )
  }
} ).count() );
var JulyClaim = ( Claim.find( {
  dateFiled: {
    $gt: new Date( '07/01/2015' ),
    $lt: new Date( '08/01/2015' )
  }
} ).count() );
var AugClaim = ( Claim.find( {
  dateFiled: {
    $gt: new Date( '08/01/2015' ),
    $lt: new Date( '09/01/2015' )
  }
} ).count() );
var SeptClaim = ( Claim.find(  {
  dateFiled: {
    $gt: new Date( '09/01/2015' ),
    $lt: new Date( '10/01/2015' )
  }
} ).count() );
var OctClaim = ( Claim.find( {
  dateFiled: {
    $gt: new Date( '10/01/2015' ),
    $lt: new Date( '11/01/2015' )
  }
} ).count() );
var NovClaim = ( Claim.find( {
  dateFiled: {
    $gt: new Date( '11/01/2015' ),
    $lt: new Date( '12/01/2015' )
  }
} ).count() );
var DecClaim = ( Claim.find( {
  dateFiled: {
    $gt: new Date( '12/01/2015' ),
    $lt: new Date( '01/30/2016' )
  }
} ).count() );
 function chartColumn() {

  $( '#claimChart' ).highcharts( {

    chart: {
      type: 'column'
    },

    title: {
      text: 'Monthly Total Cause of Death/Medical Claims'
    },

    // subtitle: {
    //   text: 'Source: WorldClimate.com'
    // },

    credits: {
      enabled: false
    },

    xAxis: {
      categories: [
        'Jan',
        'Feb',
        'Mar',
        'Apr',
        'May',
        'Jun',
        'Jul',
        'Aug',
        'Sep',
        'Oct',
        'Nov',
        'Dec'
      ]
    },

    yAxis: {
      min: 0,
      title: {
        text: 'Total Numbers'
      }
    },

    tooltip: {
      headerFormat: '<span style="font-size:10px">{point.key}</span><table>',
      pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' +
        '<td style="padding:0"><b>{point.y} </b></td></tr>',
      footerFormat: '</table>',
      shared: true,
      useHTML: true
    },

    plotOptions: {
      column: {
        pointPadding: 0.2,
        borderWidth: 0
      }
    },

    series: [ {
      name: 'Cardiovascular',
      data: [ JanClaim, FebClaim, MarClaim, AprClaim,
        MayClaim, JuneClaim,
        JulyClaim, AugClaim, SeptClaim, OctClaim,
        NovClaim,
        DecClaim
      ]

    }, {
      name: 'Respiratory',
      data: [ 83.6, 78.8, 98.5, 93.4, 106.0, 84.5, 105.0,
        104.3,
        91.2,
        83.5, 106.6, 92.3
      ]

    }, {
      name: 'Renal failure',
      data: [ 48.9, 38.8, 39.3, 41.4, 47.0, 48.3, 59.0,
        59.6,
        52.4,
        65.2, 59.3, 51.2
      ]

    }, {
      name: 'Cancer',
      data: [ 42.4, 33.2, 34.5, 39.7, 52.6, 75.5, 57.4,
        60.4,
        47.6,
        39.1, 46.8, 51.1
      ]

    }, {
      name: 'Accident',
      data: [ 42.4, 33.2, 50, 39.7, 52.6, 75.5, 54.4, 60.4,
        47.6,
        39.1,
        46.8, 51.1
      ]

    }, {
      name: 'Diabetes',
      data: [ 42.4, 33.2, 34.5, 39.7, 52.6, 75.5, 57.4,
        60.4,
        47.6,
        39.1, 46.8, 51.1
      ]

    }, {
      name: 'Animal/Insect bite',
      data: [ 42.4, 33.2, 34.5, 39.7, 52.6, 75.5, 60.4,
        60.4,
        47.6,
        40.1, 46.8, 51.1
      ]

    }, {
      name: 'Liver illness',
      data: [ 42.4, 33.2, 34.5, 39.7, 52.6, 75.5, 57.4,
        60.4,
        47.6,
        39.1, 46.8, 51.1
      ]

    }, {
      name: 'Hypertension',
      data: [ 42.4, 33.2, 34.5, 39.7, 52.6, 85.5, 57.4,
        60.4,
        47.6,
        39.1, 46.8, 51.1
      ]

    }, {
      name: 'CVA,Chronic Kidney disease',
      data: [ 42.4, 33.2, 34.5, 39.7, 52.6, 75.5, 57.4,
        60.4,
        47.6,
        39.1, 46.8, 51.1
      ]

    }, {
      name: 'Abration',
      data: [ 42.4, 33.2, 34.5, 39.7, 52.6, 75.5, 57.4,
        60.4,
        47.6,
        39.1, 46.8, 51.1
      ]

    }, {
      name: 'Others',
      data: [ 42.4, 33.2, 34.5, 39.7, 52.6, 75.5, 57.4,
        60.4,
        47.6,
        39.1, 46.8, 51.1
      ]

    } ]
  } );
}

chartColumn();
} );
} );
});

Youā€™ve noticed, I have this variable ā€œJanClaim, FebClaim, etcā€¦ā€, this is a working data that Iā€™ve wrapped in an autorun function to be reactive, the chart is actually populating with this data but itā€™s not the intended output, itā€™s more complicated as I stated above, which is to count the total number of cause of death or medical claims per month.

If you analyze the problem, all the causes will not be populated in a month. Thereā€™s a possibility of one or more fields are zero data in a month.

Iā€™ve seen your meteorpad sample and it still works with the latest meteor version.

Thanks Again!

Working on it! Havent forgotten you :slight_smile:

EDIT
Here you go

I am not 100% sure I did the right thing :stuck_out_tongue: I grouped the medical claims per claim/month. If that is what you want, I would not recommend displaying medical and death claims in one chart. It might be hard to differ between what is medical and what is death ā€¦

Wow! :+1:

Thanks a lot for this @jhuenges :smile:

Yeah, I agree on separating medical and death claims chart.

I wonder what is this line do? Particularly the {0: 0, 1: 0, ...}

_.each(medicalClaims, function(medicalClaim) {
      medicalClaimsForChart[medicalClaim] = { 0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0, 11: 0 };
    })

I will try your code and hopefully it will work on my project!

Thanks a lot!!! :grinning:

This is just an easy way to create an empty dataset. This is needed for displaying no bar in the chart, if there is no data (=0). The values 0-11 stand for the twelve month of the year

Ok, got it.

I have to modify your code a little bit to suit my apps needs.

:+1:

1 Like

Hi @jhuenges,

Iā€™ve tried your code and it seems that for future consideration, since iā€™m expecting to have a 10K documents, the code will not be efficient, right now, my app has 1K documents and it throws an error RangeError: Maximum call stack size exceeded :worried:.

Thanks again for sharing your expertise! I might go back to aggregate method.

I am not sure what the problem should be. This error message doesnt sound related to the way the data is aggregated. I have seen this message before :slight_smile: hard to find the source