Skip to content

Commit

Permalink
Adds a sort function to a QueryAxis. Axis can now be sorted by any li…
Browse files Browse the repository at this point in the history
…teral expression.

git-svn-id: https://olap4j.svn.sourceforge.net/svnroot/olap4j/trunk@275 c6a108a4-781c-0410-a6c6-c2d559e19af0
  • Loading branch information
lucboudreau committed Aug 4, 2009
1 parent 38cc365 commit a4cef50
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 10 deletions.
23 changes: 22 additions & 1 deletion src/org/olap4j/query/Olap4jNodeConverter.java
Expand Up @@ -149,12 +149,33 @@ private static AxisNode toOlap4j(QueryAxis axis) {
callNode);
}
}

// We might need to sort the whole axis.
ParseTreeNode sortedNode = null;
if (axis.getSortOrder() != null) {
LiteralNode evaluatorNode =
LiteralNode.createSymbol(
null,
axis.getSortIdentifierNodeName());
sortedNode =
new CallNode(
null,
"Order",
Syntax.Function,
callNode,
evaluatorNode,
LiteralNode.createSymbol(
null, axis.getSortOrder().name()));
} else {
sortedNode = callNode;
}

return new AxisNode(
null,
axis.isNonEmpty(),
axis.getLocation(),
new ArrayList<IdentifierNode>(),
callNode);
sortedNode);
}

private static List<ParseTreeNode> toOlap4j(QueryDimension dimension) {
Expand Down
123 changes: 123 additions & 0 deletions src/org/olap4j/query/QueryAxis.java
Expand Up @@ -10,6 +10,9 @@
package org.olap4j.query;

import org.olap4j.Axis;
import org.olap4j.OlapException;
import org.olap4j.metadata.Measure;
import org.olap4j.metadata.Member;

import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -35,6 +38,8 @@ public class QueryAxis extends QueryNodeImpl {
private final Query query;
protected Axis location = null;
private boolean nonEmpty;
private SortOrder sortOrder = null;
private String sortEvaluationLiteral = null;

/**
* Creates a QueryAxis.
Expand Down Expand Up @@ -239,6 +244,124 @@ void tearDown() {
this.clearListeners();
this.getDimensions().clear();
}

/**
* Defines in what order to perform the sort.
*/
public static enum SortOrder {
/**
* Ascending sort order. Members of
* the same hierarchy are still kept together.
*/
ASC,
/**
* Descending sort order. Members of
* the same hierarchy are still kept together.
*/
DESC,
/**
* Sorts in ascending order, but does not
* maintain members of a same hierarchy
* together. This is known as a "break
* hierarchy ascending sort".
*/
BASC,
/**
* Sorts in descending order, but does not
* maintain members of a same hierarchy
* together. This is known as a "break
* hierarchy descending sort".
*/
BDESC
}

/**
* <p>Sorts the axis according to the supplied order
* and member unique name.
* <p>Using this method will try to resolve the supplied name
* parts from the underlying cube and find the corresponding
* member. This member will then be passed as a sort evaluation
* expression.
* @param order The {@link QueryAxis.SortOrder} in which to
* sort the axis.
* @param nameParts The unique name parts of the sort
* evaluation expression.
* @throws OlapException If the supplied member cannot be resolved
* with {@link org.olap4j.metadata.Cube#lookupMember(String...)}
*/
public void sort(SortOrder order, String... nameParts)
throws OlapException
{
assert order != null;
assert nameParts != null;
Member member = query.getCube().lookupMember(nameParts);
if (member != null) {
sort(order, member);
} else {
throw new OlapException("Cannot find member.");
}
}

/**
* <p>Sorts the axis according to the supplied order
* and member.
* <p>This method is most commonly called by passing
* it a {@link Measure}.
* @param order The {@link QueryAxis.SortOrder} in which to
* sort the axis.
* @param member The member that will be used as a sort
* evaluation expression.
*/
public void sort(SortOrder order, Member member) {
assert order != null;
assert member != null;
sort(order, member.getUniqueName());
}

/**
* <p>Sorts the axis according to the supplied order
* and evaluation expression.
* <p>The string value passed as the sortIdentifierNodeName
* parameter willb e used literally as a sort evaluator.
* @param order The {@link QueryAxis.SortOrder} in which to
* sort the axis.
* @param sortEvaluationLiteral The literal expression that
* will be used to sort against.
*/
public void sort(SortOrder order, String sortEvaluationLiteral) {
assert order != null;
assert sortEvaluationLiteral != null;
this.sortOrder = order;
this.sortEvaluationLiteral = sortEvaluationLiteral;
}

/**
* Clears the sort parameters from this axis.
*/
public void clearSort() {
this.sortEvaluationLiteral = null;
this.sortOrder = null;
}

/**
* Returns the current sort order in which this
* axis will be sorted. Might return null of none
* is currently specified.
* @return The {@link SortOrder}
*/
public SortOrder getSortOrder() {
return this.sortOrder;
}

/**
* Returns the current sort evaluation expression,
* or null if none are currently defined.
* @return The string literal that will be used in the
* MDX Order() function.
*/
public String getSortIdentifierNodeName() {
return sortEvaluationLiteral;
}
}

// End QueryAxis.java
23 changes: 22 additions & 1 deletion src/org/olap4j/query/QueryDimension.java
Expand Up @@ -307,22 +307,43 @@ public void setDimension(Dimension dimension) {
this.dimension = dimension;
}

/**
* Sorts the dimension members by name in the
* order supplied as a parameter.
* @param order The {@link SortOrder} to use.
*/
public void setSortOrder(SortOrder order) {
this.sortOrder = order;
}

/**
* Returns the current order in which the
* dimension members are sorted.
* @return A value of {@link SortOrder}
*/
public SortOrder getSortOrder() {
return this.sortOrder;
}

/**
* Clears the current sorting settings.
*/
public void clearSort() {
this.sortOrder = null;
}

public HierarchizeMode getHierarchizeMode() {
return hierarchizeMode;
}

/**
* Triggers the hierarchization of the included members within this
* QueryDimension.
* @param hierarchizeMode Whether or not to include the POST litteral
* <p>The dimension inclusions will be wrapped in an MDX Hierarchize
* function call.
* @param hierarchizeMode If parents should be included before or after
* their children. (Equivalent to the POST/PRE MDX literal for the
* Hierarchize() function)
* inside the Hierarchize() MDX function call.
*/
public void setHierarchizeMode(HierarchizeMode hierarchizeMode) {
Expand Down
102 changes: 94 additions & 8 deletions testsrc/org/olap4j/OlapTest.java
Expand Up @@ -472,8 +472,12 @@ public void testSortDimension() {
QueryDimension measuresDimension = query.getDimension("Measures");
measuresDimension.include("Measures", "Store Sales");

QueryDimension timeDimension = query.getDimension("Time");
timeDimension.include("Time", "Year", "1997", "Q3", "7");

query.getAxis(Axis.ROWS).addDimension(productDimension);
query.getAxis(Axis.COLUMNS).addDimension(measuresDimension);
query.getAxis(Axis.FILTER).addDimension(timeDimension);

query.validate();

Expand All @@ -490,7 +494,8 @@ public void testSortDimension() {
"SELECT\n"
+ "{[Measures].[Store Sales]} ON COLUMNS,\n"
+ "{{[Product].[All Products].[Drink], [Product].[All Products].[Drink].Children}} ON ROWS\n"
+ "FROM [Sales]",
+ "FROM [Sales]\n"
+ "WHERE ([Time].[1997].[Q3].[7])",
mdxString);

// Sort the products in ascending order.
Expand All @@ -502,32 +507,114 @@ public void testSortDimension() {
"SELECT\n"
+ "{[Measures].[Store Sales]} ON COLUMNS,\n"
+ "{Order({{[Product].[All Products].[Drink], [Product].[All Products].[Drink].Children}}, [Product].CurrentMember.Name, DESC)} ON ROWS\n"
+ "FROM [Sales]",
+ "FROM [Sales]\n"
+ "WHERE ([Time].[1997].[Q3].[7])",
sortedMdxString);

CellSet results = query.execute();
String s = TestContext.toString(results);
TestContext.assertEqualsVerbose(
"Axis #0:\n"
+ "{[Store].[All Stores], [Store Size in SQFT].[All Store Size in SQFTs], [Store Type].[All Store Types], [Time].[1997], [Promotion Media].[All Media], [Promotions].[All Promotions], [Customers].[All Customers], [Education Level].[All Education Levels], [Gender].[All Gender], [Marital Status].[All Marital Status], [Yearly Income].[All Yearly Incomes]}\n"
+ "{[Store].[All Stores], [Store Size in SQFT].[All Store Size in SQFTs], [Store Type].[All Store Types], [Time].[1997].[Q3].[7], [Promotion Media].[All Media], [Promotions].[All Promotions], [Customers].[All Customers], [Education Level].[All Education Levels], [Gender].[All Gender], [Marital Status].[All Marital Status], [Yearly Income].[All Yearly Incomes]}\n"
+ "Axis #1:\n"
+ "{[Measures].[Store Sales]}\n"
+ "Axis #2:\n"
+ "{[Product].[All Products].[Drink]}\n"
+ "{[Product].[All Products].[Drink].[Dairy]}\n"
+ "{[Product].[All Products].[Drink].[Beverages]}\n"
+ "{[Product].[All Products].[Drink].[Alcoholic Beverages]}\n"
+ "Row #0: 48,836.21\n"
+ "Row #1: 7,058.60\n"
+ "Row #2: 27,748.53\n"
+ "Row #3: 14,029.08\n",
+ "Row #0: 4,409.58\n"
+ "Row #1: 629.69\n"
+ "Row #2: 2,477.02\n"
+ "Row #3: 1,302.87\n",
s);
} catch (Exception e) {
e.printStackTrace();
fail();
}
}

public void testSortAxis() {
try {
Cube cube = getFoodmartCube("Sales");
if (cube == null) {
fail("Could not find Sales cube");
}
Query query = new Query("my query", cube);

// create selections

QueryDimension productDimension = query.getDimension("Product");
productDimension.include(
Selection.Operator.INCLUDE_CHILDREN, "Product", "Drink");

QueryDimension measuresDimension = query.getDimension("Measures");
measuresDimension.include("Measures", "Store Sales");

QueryDimension timeDimension = query.getDimension("Time");
timeDimension.include("Time", "Year", "1997", "Q3", "7");

query.getAxis(Axis.ROWS).addDimension(productDimension);
query.getAxis(Axis.COLUMNS).addDimension(measuresDimension);
query.getAxis(Axis.FILTER).addDimension(timeDimension);

query.validate();

assertEquals(
Axis.ROWS,
productDimension.getAxis().getLocation());
assertEquals(
Axis.COLUMNS,
measuresDimension.getAxis().getLocation());

SelectNode mdx = query.getSelect();
String mdxString = mdx.toString();
TestContext.assertEqualsVerbose(
"SELECT\n"
+ "{[Measures].[Store Sales]} ON COLUMNS,\n"
+ "{{[Product].[All Products].[Drink], [Product].[All Products].[Drink].Children}} ON ROWS\n"
+ "FROM [Sales]\n"
+ "WHERE ([Time].[1997].[Q3].[7])",
mdxString);

// Sort the rows in ascending order.
query.getAxis(Axis.ROWS).sort(
org.olap4j.query.QueryAxis.SortOrder.BASC,
"Measures",
"Store Sales");

SelectNode sortedMdx = query.getSelect();
String sortedMdxString = sortedMdx.toString();
TestContext.assertEqualsVerbose(
"SELECT\n"
+ "{[Measures].[Store Sales]} ON COLUMNS,\n"
+ "Order({{[Product].[All Products].[Drink], [Product].[All Products].[Drink].Children}}, [Measures].[Store Sales], BASC) ON ROWS\n"
+ "FROM [Sales]\n"
+ "WHERE ([Time].[1997].[Q3].[7])",
sortedMdxString);

CellSet results = query.execute();
String s = TestContext.toString(results);
TestContext.assertEqualsVerbose(
"Axis #0:\n"
+ "{[Store].[All Stores], [Store Size in SQFT].[All Store Size in SQFTs], [Store Type].[All Store Types], [Time].[1997].[Q3].[7], [Promotion Media].[All Media], [Promotions].[All Promotions], [Customers].[All Customers], [Education Level].[All Education Levels], [Gender].[All Gender], [Marital Status].[All Marital Status], [Yearly Income].[All Yearly Incomes]}\n"
+ "Axis #1:\n"
+ "{[Measures].[Store Sales]}\n"
+ "Axis #2:\n"
+ "{[Product].[All Products].[Drink].[Dairy]}\n"
+ "{[Product].[All Products].[Drink].[Alcoholic Beverages]}\n"
+ "{[Product].[All Products].[Drink].[Beverages]}\n"
+ "{[Product].[All Products].[Drink]}\n"
+ "Row #0: 629.69\n"
+ "Row #1: 1,302.87\n"
+ "Row #2: 2,477.02\n"
+ "Row #3: 4,409.58\n",
s);
} catch (Exception e) {
e.printStackTrace();
fail();
}
}

public void testDimensionsOrder() {
try {
Expand All @@ -548,7 +635,6 @@ public void testDimensionsOrder() {
Selection.Operator.INCLUDE_CHILDREN, "Store", "USA");

QueryDimension timeDimension = query.getDimension("Time");

timeDimension.include(Selection.Operator.CHILDREN, "Time", "1997");

QueryDimension measuresDimension = query.getDimension("Measures");
Expand Down

0 comments on commit a4cef50

Please sign in to comment.