wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

Aggregating view data for use in d3js graphics

Hero image for Aggregating view data for use in d3js graphics
Dashboards are all the rage, so it is natural that your XPages application need a dash of a dashboard. A view makes an excellent source for dashboard data and the ability to categorize views handles the heavy lifting of aggregating the values you want to use e.g. in a bar or pie chart. I've been fallen in love with d3js since she is the ultimate visualization (if in doubt, read the classics ).
D3Js is a harsh mistress of exceptional beauty, so you might want to check out some of her offsprings like RickShaw, NVD3 or xCharts. Indulge in myriads of tutorials and reviews (check part 2 too) and the over 1000 examples.
With just a few lines of JavaScript any categorized view can be used as source for d3js. The script can read a categorized view with one to three categories. If you have one it reads the view, for two you get hierarchical data or provide a key, so you only retrieve the selected category etc.Since I use a viewNavigator performance is quite good:
function getCategoryData(viewName, dataColumn, getChildren, key) {
 var v = database.getView(viewName);
 var nav;
 if (key) {
  nav = v.createViewNavFromCategory(key);
 } else {
    nav= v.createViewNav();
 }
 var ve = nav.getFirst();
 var isFirst = true;
 var result = "[";
 while (ve) {
  if (!ve.isTotal()) {
   var curData = ve.getColumnValues();
   if (!isFirst) {
    result += ",";
   }
   result += "{label : \"";
   if (key) {
    result += curData[1 ];
   } else {
    result += curData[0 ];
   }
   result += "\", value : ";
   result += curData[dataColumn ];
   /*for 2 level categories we fetch additional data*/
   if (getChildren) {
    var childve = nav.getChild();
    var firstChild = true;
    result += ", children : [";
    while (childve) {
     var childData = childve.getColumnValues();
     if (!firstChild) {
      result += ",";
     }
     result += "{label : \"";
     result += childData[1 ];
     result += "\", value : ";
     result += childData[dataColumn ];
     result += "}";
     firstChild = false;
     childve = nav.getNextSibling(childve);
    }
    result += " ]"
   }
   result += "}";
   isFirst = false;
  }
  ve = nav.getNextSibling(ve);
 }
 result += " ]";
 return result;
}
A very typical use are year-to-date graphs, that accummulate the values from category to category. Here I use this:
function getCumulativeCategoryData(viewName, key, dataColumn, fetchChildren) {
 var v = database.getView(viewName);

 var nav = v.createViewNavFromCategory(key);
 var ve = nav.getFirst();
 var nextVe;
 var isFirst = true;
 var result = "[";
 var runningTotal = 0;
 while (ve) {
  // Prefetch the next entry
  nextVe = nav.getNextSibling(ve);
  if (!ve.isTotal()) {
   var curData = ve.getColumnValues();
   if (!isFirst) {
    result += ",";
   }
   result += "{label : \"";
   result += curData[1 ];
   result += "\", value : ";
   runningTotal += Number(curData[dataColumn ]);
   result += runningTotal;
   /*for 2 level categories we fetch additional data*/
   if (fetchChildren) {
    var runningSubTotal = 0;
    var childve = nav.getChild();
    var nextChildVe;
    var firstChild = true;
    result += ", children : [";
    while (childve) {
     nextChildVe = nav.getNextSibling(childve);
     var childData = childve.getColumnValues();
     if (!firstChild) {
      result += ",";
     }
     result += "{label : \"";
     result += childData[2 ];
     result += "\", value : ";
     runningSubTotal += Number(childData[dataColumn ]);
     result += runningSubTotal;
     result += "}";
     firstChild = false;
     try {
      childve.recycle();
     } catch (e) {
      // No action
     }
     childve = nextChildVe;
    result += " ]"
    }
   }
   result += "}";
   isFirst = false;
  }

  // Cleanup view entry
  try {
   ve.recycle();
  } catch (e) {
   // We don't care
  }
  ve = nextVe;
 }
 result += " ]";
 // Cleanup all objects
 try {
  nav.recyle();
  v.recycle();
 } catch (e) {
  // We don't care
 }
 return result;
}


As usual: YMMV
P.S.: for end-users, check out Tableau

Posted by on 24 September 2013 | Comments (2) | categories: XPages

Comments

  1. posted by Patrick Kwinten on Thursday 24 October 2013 AD:
    does not work very well in my IE browser (as more charting solutions do) :-?
  2. posted by Stephan H. Wissel on Thursday 24 October 2013 AD:
    @Patrick: you pick your own level of suffering.