Skip to content

Commit

Permalink
MONDRIAN: Feature (#1824609) - Support for Aggregating Distinct Count…
Browse files Browse the repository at this point in the history
… Measures over list of tuples. Limitation: Has a hard limit of list size <= 100. Further optimization is possible by eliminating a tuple if the list already has another tuple that has all its members at parent level

[git-p4: depot-paths = "//open/mondrian/": change = 10340]
  • Loading branch information
Ajit Joglekar committed Dec 20, 2007
1 parent 03dca15 commit f91c8a8
Show file tree
Hide file tree
Showing 8 changed files with 651 additions and 155 deletions.
12 changes: 6 additions & 6 deletions mondrian.properties
Expand Up @@ -411,31 +411,31 @@ mondrian.test.WarnIfNoPatternForDialect=ANY

###############################################################################
# Property which defines whether to ignore measure when non joining
# dimension is in the tuple during aggregation
# dimension is in the tuple during aggregation.
#
# If there are unrelated dimensions to a measure in context during
# aggregation, the measure is ignored in the evaluation context. This
# behaviour kicks in only if the cubeusage for this measure has
# IgnoreUnrelatedDimensions attribute set to false
# IgnoreUnrelatedDimensions attribute set to false.
#
# Gender doesn't join with [Warehouse Sales] measure
# For example, Gender doesn't join with [Warehouse Sales] measure.
#
# With mondrian.olap.agg.IgnoreMeasureForNonJoiningDimension=true
# Warehouse Sales gets eliminated and is ignored in the aggregate value
# Warehouse Sales gets eliminated and is ignored in the aggregate value.
# [Store Sales] + [Warehouse Sales]
# SUM({Product.members * Gender.members}) 7,913,333.82
#
# With mondrian.olap.agg.IgnoreMeasureForNonJoiningDimension=false
# Warehouse Sales with Gender All level member contributes to the aggregate
# value
# value.
# [Store Sales] + [Warehouse Sales]
# SUM({Product.members * Gender.members}) 9,290,730.03
#
# On a report where Gender M, F and All members exist a user will see a
# large aggregated value compared to the aggregated value that can be
# arrived at by suming up values against Gender M and F. This can be
# confusing to the user. This feature can be used to eliminate such a
# situation
# situation.
#
#
#mondrian.olap.agg.IgnoreMeasureForNonJoiningDimension=false
Expand Down
68 changes: 52 additions & 16 deletions src/main/mondrian/olap/fun/AggregateFunDef.java
Expand Up @@ -28,11 +28,11 @@
*/
public class AggregateFunDef extends AbstractAggregateFunDef {
static final ReflectiveMultiResolver resolver =
new ReflectiveMultiResolver(
"Aggregate", "Aggregate(<Set>[, <Numeric Expression>])",
"Returns a calculated value using the appropriate aggregate function, based on the context of the query.",
new String[]{"fnx", "fnxn"},
AggregateFunDef.class);
new ReflectiveMultiResolver(
"Aggregate", "Aggregate(<Set>[, <Numeric Expression>])",
"Returns a calculated value using the appropriate aggregate function, based on the context of the query.",
new String[]{"fnx", "fnxn"},
AggregateFunDef.class);

public AggregateFunDef(FunDef dummyFunDef) {
super(dummyFunDef);
Expand All @@ -41,7 +41,7 @@ public AggregateFunDef(FunDef dummyFunDef) {
public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
final ListCalc listCalc = compiler.compileList(call.getArg(0));
final Calc calc = call.getArgCount() > 1 ?
compiler.compileScalar(call.getArg(1), true) :
compiler.compileScalar(call.getArg(1), true) :
new ValueCalc(call);
return new AggregateCalc(call, listCalc, calc);
}
Expand Down Expand Up @@ -73,20 +73,29 @@ public double evaluateDouble(Evaluator evaluator) {
}
final List list = evaluateCurrentList(listCalc, evaluator);
if (aggregator == RolapAggregator.DistinctCount) {
// Can't aggregate distinct-count values. To evaluate a
//If the list is empty, there is no need to evaluate any further
if (list.size() == 0) {
return DoubleNull;
}
// TODO: Optimize the list
// E.g.
// List consists of:
// (Gender.[All Gender], [Product].[All Products]),
// (Gender.[All Gender].[F], [Product].[All Products].[Drink]),
// (Gender.[All Gender].[M], [Product].[All Products].[Food])
// Can be optimized to:
// (Gender.[All Gender], [Product].[All Products])
//
// Similar optimization can also be done for list of members.

checkIfAggregationSizeIsTooLarge(list);

// Can't aggregate distinct-count values in the same way
// which is used for other types of aggregations. To evaluate a
// distinct-count across multiple members, we need to gather
// the members together, then evaluate the collection of
// members all at once. To do this, we postpone evaluation,
// and create a lambda function containing the members.
for (Object o : list) {
// Currently assume the list consists of members.
// We could in principle handle lists of tuples.
if (!(o instanceof Member)) {
throw new UnsupportedOperationException(
"aggregating distinct-count over lists of " +
"tuples is not supported");
}
}
Evaluator evaluator2 =
evaluator.pushAggregation((List<Member>) list);
final Object o = evaluator2.evaluateCurrent();
Expand All @@ -96,6 +105,33 @@ public double evaluateDouble(Evaluator evaluator) {
return (Double) rollup.aggregate(evaluator.push(), list, calc);
}

/**
* In case of distinct count totals, the Sql generated would have at
* least, as many where conditions as the size of the list.
* Incase of a large list, the SQL generation would take too much time
* and memory. Also the generated SQL would be too large to execute.
*
* <p>TODO: Optimize the list
* E.g.
* List consists of:
* (Gender.[All Gender], [Product].[All Products]),
* (Gender.[All Gender].[F], [Product].[All Products].[Drink]),
* (Gender.[All Gender].[M], [Product].[All Products].[Food])
* Can be optimized to:
* (Gender.[All Gender], [Product].[All Products])
*
* <p>Similar optimization can also be done for list of members.
*
* @param list
*/
private void checkIfAggregationSizeIsTooLarge(List list) {
if (list.size() > 100) {
throw newEvalException(
null,"Distinct Count aggregation is not supported over a " +
"large list");
}
}

public Calc[] getCalcs() {
return new Calc[] {listCalc, calc};
}
Expand Down

0 comments on commit f91c8a8

Please sign in to comment.