/*
//
//Licensed to Benedikt Kämpgen under one or more contributor license
//agreements. See the NOTICE file distributed with this work for
//additional information regarding copyright ownership.
//
//Benedikt Kämpgen 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.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
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.linkeddata.BaseCubeOp;
import org.olap4j.driver.olap4ld.linkeddata.ConvertCubeOp;
import org.olap4j.driver.olap4ld.linkeddata.EmbeddedSesameEngine;
import org.olap4j.driver.olap4ld.linkeddata.LogicalOlapOp;
import org.olap4j.driver.olap4ld.linkeddata.ReconciliationCorrespondence;
import org.olap4j.layout.RectangularCellSetFormatter;
import org.olap4j.layout.TraditionalCellSetFormatter;
import org.olap4j.test.TestContext;
/**
* Unit test for OLAP4LD Queries on external example data and Drill-Across and
* Convert-Cube and Merge-Cubes operator.
*
* To also help developers with Convert-Cube and Merge-Cubes queries, we provide
* the same as Example_QB_Datasets_QueryTest for those queries.
*
*
* Remember to set test.properties:
* org.olap4j.test.helperClassName=org.olap4j.LdRemoteOlap4jTester
* org.olap4j.RemoteXmlaTester
* .JdbcUrl=jdbc:ld://olap4ld;Catalog=LdCatalog;JdbcDrivers
* =com.mysql.jdbc.Driver
* ;Server=http://;Database=EMBEDDEDSESAME;Datastructuredefinitions=;Datasets=;
*
* @version 0.1
* @author bkaempgen
*/
public class Example_QB_Datasets_ConvertCube_QueryTest extends TestCase {
private final TestContext testContext = TestContext.instance();
private final TestContext.Tester tester = testContext.getTester();
private Connection connection;
private OlapConnection olapConnection;
private OlapStatement stmt;
public Example_QB_Datasets_ConvertCube_QueryTest() throws SQLException {
Olap4ldUtil.prepareLogging();
// Logging
// For debugging purposes
Olap4ldUtil._log.setLevel(Level.CONFIG);
}
protected void setUp() throws SQLException {
connection = tester.createConnection();
connection.getCatalog();
olapConnection = tester.getWrapper().unwrap(connection,
OlapConnection.class);
olapConnection.getMetaData();
// Create a statement based upon the object model.
// One can simply keep open the statement and issue new queries.
OlapConnection olapconnection = (OlapConnection) connection;
this.stmt = null;
try {
stmt = olapconnection.createStatement();
} catch (OlapException e) {
System.out.println("Validation failed: " + e);
return;
}
}
protected void tearDown() throws Exception {
if (olapConnection != null && !olapConnection.isClosed()) {
olapConnection.close();
olapConnection = null;
}
if (connection != null && !connection.isClosed()) {
connection.close();
connection = null;
}
}
// Variations of the UNEMPLOY query
/**
* Specific measures from two datasets.
*
* Here we see that we get three values per cell because due to reasoning we
* have another dimension which we should add to column as well.
*
*/
public void executeDrillAcrossUnemploymentFearAndRealGDPGrowthRateGermany_AutomaticConvert() {
// Maybe ask for specific measure?
// Add another dataset?
// XXX2ChttpXXX3AXXX2FXXX2FestatwrapYYYontologycentralYYYcomXXX2FidXXX2Fnama_aux_gphXXX23ds
String result = executeStatement("SELECT /* $session: ldcx_performance_evaluation_testGdpEmployment */ {[httpXXX3AXXX2FXXX2FpurlYYYorgXXX2FlinkedZZZdataXXX2FsdmxXXX2F2009XXX2FmeasureXXX23obsValue]} ON COLUMNS, CrossJoin(Members([httpXXX3AXXX2FXXX2FpurlYYYorgXXX2FdcXXX2FtermsXXX2Fdate]),Members([httpXXX3AXXX2FXXX2FlodYYYgesisYYYorgXXX2FlodpilotXXX2FALLBUSXXX2FvocabYYYrdfXXX23geo])) ON ROWS FROM [httpXXX3AXXX2FXXX2FlodYYYgesisYYYorgXXX2FlodpilotXXX2FALLBUSXXX2FZA4570v590YYYrdfXXX23dsXXX2ChttpXXX3AXXX2FXXX2FestatwrapYYYontologycentralYYYcomXXX2FidXXX2Ftec00115XXX23ds] WHERE {[httpXXX3AXXX2FXXX2FlodYYYgesisYYYorgXXX2FlodpilotXXX2FALLBUSXXX2FgeoYYYrdfXXX2300]}");
// With both COMP_YES and COMP_PERCNOS,
// Should be correct: GDP Growth 1.1, Answers to survey, COMP_YES,
// Unemployment Fear Metric from COMP_PERCNOS: 0.826; Dice: Germany,
// 2008
// Result as manually computed: Unemployment Fear Metric 0.826
assertContains(
"| | | | 00 | 1144 160 80,1.1,240 240 240,0.826589595375722543352601 0.826589595375722543352601 0.826589595375722543352601 |",
result);
// Result as in ISEM paper: 0.9275 Dice: Germany, 1980
assertContains(
"| | | | 00 | 1126 42 46,88 88 88,0.927512355848434925864909 0.927512355848434925864909 0.927512355848434925864909 |",
result);
}
public void executeDrillAcrossGdpCap_AutomaticConvert() {
// Maybe ask for specific measure?
// Add another dataset?
String result = executeStatement("SELECT /* $session: ldcx_performance_evaluation_testGdpEmployment */ CrossJoin({[httpXXX3AXXX2FXXX2FpurlYYYorgXXX2FlinkedZZZdataXXX2FsdmxXXX2F2009XXX2FmeasureXXX23obsValue]}, Members([httpXXX3AXXX2FXXX2FontologycentralYYYcomXXX2F2009XXX2F01XXX2FeurostatXXX2FnsXXX23indic_na])) ON COLUMNS, CrossJoin(Members([httpXXX3AXXX2FXXX2FpurlYYYorgXXX2FdcXXX2FtermsXXX2Fdate]),Members([httpXXX3AXXX2FXXX2FlodYYYgesisYYYorgXXX2FlodpilotXXX2FALLBUSXXX2FvocabYYYrdfXXX23geo])) ON ROWS FROM [httpXXX3AXXX2FXXX2FestatwrapYYYontologycentralYYYcomXXX2FidXXX2Fnama_aux_gphXXX23dsXXX2ChttpXXX3AXXX2FXXX2FestatwrapYYYontologycentralYYYcomXXX2FidXXX2Fnama_gdp_cXXX23dsXXX2ChttpXXX3AXXX2FXXX2FestatwrapYYYontologycentralYYYcomXXX2FidXXX2Fdemo_pjanXXX23ds] WHERE {[httpXXX3AXXX2FXXX2FlodYYYgesisYYYorgXXX2FlodpilotXXX2FALLBUSXXX2FgeoYYYrdfXXX2300]}");
// Should be correct: | | | | 00 | 28100 104.1 4.1 27300 | 28000 28000
// 103.8 3.8 |
// We distinguish different NGDPH | RGDPH (with different units)
assertContains(
"| | | | 00 | 461.333333333333333333333333,1.1,140.3 |",
result);
}
private void assertContains(String seek, String s) {
if (s.indexOf(seek) < 0) {
fail("expected to find '" + seek + "' in '" + s + "'");
}
}
/**
*
* The ordering of Merge-Cubes is relevant but every Merge-Cube may only be
* used once in a query plan (no cycles).
*
* Implementation for GlobalCube paper: noc(dp,ds,mc)
*
* @param dp
* Depth of query plan
* @param ds
* Number of datasets (converted or not converted)
* @param mc
* Number of merge-cubes
* @return Number of query plans
*/
public static int numberForMergeChildren(int dp, int ds, int mc) {
if (dp == 0) {
return ds;
}
int no = 0;
// Both same depth
no += mc * mc * numberForMergeChildren(dp - 1, ds, mc - 1)
* numberForMergeChildren(dp - 1, ds, mc - 1);
// Left
for (int i = 0; i < dp - 1; i++) {
no += mc * mc * numberForMergeChildren(dp - 1, ds, mc - 1)
* numberForMergeChildren(i, ds, mc - 1);
}
// Right
for (int i = 0; i < dp - 1; i++) {
no += mc * mc * numberForMergeChildren(dp - 1, ds, mc - 1)
* numberForMergeChildren(i, ds, mc - 1);
}
return no;
}
/**
* The ordering of Merge-Cubes is relevant but every Merge-Cube may only be
* used once in a query plan.
*
* XXX: Is not up to date to numberForMergeChildren anymore.
*
* @param depth
* Depth of query plan
* @param datasets
* Number of datasets
* @param correspondences
* Merge
* @return
*/
public static List<LogicalOlapOp> generateMergeLqpsForDepth(int depth,
String[] datasets,
List<ReconciliationCorrespondence> correspondences) {
List<LogicalOlapOp> newlqps = new ArrayList<LogicalOlapOp>();
if (depth == 0) {
for (int i = 0; i < datasets.length; i++) {
newlqps.add(new BaseCubeOp(datasets[i]));
}
return newlqps;
}
// Both same depth.
for (ReconciliationCorrespondence selectedCorrespondence : correspondences) {
List<ReconciliationCorrespondence> reducedcorrespondences = new ArrayList<ReconciliationCorrespondence>();
for (ReconciliationCorrespondence aCorrespondence : correspondences) {
// Only if not correspondence
if (!aCorrespondence.getname().equals(
selectedCorrespondence.getname())) {
reducedcorrespondences.add(aCorrespondence);
}
}
// Here, the same correspondence is not allowed to be used.
List<LogicalOlapOp> nextdepthlqps = generateMergeLqpsForDepth(
depth - 1, datasets, reducedcorrespondences);
for (LogicalOlapOp logicalOlapOp1 : nextdepthlqps) {
for (LogicalOlapOp logicalOlapOp2 : nextdepthlqps) {
newlqps.add(new ConvertCubeOp(logicalOlapOp1,
logicalOlapOp2, selectedCorrespondence));
}
}
// Recursively other depths of the other.
for (int i = 0; i < depth - 1; i++) {
List<LogicalOlapOp> lowerdepthlqps = generateMergeLqpsForDepth(
i, datasets, reducedcorrespondences);
for (LogicalOlapOp logicalOlapOp1 : lowerdepthlqps) {
for (LogicalOlapOp logicalOlapOp2 : nextdepthlqps) {
// Left / Right
newlqps.add(new ConvertCubeOp(logicalOlapOp1,
logicalOlapOp2, selectedCorrespondence));
// Vice-versa
newlqps.add(new ConvertCubeOp(logicalOlapOp2,
logicalOlapOp1, selectedCorrespondence));
}
}
}
}
return newlqps;
}
/**
* The ordering of ccs is relevant but every cc can only be used once in a
* query plan.
*
* @param depth
* @param d
* @param cc
* @return
*/
public static int numberForConvertChildren(int depth, int d, int cc) {
if (depth == 0) {
return d;
}
return cc * numberForConvertChildren(depth - 1, d, cc - 1);
}
/**
* We are interested in whether we can estimate the upper bound of derived
* datasets.
*
*/
public void testExample_QB_Datasets_ConvertCube_QueryTestnumberForMergeChildren() {
System.out.println(Example_QB_Datasets_ConvertCube_QueryTest
.numberForMergeChildren(0, 3, 3));
System.out.println(Example_QB_Datasets_ConvertCube_QueryTest
.numberForMergeChildren(1, 3, 3));
System.out.println(Example_QB_Datasets_ConvertCube_QueryTest
.numberForMergeChildren(2, 3, 3));
System.out.println(Example_QB_Datasets_ConvertCube_QueryTest
.numberForMergeChildren(3, 3, 3));
System.out.println(Example_QB_Datasets_ConvertCube_QueryTest
.numberForMergeChildren(4, 3, 3));
int no = Example_QB_Datasets_ConvertCube_QueryTest
.numberForMergeChildren(0, 3, 3);
assertEquals(3, no);
no = Example_QB_Datasets_ConvertCube_QueryTest.numberForMergeChildren(
1, 3, 3);
assertEquals(81, no);
no = Example_QB_Datasets_ConvertCube_QueryTest.numberForMergeChildren(
2, 3, 3);
assertEquals(13608, no);
no = Example_QB_Datasets_ConvertCube_QueryTest.numberForMergeChildren(
3, 3, 3);
assertEquals(3003480, no);
no = Example_QB_Datasets_ConvertCube_QueryTest.numberForMergeChildren(
4, 3, 3);
assertEquals(0, no);
// Before
// int no =
// Example_QB_Datasets_ConvertCube_QueryTest.numberForMergeChildren(0,
// 3, 2);
// assertEquals(3, no);
//
// no =
// Example_QB_Datasets_ConvertCube_QueryTest.numberForMergeChildren(1,
// 3, 2);
// assertEquals(18, no);
//
// no =
// Example_QB_Datasets_ConvertCube_QueryTest.numberForMergeChildren(2,
// 3, 2);
// assertEquals(864, no);
//
// no =
// Example_QB_Datasets_ConvertCube_QueryTest.numberForMergeChildren(3,
// 3, 2);
// assertEquals(1565568, no);
}
public void testExample_QB_Datasets_ConvertCube_QueryTestgenerateMergeLqpsForDepth() {
List<ReconciliationCorrespondence> correspondences = EmbeddedSesameEngine
.getReconciliationCorrespondences(true);
String[] datasets = {
"http://estatwrap.ontologycentral.com/id/tec00115#ds",
"http://lod.gesis.org/lodpilot/ALLBUS/ZA4570v590.rdf#ds",
"http://estatwrap.ontologycentral.com/id/nama_aux_gph#ds" };
List<LogicalOlapOp> lqps = Example_QB_Datasets_ConvertCube_QueryTest
.generateMergeLqpsForDepth(0, datasets, correspondences);
assertEquals(3, lqps.size());
lqps = Example_QB_Datasets_ConvertCube_QueryTest
.generateMergeLqpsForDepth(1, datasets, correspondences);
assertEquals(18, lqps.size());
lqps = Example_QB_Datasets_ConvertCube_QueryTest
.generateMergeLqpsForDepth(2, datasets, correspondences);
assertEquals(270, lqps.size());
lqps = Example_QB_Datasets_ConvertCube_QueryTest
.generateMergeLqpsForDepth(3, datasets, correspondences);
assertEquals(0, lqps.size());
// Before
// List<LogicalOlapOp> lqps =
// Example_QB_Datasets_ConvertCube_QueryTest.generateMergeLqpsForDepth(0,
// datasets, correspondences);
// assertEquals(3, lqps.size());
//
// lqps =
// Example_QB_Datasets_ConvertCube_QueryTest.generateMergeLqpsForDepth(1,
// datasets, correspondences);
// assertEquals(18, lqps.size());
//
// lqps =
// Example_QB_Datasets_ConvertCube_QueryTest.generateMergeLqpsForDepth(2,
// datasets, correspondences);
// assertEquals(864, lqps.size());
//
// lqps =
// Example_QB_Datasets_ConvertCube_QueryTest.generateMergeLqpsForDepth(3,
// datasets, correspondences);
// assertEquals(1565568, lqps.size());
}
public void testExample_QB_Datasets_ConvertCube_QueryTestnumberForConvertChildren() {
// Case for one
int no = Example_QB_Datasets_ConvertCube_QueryTest
.numberForConvertChildren(0, 3, 1);
assertEquals(3, no);
no = Example_QB_Datasets_ConvertCube_QueryTest
.numberForConvertChildren(1, 3, 1);
assertEquals(3, no);
no = Example_QB_Datasets_ConvertCube_QueryTest
.numberForConvertChildren(2, 3, 1);
assertEquals(0, no);
// Case for two
no = Example_QB_Datasets_ConvertCube_QueryTest
.numberForConvertChildren(0, 3, 2);
assertEquals(3, no);
no = Example_QB_Datasets_ConvertCube_QueryTest
.numberForConvertChildren(1, 3, 2);
assertEquals(6, no);
no = Example_QB_Datasets_ConvertCube_QueryTest
.numberForConvertChildren(2, 3, 2);
assertEquals(6, no);
no = Example_QB_Datasets_ConvertCube_QueryTest
.numberForConvertChildren(3, 3, 2);
assertEquals(0, no);
}
private String executeStatement(String mdxString) {
// Execute the statement.
String resultString = "";
CellSet cset;
try {
cset = stmt.executeOlapQuery(mdxString);
// String s = TestContext.toString(cset);
resultString = toString(cset, Format.RECTANGULAR);
System.out.println("Output:");
System.out.println(resultString);
} catch (OlapException e) {
System.out.println("Execution failed: " + e);
}
return resultString;
}
/**
* 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