wissel.net

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

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.