com.espertech.esper.epl.agg.rollup.GroupByExpressionHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of esper Show documentation
Show all versions of esper Show documentation
Complex event processing and event series analysis component
/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
***************************************************************************************
*/
package com.espertech.esper.epl.agg.rollup;
import com.espertech.esper.collection.CombinationEnumeration;
import com.espertech.esper.collection.MultiKeyInt;
import com.espertech.esper.epl.expression.core.ExprNode;
import com.espertech.esper.epl.expression.core.ExprNodeUtility;
import com.espertech.esper.epl.expression.core.ExprValidationException;
import com.espertech.esper.epl.expression.visitor.ExprNodeSubselectDeclaredDotVisitor;
import com.espertech.esper.epl.spec.*;
import com.espertech.esper.util.CollectionUtil;
import com.espertech.esper.util.SerializableObjectCopier;
import java.io.StringWriter;
import java.util.*;
public class GroupByExpressionHelper {
public static GroupByClauseExpressions getGroupByRollupExpressions(List groupByElements,
SelectClauseSpecRaw selectClauseSpec, ExprNode optionalHavingNode, List orderByList,
ExprNodeSubselectDeclaredDotVisitor visitor)
throws ExprValidationException {
if (groupByElements == null || groupByElements.size() == 0) {
return null;
}
// walk group-by-elements, determine group-by expressions and rollup nodes
GroupByExpressionInfo groupByExpressionInfo = groupByToRollupNodes(groupByElements);
// obtain expression nodes, collect unique nodes and assign index
List distinctGroupByExpressions = new ArrayList();
Map expressionToIndex = new HashMap();
for (ExprNode exprNode : groupByExpressionInfo.getExpressions()) {
boolean found = false;
for (int i = 0; i < distinctGroupByExpressions.size(); i++) {
ExprNode other = distinctGroupByExpressions.get(i);
// find same expression
if (ExprNodeUtility.deepEquals(exprNode, other, false)) {
expressionToIndex.put(exprNode, i);
found = true;
break;
}
}
// not seen before
if (!found) {
expressionToIndex.put(exprNode, distinctGroupByExpressions.size());
distinctGroupByExpressions.add(exprNode);
}
}
// determine rollup, validate it is either (not both)
boolean hasGroupingSet = false;
boolean hasRollup = false;
for (GroupByClauseElement element : groupByElements) {
if (element instanceof GroupByClauseElementGroupingSet) {
hasGroupingSet = true;
}
if (element instanceof GroupByClauseElementRollupOrCube) {
hasRollup = true;
}
}
// no-rollup or grouping-sets means simply validate
ExprNode[] groupByExpressions = distinctGroupByExpressions.toArray(new ExprNode[distinctGroupByExpressions.size()]);
if (!hasRollup && !hasGroupingSet) {
return new GroupByClauseExpressions(groupByExpressions);
}
// evaluate rollup node roots
List nodes = groupByExpressionInfo.getNodes();
Object[][] perNodeCombinations = new Object[nodes.size()][];
GroupByRollupEvalContext context = new GroupByRollupEvalContext(expressionToIndex);
try {
for (int i = 0; i < nodes.size(); i++) {
GroupByRollupNodeBase node = nodes.get(i);
List combinations = node.evaluate(context);
perNodeCombinations[i] = new Object[combinations.size()];
for (int j = 0; j < combinations.size(); j++) {
perNodeCombinations[i][j] = combinations.get(j);
}
}
} catch (GroupByRollupDuplicateException ex) {
if (ex.getIndexes().length == 0) {
throw new ExprValidationException("Failed to validate the group-by clause, found duplicate specification of the overall grouping '()'");
} else {
StringWriter writer = new StringWriter();
String delimiter = "";
for (int i = 0; i < ex.getIndexes().length; i++) {
writer.append(delimiter);
writer.append(ExprNodeUtility.toExpressionStringMinPrecedenceSafe(groupByExpressions[ex.getIndexes()[i]]));
delimiter = ", ";
}
throw new ExprValidationException("Failed to validate the group-by clause, found duplicate specification of expressions (" + writer.toString() + ")");
}
}
// enumerate combinations building an index list
CombinationEnumeration combinationEnumeration = new CombinationEnumeration(perNodeCombinations);
Set combination = new TreeSet();
Set indexList = new LinkedHashSet();
for (; combinationEnumeration.hasMoreElements(); ) {
combination.clear();
Object[] combinationOA = combinationEnumeration.nextElement();
for (Object indexes : combinationOA) {
int[] indexarr = (int[]) indexes;
for (int anIndex : indexarr) {
combination.add(anIndex);
}
}
int[] indexArr = CollectionUtil.intArray(combination);
indexList.add(new MultiKeyInt(indexArr));
}
// obtain rollup levels
int[][] rollupLevels = new int[indexList.size()][];
int count = 0;
for (MultiKeyInt mk : indexList) {
rollupLevels[count++] = mk.getKeys();
}
int numberOfLevels = rollupLevels.length;
if (numberOfLevels == 1 && rollupLevels[0].length == 0) {
throw new ExprValidationException("Failed to validate the group-by clause, the overall grouping '()' cannot be the only grouping");
}
// obtain select-expression copies for rewrite
List expressions = selectClauseSpec.getSelectExprList();
ExprNode[][] selects = new ExprNode[numberOfLevels][];
for (int i = 0; i < numberOfLevels; i++) {
selects[i] = new ExprNode[expressions.size()];
for (int j = 0; j < expressions.size(); j++) {
SelectClauseElementRaw selectRaw = expressions.get(j);
if (!(selectRaw instanceof SelectClauseExprRawSpec)) {
throw new ExprValidationException("Group-by with rollup requires that the select-clause does not use wildcard");
}
SelectClauseExprRawSpec compiled = (SelectClauseExprRawSpec) selectRaw;
selects[i][j] = copyVisitExpression(compiled.getSelectExpression(), visitor);
}
}
// obtain having-expression copies for rewrite
ExprNode[] optHavingNodeCopy = null;
if (optionalHavingNode != null) {
optHavingNodeCopy = new ExprNode[numberOfLevels];
for (int i = 0; i < numberOfLevels; i++) {
optHavingNodeCopy[i] = copyVisitExpression(optionalHavingNode, visitor);
}
}
// obtain orderby-expression copies for rewrite
ExprNode[][] optOrderByCopy = null;
if (orderByList != null && orderByList.size() > 0) {
optOrderByCopy = new ExprNode[numberOfLevels][];
for (int i = 0; i < numberOfLevels; i++) {
optOrderByCopy[i] = new ExprNode[orderByList.size()];
for (int j = 0; j < orderByList.size(); j++) {
OrderByItem element = orderByList.get(j);
optOrderByCopy[i][j] = copyVisitExpression(element.getExprNode(), visitor);
}
}
}
return new GroupByClauseExpressions(groupByExpressions, rollupLevels, selects, optHavingNodeCopy, optOrderByCopy);
}
private static GroupByExpressionInfo groupByToRollupNodes(List groupByExpressions) {
List parents = new ArrayList(groupByExpressions.size());
List exprNodes = new ArrayList();
for (GroupByClauseElement element : groupByExpressions) {
GroupByRollupNodeBase parent;
if (element instanceof GroupByClauseElementExpr) {
GroupByClauseElementExpr expr = (GroupByClauseElementExpr) element;
exprNodes.add(expr.getExpr());
parent = new GroupByRollupNodeSingleExpr(expr.getExpr());
} else if (element instanceof GroupByClauseElementRollupOrCube) {
GroupByClauseElementRollupOrCube spec = (GroupByClauseElementRollupOrCube) element;
parent = new GroupByRollupNodeRollupOrCube(spec.isCube());
groupByAddRollup(spec, parent, exprNodes);
} else if (element instanceof GroupByClauseElementGroupingSet) {
GroupByClauseElementGroupingSet spec = (GroupByClauseElementGroupingSet) element;
parent = new GroupByRollupNodeGroupingSet();
for (GroupByClauseElement groupElement : spec.getElements()) {
if (groupElement instanceof GroupByClauseElementExpr) {
GroupByClauseElementExpr single = (GroupByClauseElementExpr) groupElement;
exprNodes.add(single.getExpr());
parent.add(new GroupByRollupNodeSingleExpr(single.getExpr()));
}
if (groupElement instanceof GroupByClauseElementCombinedExpr) {
GroupByClauseElementCombinedExpr combined = (GroupByClauseElementCombinedExpr) groupElement;
exprNodes.addAll(combined.getExpressions());
parent.add(new GroupByRollupNodeCombinedExpr(combined.getExpressions()));
}
if (groupElement instanceof GroupByClauseElementRollupOrCube) {
GroupByClauseElementRollupOrCube rollup = (GroupByClauseElementRollupOrCube) groupElement;
GroupByRollupNodeRollupOrCube node = new GroupByRollupNodeRollupOrCube(rollup.isCube());
groupByAddRollup(rollup, node, exprNodes);
parent.add(node);
}
}
} else {
throw new IllegalStateException("Unexpected group-by clause element " + element);
}
parents.add(parent);
}
return new GroupByExpressionInfo(exprNodes, parents);
}
private static void groupByAddRollup(GroupByClauseElementRollupOrCube spec, GroupByRollupNodeBase parent, List exprNodes) {
for (GroupByClauseElement rolledUp : spec.getRollupExpressions()) {
if (rolledUp instanceof GroupByClauseElementExpr) {
GroupByClauseElementExpr expr = (GroupByClauseElementExpr) rolledUp;
exprNodes.add(expr.getExpr());
parent.add(new GroupByRollupNodeSingleExpr(expr.getExpr()));
} else {
GroupByClauseElementCombinedExpr combined = (GroupByClauseElementCombinedExpr) rolledUp;
exprNodes.addAll(combined.getExpressions());
parent.add(new GroupByRollupNodeCombinedExpr(combined.getExpressions()));
}
}
}
private static ExprNode copyVisitExpression(ExprNode expression, ExprNodeSubselectDeclaredDotVisitor visitor) {
try {
ExprNode node = (ExprNode) SerializableObjectCopier.copy(expression);
node.accept(visitor);
return node;
} catch (Exception e) {
throw new RuntimeException("Internal error providing expression tree: " + e.getMessage(), e);
}
}
private static class GroupByExpressionInfo {
private final List expressions;
private final List nodes;
private GroupByExpressionInfo(List expressions, List nodes) {
this.expressions = expressions;
this.nodes = nodes;
}
public List getExpressions() {
return expressions;
}
public List getNodes() {
return nodes;
}
}
}