/*
//$Id: MetadataTest.java 482 2012-01-05 23:27:27Z jhyde $
//
//Licensed to Julian Hyde under one or more contributor license
//agreements. See the NOTICE file distributed with this work for
//additional information regarding copyright ownership.
//
//Julian Hyde licenses this file to you under the Apache License,
//Version 2.0 (the "License"); you may not use this file except in
//compliance with the License. You may obtain a copy of the License at:
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
*/
package org.olap4j;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import junit.framework.TestCase;
import org.olap4j.CellSetFormatterTest.Format;
import org.olap4j.driver.olap4ld.Olap4ldUtil;
import org.olap4j.driver.olap4ld.helper.Olap4ldLinkedDataUtil;
import org.olap4j.driver.olap4ld.linkeddata.BaseCubeOp;
import org.olap4j.driver.olap4ld.linkeddata.DiceOp;
import org.olap4j.driver.olap4ld.linkeddata.EmbeddedSesameEngine;
import org.olap4j.driver.olap4ld.linkeddata.LogicalOlapOp;
import org.olap4j.driver.olap4ld.linkeddata.LogicalOlapQueryPlan;
import org.olap4j.driver.olap4ld.linkeddata.PhysicalOlapQueryPlan;
import org.olap4j.driver.olap4ld.linkeddata.ProjectionOp;
import org.olap4j.driver.olap4ld.linkeddata.Restrictions;
import org.olap4j.layout.RectangularCellSetFormatter;
import org.olap4j.layout.TraditionalCellSetFormatter;
import org.semanticweb.yars.nx.Node;
import org.semanticweb.yars.nx.Resource;
/**
* Tests on building a slicer. Input: Arbitrary dataset. Output: For each
* 1-/2-dimensional slice show output.
*
* Documentation: http://www.linked-data-cubes.org/index.php/Slicer#Problem:
* _How_to_create_all_1-dimensional_queries
*
* @version $Id: MetadataTest.java 482 2012-01-05 23:27:27Z jhyde $
*/
public class Slicer_QueryTest extends TestCase {
private EmbeddedSesameEngine lde;
// Stores members of dimension (dimensionUniquename hash code)
private HashMap<Integer, List<Node[]>> membersofdimensions;
private List<Node[]> dimensions;
private List<Node[]> measures;
private List<Node[]> cubes;
private List<Node[]> hierarchies;
public Slicer_QueryTest() throws SQLException {
Olap4ldUtil.prepareLogging();
// Logging
// For debugging purposes
// Olap4ldUtil._log.setLevel(Level.CONFIG);
// For monitoring usage
Olap4ldUtil._log.setLevel(Level.INFO);
// For warnings (and errors) only
// Olap4ldUtil._log.setLevel(Level.WARNING);
try {
// Must have settings without influence on query processing
URL serverUrlObject = new URL("http://example.de");
List<String> datastructuredefinitions = new ArrayList<String>();
List<String> datasets = new ArrayList<String>();
lde = new EmbeddedSesameEngine(serverUrlObject,
datastructuredefinitions, datasets, "EMBEDDEDSESAME");
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
protected void setUp() throws SQLException {
}
protected void tearDown() throws Exception {
}
public void test_example_ssb001_slicer_certainDim() {
/*
* Coordinates
*/
List<Integer> coordinates = new ArrayList<Integer>();
// We query for the very first slice
coordinates.add(0, 0);
coordinates.add(1, 0);
coordinates.add(2, 0);
coordinates.add(3, 0);
String coordinateString = "(";
for (int i = 0; i < coordinates.size(); i++) {
coordinateString += coordinates.get(i);
if (i != coordinates.size() - 1) {
coordinateString += ", ";
}
}
String dsUri = "http://olap4ld.googlecode.com/git/OLAP4LD-trunk/tests/ssb001/ttl/example.ttl#ds";
System.out.println("Find 1-dim for " + dsUri + coordinateString);
List<LogicalOlapQueryPlan> logicalqueries = getOneDimSlices(dsUri);
LogicalOlapQueryPlan logicalOlapQueryPlan = logicalqueries
.get(coordinatesToOrdinal(coordinates));
executeStatement(logicalOlapQueryPlan);
}
public void test_example_ssb001_slicer_allOneDim() {
String dsUri = "http://olap4ld.googlecode.com/git/OLAP4LD-trunk/tests/ssb001/ttl/example.ttl#ds";
List<LogicalOlapQueryPlan> queryplans = getOneDimSlices(dsUri);
for (LogicalOlapQueryPlan logicalOlapQueryPlan : queryplans) {
executeStatement(logicalOlapQueryPlan);
}
}
public void testEurostatEmploymentRateExampleSlices() {
String dsUri = "http://olap4ld.googlecode.com/git/OLAP4LD-trunk/tests/estatwrap/tsdec420_ds.rdf#ds";
List<LogicalOlapQueryPlan> queryplans = getOneDimSlices(dsUri);
for (LogicalOlapQueryPlan logicalOlapQueryPlan : queryplans) {
executeStatement(logicalOlapQueryPlan);
}
}
public List<Integer> ordinalToCoordinates(int ordinal) {
final List<Integer> list = new ArrayList<Integer>(dimensions.size());
int modulo = 1;
Map<String, Integer> dimensionmap = Olap4ldLinkedDataUtil
.getNodeResultFields(dimensions.get(0));
boolean first = true;
for (Node[] dimension : dimensions) {
// No measure dimension
if (dimension[dimensionmap.get("?DIMENSION_UNIQUE_NAME")]
.toString().equals(
Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) {
continue;
}
if (first) {
first = false;
continue;
}
// Get unique name
String dimensionUniqueName = dimension[dimensionmap
.get("?DIMENSION_UNIQUE_NAME")].toString();
List<Node[]> members = this.membersofdimensions
.get(dimensionUniqueName.hashCode());
int prevModulo = modulo;
modulo *= members.size() - 1;
list.add((ordinal % modulo) / prevModulo);
}
if (ordinal < 0 || ordinal >= modulo) {
throw new IndexOutOfBoundsException("Cell ordinal " + ordinal
+ ") lies outside CellSet bounds (?" + ")");
}
return list;
}
/**
* From a list of coordinates that relate to the axes (getAxes),
*/
public int coordinatesToOrdinal(List<Integer> coordinates) {
// List<CellSetAxis> axes = getAxes();
// We do not count the measure dimension and the header node.
if (coordinates.size() != dimensions.size() - 2) {
throw new IllegalArgumentException(
"Coordinates have different dimensionality "
+ coordinates.size() + " than dataset "
+ dimensions.size());
}
int modulo = 1;
int ordinal = 0;
int k = 0;
Map<String, Integer> dimensionmap = Olap4ldLinkedDataUtil
.getNodeResultFields(dimensions.get(0));
boolean first = true;
for (Node[] dimension : dimensions) {
// No measure dimension
if (dimension[dimensionmap.get("?DIMENSION_UNIQUE_NAME")]
.toString().equals(
Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) {
continue;
}
if (first) {
first = false;
continue;
}
final Integer coordinate = coordinates.get(k++);
// Get unique name
String dimensionUniqueName = dimension[dimensionmap
.get("?DIMENSION_UNIQUE_NAME")].toString();
List<Node[]> members = this.membersofdimensions
.get(dimensionUniqueName.hashCode());
if (coordinate < 0 || coordinate >= members.size() - 1) {
throw new IndexOutOfBoundsException("Coordinate " + coordinate
+ " of axis " + k + " is out of range (?" + ")");
}
// Add to ordinal the actual coordinate of the axis times the size
// of the last axis
ordinal += coordinate * modulo;
// modulo is the number of positions in this axis
modulo *= members.size() - 1;
}
return ordinal;
}
private void populateMetadata(String dsUri) {
Restrictions restrictions = new Restrictions();
restrictions.cubeNamePattern = new Resource(dsUri);
try {
// In order to fill the engine with data
this.cubes = lde.getCubes(restrictions);
assertEquals(2, cubes.size());
Map<String, Integer> cubemap = Olap4ldLinkedDataUtil
.getNodeResultFields(cubes.get(0));
System.out.println("CUBE_NAME: "
+ cubes.get(1)[cubemap.get("?CUBE_NAME")]);
// Get all metadata
this.measures = lde.getMeasures(restrictions);
assertEquals(true, measures.size() > 1);
System.out.println("MEASURE_UNIQUE_NAMES:");
Map<String, Integer> measuremap = Olap4ldLinkedDataUtil
.getNodeResultFields(measures.get(0));
for (Node[] nodes : measures) {
System.out
.println(nodes[measuremap.get("?MEASURE_UNIQUE_NAME")]);
}
this.dimensions = lde.getDimensions(restrictions);
assertEquals(true, dimensions.size() > 1);
System.out.println("DIMENSION_UNIQUE_NAMES:");
Map<String, Integer> dimensionmap = Olap4ldLinkedDataUtil
.getNodeResultFields(dimensions.get(0));
for (Node[] nodes : dimensions) {
System.out.println(nodes[dimensionmap
.get("?DIMENSION_UNIQUE_NAME")]);
}
this.hierarchies = lde.getHierarchies(restrictions);
// this.levels = lde.getLevels(restrictions);
// this.members = lde.getMembers(restrictions);
// Collect members
this.membersofdimensions = new HashMap<Integer, List<Node[]>>();
for (int i = 1; i < dimensions.size(); i++) {
restrictions = new Restrictions();
restrictions.cubeNamePattern = new Resource(dsUri);
restrictions.dimensionUniqueName = dimensions.get(i)[dimensionmap
.get("?DIMENSION_UNIQUE_NAME")];
// Fix all members: We get all members no matter from which
// hierarchy?
List<Node[]> members = lde.getMembers(restrictions);
membersofdimensions.put(
restrictions.dimensionUniqueName.hashCode(), members);
assertEquals(true, members.size() > 1);
System.out.println("DIMENSION_UNIQUE_NAME:"
+ restrictions.dimensionUniqueName);
System.out.println("MEMBER_UNIQUE_NAMES:");
Map<String, Integer> membermap = Olap4ldLinkedDataUtil
.getNodeResultFields(members.get(0));
for (Node[] nodes : members) {
System.out.println(nodes[membermap
.get("?MEMBER_UNIQUE_NAME")]);
}
}
} catch (OlapException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* For a dataset, we create all one-dimensional queries.
*
* For that, we ask for all dimensions. We go through all dimensions and for
* each we set a value for each other dimension.
*
* @param dsUri
* @return
*/
private List<LogicalOlapQueryPlan> getOneDimSlices(String dsUri) {
System.out.println("Find 1-dim for " + dsUri + "...");
// First, we need the metadata of the dataset
populateMetadata(dsUri);
List<LogicalOlapQueryPlan> output = new ArrayList<LogicalOlapQueryPlan>();
Map<String, Integer> dimensionmap = Olap4ldLinkedDataUtil
.getNodeResultFields(dimensions.get(0));
// 1-dim
// First is header
boolean first = true;
for (Node[] dim2rollup : dimensions) {
// Not the the header
if (first) {
first = false;
continue;
}
// No measure dimension
if (dim2rollup[dimensionmap.get("?DIMENSION_UNIQUE_NAME")]
.toString().equals(
Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) {
continue;
}
/*
* Now, I should count an ordinal number and translate it into
* coordinates.
*/
// fix all other dim
List<List<Node[]>> membercombinations = new ArrayList<List<Node[]>>();
List<Node[]> fixeddims = new ArrayList<Node[]>();
List<Node[]> hierarchysignature = new ArrayList<Node[]>();
first = true;
for (Node[] dim2fix : dimensions) {
// No first
if (first) {
first = false;
fixeddims.add(dimensions.get(0));
hierarchysignature.add(hierarchies.get(0));
continue;
}
// No measure dimension
if (dim2fix[dimensionmap.get("?DIMENSION_UNIQUE_NAME")]
.toString()
.equals(Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) {
continue;
}
// No non-fixed dim
if (dim2fix[dimensionmap.get("?DIMENSION_UNIQUE_NAME")]
.toString().equals(
dim2rollup[dimensionmap
.get("?DIMENSION_UNIQUE_NAME")]
.toString())) {
continue;
}
List<Node[]> members = membersofdimensions
.get(dim2fix[dimensionmap
.get("?DIMENSION_UNIQUE_NAME")].toString()
.hashCode());
// After, every List<Node> in membercombinations is
// one possible query
membercombinations = cartesian_product(membercombinations,
members);
fixeddims.add(dim2fix);
Map<String, Integer> hierarchymap = Olap4ldLinkedDataUtil
.getNodeResultFields(hierarchies.get(0));
for (Node[] hierarchy2fix : hierarchies) {
if (dim2fix[dimensionmap.get("?DIMENSION_UNIQUE_NAME")]
.toString().equals(
hierarchy2fix[hierarchymap
.get("?DIMENSION_UNIQUE_NAME")]
.toString())) {
hierarchysignature.add(hierarchy2fix);
break;
}
}
}
assertEquals(true, membercombinations.size() > 0);
assertEquals(true, membercombinations.get(0).size() > 0);
// membercombinations does not have a header
// First is header
first = true;
for (List<Node[]> membercombination : membercombinations) {
if (first) {
first = false;
continue;
}
LogicalOlapQueryPlan myplan = createQueryPlanForSlice(
hierarchysignature, membercombination);
output.add(myplan);
}
}
return output;
}
private LogicalOlapQueryPlan createQueryPlanForSlice(List<Node[]> hierarchysignature, List<Node[]> membercombination) {
// Query 1-dim
// One member combination contains a header and a list of
// members (= one combination)
// each combination will have the same signature
Map<String, Integer> cubemap = Olap4ldLinkedDataUtil
.getNodeResultFields(cubes.get(0));
System.out.println("CUBE_NAME: "
+ cubes.get(1)[cubemap.get("?CUBE_NAME")]);
LogicalOlapOp basecube = new BaseCubeOp(cubes.get(1)[cubemap.get("?CUBE_NAME")].toString());
LogicalOlapOp projection = new ProjectionOp(basecube, measures);
List<List<Node[]>> aMembercombinations = new ArrayList<List<Node[]>>();
aMembercombinations.add(membercombination);
LogicalOlapOp dice = new DiceOp(projection, hierarchysignature,
aMembercombinations);
// Header is included
LogicalOlapQueryPlan myplan = new LogicalOlapQueryPlan(dice);
return myplan;
}
/**
* Adds members to membercombinations, i.e., create new membercombinations
* by combining each single membercombinations with each single members.
*
* @param membercombinations
* @param members
* @return
*/
private List<List<Node[]>> cartesian_product(
List<List<Node[]>> membercombinations, List<Node[]> members) {
List<List<Node[]>> newmembercombinations = new ArrayList<List<Node[]>>();
// Three possibilities: 1) membercombinations empty 2) members empty 3)
// none empty
if (membercombinations.isEmpty()) {
// First is header
for (int i = 1; i < members.size(); i++) {
Node[] member = members.get(i);
List<Node[]> newmembercombination = new ArrayList<Node[]>();
// Add header
newmembercombination.add(members.get(0));
newmembercombination.add(member);
newmembercombinations.add(newmembercombination);
}
// Exclude header
assertEquals(members.size() - 1, newmembercombinations.size());
} else if (members.isEmpty()) {
newmembercombinations = membercombinations;
assertEquals(membercombinations.size(),
newmembercombinations.size());
} else {
// First is no header
for (List<Node[]> membercombination : membercombinations) {
// First is header
boolean first = true;
for (Node[] member : members) {
if (first) {
first = false;
} else {
List<Node[]> newmembercombination = new ArrayList<Node[]>();
// First
for (Node[] combinedmember : membercombination) {
newmembercombination.add(combinedmember);
}
newmembercombination.add(member);
newmembercombinations.add(newmembercombination);
}
}
}
// Assert that newmembercombinations contain
// |membercombinations|*(|members|-1) elements
assertEquals(membercombinations.size() * (members.size() - 1),
newmembercombinations.size());
}
return newmembercombinations;
}
@SuppressWarnings("unused")
private void assertContains(String seek, String s) {
if (s.indexOf(seek) < 0) {
fail("expected to find '" + seek + "' in '" + s + "'");
}
}
private void executeStatement(LogicalOlapQueryPlan queryplan) {
try {
// For now, we simply return plan
System.out.println("--------------");
System.out.println("Logical plan:" + queryplan.toString());
// Execute query return representation of physical query plan
List<Node[]> result = this.lde.executeOlapQuery(queryplan);
PhysicalOlapQueryPlan execplan = this.lde.getExecplan();
System.out.println("Physical plan:" + execplan.toString());
System.out.println("Result:");
for (Node[] nodes : result) {
for (Node node : nodes) {
System.out.print(node.toString() + "; ");
}
System.out.println();
}
System.out.println("--------------");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Converts a {@link CellSet} to text.
*
* @param cellSet
* Query result
* @param format
* Format
* @return Result as text
*/
static String toString(CellSet cellSet, Format format) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
switch (format) {
case TRADITIONAL:
new TraditionalCellSetFormatter().format(cellSet, pw);
break;
case COMPACT_RECTANGULAR:
case RECTANGULAR:
new RectangularCellSetFormatter(
format == Format.COMPACT_RECTANGULAR).format(cellSet, pw);
break;
}
pw.flush();
return sw.toString();
}
}
// End MetadataTest.java