Version 6: Hierarchy Expansion, let’s drill-up

Wednesday, February 22, 2012
You can download this version of the sample, and an improved version of the component library from here.

This version includes the capability to expand/collapse a hierarchy in the cellset table. An expanded hierarchy initially shows all of its members, and the user can drill-up to hide details. A collapsed hierarchy initially shows only its root members, and the user can drill-down to show further detail.

Expanded Hierarchy: Store Type hierarchy expanded with some undrilled members

I've modified the queryCellSet composite component (and QueryCellSetBean, its associated managed bean) to include a hierarchy expansion button in the header of both cellset axes, allowing the user toggling the expand state for a hierarchy.

This functionality is supported by three methods in the QueryAxis class:

  • void expandHierarchy(QueryHierarchy h)Expands the hierarchy, invalidating any previous drill/undrill operation on every appearance of h in this axis
  • void collapseHierarchy(QueryHierarchy h)
    Collapses the hierarchy, invalidating any previous drill/undrill operation on every appearance of h in this axis
  • boolean isExpanded(QueryHierarchy h)
    Tests if the provided hierarchy is expanded in this axis.

QueryHierarchy Expression Generation

The strategy to MDX generation for an expanded hierarchy is as follows:
  • A fully expanded hierarchy generates this MDX:
    DESCENDANTS(<root members set>, 0, SELF_AND_AFTER)
  • Member undrill is implemented by an external EXCEPT:
    EXCEPT(
        DESCENDANTS(<root members set>, 0, SELF_AND_AFTER),
        DESCENDANTS(<undrilled members set>, 0, AFTER)
    )

Refactoring Axis Set Expression Generation
Previously, axis MDX expression generation was driven by the drill tree. I've refactored this logic to let the axis expression generation be driven by the QueryHierarchy inclusion/exclusion tree.
This is a simplified version of the function to generate an axis set expression for a QueryHierarchy

  /**
   * Recursively generates the set expression for a Query Hierarchy.
   *
   *
@param current
   *            currently visited node
   *
@param expander
   *            helper object to execute drills/expansions
   *
@param drillList
   *            members to drill/undrill
   *
@param expression
   *            generated expression
   */
 
@SuppressWarnings("unused")
 
private void toOlap4jQueryDidactic(VisitingInfo current,
      HierarchyExpander expander, List<Member> drillList,
      AxisExpression expression
) {
   
boolean isMemberIncluded =
        current.getEffectiveSign
(Operator.MEMBER) == Sign.INCLUDE;
   
boolean areChildrenIncluded =
        current.getEffectiveSign
(Operator.CHILDREN) == Sign.INCLUDE;
   
boolean areDescendantsIncluded =
        current.getEffectiveSign
(Operator.DESCENDANTS) == Sign.INCLUDE;


   
// Processes current member, including or excluding it from the
    // expression as necessary.
   
Member currentMember = current.getMember();
   
if (isMemberIncluded) {
     
expression.include(currentMember);
     
if (expander.isDrilled(currentMember, drillList)) {
       
// Current member is included in the hierarchy but collapsed.
        // Ends this visit as we should not include any descendant.
       
return;
     
}
    }

   
// Recursively calls toOlap4jQueryDidactic on overrided children
   
List<Member> overridedChildren = new ArrayList<Member>();
   
for (SelectionTree overridedChild : current.getNode()
                       
.getOverridingChildren()) {
     
overridedChildren.add(overridedChild.getMember());

      VisitingInfo childVisit = current.visitChild
(overridedChild);
      toOlap4jQueryDidactic
(
         
childVisit,
          expander,
          drillList,
          expression
);
   
}

   
if (areDescendantsIncluded) {
     
// Expand/undrill descendants
     
MemberSet expansionBase;
     
if (areChildrenIncluded) {
       
expansionBase = new ChildrenMemberSet(currentMember,
            overridedChildren
);
     
} else {
       
expansionBase = new GrandchildrenSet(currentMember,
            overridedChildren
);
     
}
     
expander.expand(expansionBase, drillList, expression);
   
} else {
     
// Include children if necessary
     
if (areChildrenIncluded) {
       
MemberSet nonOverridingChildren
          =
new ChildrenMemberSet(currentMember, overridedChildren);
        expression.include
(nonOverridingChildren.getMdx());
     
}
    }
  }
}