Search code examples
javascriptchartsgoogle-visualizationgoogle-query-language

Creating multiple queries from the same data source in Google Charts


I have a spreadsheet that generically looks like this:

Date        Families
1/2/2016    3
1/3/2016    4
1/4/2016    5

What I'd like to do is create a table from it that's populated with queries for the following values:

  • Total families
  • Families from the last 30 days
  • Families from the 30 days before that
  • Families for the year-to-date.

My preferred way of doing this would be to set a variable for each query and then manually create a table using those variables.

For example:

var sum1 = value from Query1
var sum2 = value from Query2
var sum3 = value from Query3
var sum4 = value from Query4

var data = new.google.visualization.DataTable({
    data.AddColumn('string', 'This Month');
    data.AddColumn('string', 'Last Month');
    data.AddColumn('string', 'Year to Date');
    data.AddColumn('string', 'All Time');

    data.AddRow([sum1, sum2, sum3, sum4])
})

var chart = new.google.visualization.Table();
chart.draw(data);

To give the following table:

'This Month'  'Last Month'   'Year to Date'   'All Time'
 sum1          sum2           sum3             sum4

However, the big problem is that it seems you can only set one callback for each query, so getting something like this seems like it might require nested callbacks for each query, with each one getting turned into a datatable and then finally joined at the end.

For example:

var q1 = google.visualization.Query(URL);
q1.setQuery(QueryStatement1);
q1.send(function (r1) {
    var data1 = r1.getDataTable();
    var q2 = google.visualization.Query(URL);
    q2.setQuery(QueryStatement2);
    q2.send(function(r2) {
        var data2 = r2.getDataTable();

        var joinedData = google.visualization.data.join(
          data1, 
          data2,
          'full',
          [0,0],
          [[0,0], [0,0]]
    );
  }
}

The latter way seems a lot more complicated and would probably lend itself to difficulties with formatting. It also seems redundant to repeatedly call the same URL over and over.

The page this is going to go on will do similar things to 3 different columns, so that'd be getting a query 12 times from the same spreadsheet.

So, StackOverflow, I'd like to wrap up this question in two parts.

PART 1: Is it possible to avoid the latter method to create this table?

PART 2: If no, what's the best way to construct google.visualization.data.join() to achieve the desired result?

Thank you.


Solution

  • use a DataView with calculated columns for each category...

    var view = new google.visualization.DataView(response.getDataTable());
    view.setColumns([0,
      {
        calc: function (dt, r) {
          if ((dt.getValue(r, 0).getMonth() === (new Date()).getMonth()) &&
              (dt.getValue(r, 0).getFullYear() === (new Date()).getFullYear())) {
            return dt.getValue(r, 1);
          }
          return null;
        },
        type: 'number',
        label: 'This Month'
      },
      ...
    

    then use the group() method to aggregate the results...

    var aggData = google.visualization.data.group(
      view,
      [{
        column: 0,
        label: 'Total',
        modifier: function () {
          return 'Total';
        },
        type: 'string'
      }],
      [
        {
          column: 1,
          label: view.getColumnLabel(1),
          aggregation: google.visualization.data.sum,
          type: 'number'
        },
        ...
    

    see following working snippet...

    google.charts.load('current', {
      callback: drawChart,
      packages: ['table']
    });
    
    function drawChart() {
      var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/1-UbQQMofRecLpO9zUYoscQxo4Wi50N8YnmQmbW1lNJ0/edit?usp=sharing');
      query.setQuery('select A, B');
      query.send(function (response) {
        var view = new google.visualization.DataView(response.getDataTable());
        view.setColumns([0,
          {
            calc: function (dt, r) {
              if ((dt.getValue(r, 0).getMonth() === (new Date()).getMonth()) &&
                  (dt.getValue(r, 0).getFullYear() === (new Date()).getFullYear())) {
                return dt.getValue(r, 1);
              }
              return null;
            },
            type: 'number',
            label: 'This Month'
          },
          {
            calc: function (dt, r) {
              var curDate = new Date();
              var rowDate = dt.getValue(r, 0);
              var thisMonth = new Date(curDate.getFullYear(), curDate.getMonth(), 1);
              var lastMonth = new Date(curDate.getFullYear(), curDate.getMonth(), 0);
              lastMonth = new Date(lastMonth.getFullYear(), lastMonth.getMonth(), 1);
              if ((rowDate.getTime() >= lastMonth.getTime()) &&
                  (rowDate.getTime() < thisMonth.getTime())) {
                return dt.getValue(r, 1);
              }
              return null;
            },
            type: 'number',
            label: 'Last Month'
          },
          {
            calc: function (dt, r) {
              if (dt.getValue(r, 0).getYear() === (new Date()).getYear()) {
                return dt.getValue(r, 1);
              }
              return null;
            },
            type: 'number',
            label: 'Year to Date'
          },
          {
            calc: function (dt, r) {
              return dt.getValue(r, 1);
            },
            type: 'number',
            label: 'All Time'
          }
        ]);
    
        var aggData = google.visualization.data.group(
          view,
          [{
            column: 0,
            label: 'Total',
            modifier: function () {
              return 'Total';
            },
            type: 'string'
          }],
          [
            {
              column: 1,
              label: view.getColumnLabel(1),
              aggregation: google.visualization.data.sum,
              type: 'number'
            },
            {
              column: 2,
              label: view.getColumnLabel(2),
              aggregation: google.visualization.data.sum,
              type: 'number'
            },
            {
              column: 3,
              label: view.getColumnLabel(3),
              aggregation: google.visualization.data.sum,
              type: 'number'
            },
            {
              column: 4,
              label: view.getColumnLabel(4),
              aggregation: google.visualization.data.sum,
              type: 'number'
            }
          ]
        );
    
        var chartDiv = document.getElementById('chart_div');
        var chart = new google.visualization.Table(chartDiv);
        chart.draw(aggData);
      });
    }
    <script src="https://www.gstatic.com/charts/loader.js"></script>
    <div id="chart_div"></div>