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.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.ConvertCubeOp; import org.olap4j.driver.olap4ld.linkeddata.DrillAcrossOp; import org.olap4j.driver.olap4ld.linkeddata.EmbeddedSesameEngine; import org.olap4j.driver.olap4ld.linkeddata.LinkedDataCubesEngine; 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.ReconciliationCorrespondence; import org.olap4j.driver.olap4ld.linkeddata.Restrictions; import org.olap4j.driver.olap4ld.linkeddata.SliceOp; import org.olap4j.layout.RectangularCellSetFormatter; import org.olap4j.layout.TraditionalCellSetFormatter; import org.semanticweb.yars.nx.Node; import org.semanticweb.yars.nx.Resource; /** * Tests on executing drill-across, convert-cube, and merge-cubes operators. * * Seems to need at least: * * -Xms512M -Xmx1524M -XX:PermSize=512M -XX:MaxPermSize=1000M * * @version $Id: MetadataTest.java 482 2012-01-05 23:27:27Z jhyde $ */ public class LDCX_Performance_Evaluation_ConvertCubeTest extends TestCase { private LinkedDataCubesEngine lde; public LDCX_Performance_Evaluation_ConvertCubeTest() throws SQLException { Olap4ldUtil.prepareLogging(); Olap4ldUtil._isDebug = false; 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 { } /** * First try of a pipeline. Using Estatwrap data. Computes GDP per capita. * * @throws OlapException */ public void executeGDP_Per_Capita_Compared() { try { // First: GDP and main components - Current prices dataset Node gdpdsuri = new Resource( "http://estatwrap.ontologycentral.com/id/nama_gdp_c#ds"); Restrictions gdprestrictions = new Restrictions(); gdprestrictions.cubeNamePattern = gdpdsuri; // Base-cube // XXX: We need to make sure that only the lowest members are // queried // from each cube. // In order to fill the engine with data List<Node[]> gdpcube = lde.getCubes(gdprestrictions); assertEquals(2, gdpcube.size()); Map<String, Integer> gdpcubemap = Olap4ldLinkedDataUtil .getNodeResultFields(gdpcube.get(0)); System.out.println("CUBE_NAME: " + gdpcube.get(1)[gdpcubemap.get("?CUBE_NAME")]); List<Node[]> gdpcubemeasures = lde.getMeasures(gdprestrictions); List<Node[]> gdpcubedimensions = lde.getDimensions(gdprestrictions); assertEquals(true, gdpcubedimensions.size() > 1); List<Node[]> gdpcubehierarchies = lde .getHierarchies(gdprestrictions); List<Node[]> gdpcubelevels = lde.getLevels(gdprestrictions); List<Node[]> gdpcubemembers = lde.getMembers(gdprestrictions); // Base-cube BaseCubeOp gdpbasecube = new BaseCubeOp(gdpdsuri.toString()); // Projection // Since we do not remove any measure from the cubes, we do not need // projection. // XXX: We will see how the query processor handles it if no // projection // is called. // Dice // For now, we do not do any dice since we slice over most // dimensions // XXX: However, the results may be wrong, if we do not ensure the // constraint // that base cube only queries for those members that are on the // lowest // level. // Slice // XXX: Since in MDX, we probably cannot distinguish between // dimensions // for specific cubes // {indic_na, sex, age, esa95} // Roll-up // XXX: We do not need roll-up List<Node[]> mio_eur2eur_inputmembers = new ArrayList<Node[]>(); mio_eur2eur_inputmembers .add(new Node[] { new Resource( "http://ontologycentral.com/2009/01/eurostat/ns#unit"), new Resource( "http://estatwrap.ontologycentral.com/dic/unit#MIO_EUR") }); // mio_eur2eur_inputmembers.add(new Node[] {new // Resource("http://purl.org/linked-data/sdmx/2009/measure#obsValue"), // new Variable("value1")}); List<Node[]> mio_eur2eur_outputmembers = new ArrayList<Node[]>(); mio_eur2eur_outputmembers .add(new Node[] { new Resource( "http://ontologycentral.com/2009/01/eurostat/ns#unit"), new Resource( "http://estatwrap.ontologycentral.com/dic/unit#EUR") }); // mio_eur2eur_outputmembers.add(new Node[] {new // Variable("outputcube"), // new // Resource("http://purl.org/linked-data/sdmx/2009/measure#obsValue"), // new Variable("value2")}); String mio_eur2eur_function = "(1000000 * x)"; ReconciliationCorrespondence mio_eur2eur_correspondence = new ReconciliationCorrespondence( "mio_eur2eur", mio_eur2eur_inputmembers, null, mio_eur2eur_outputmembers, mio_eur2eur_function); // Mioeur2eur(dataset): Converting MIO_EUR to EUR in GDP dataset LogicalOlapOp mio_eur2eur_op = new ConvertCubeOp(gdpbasecube, mio_eur2eur_correspondence); // Dice mioeur2eur for B1G. // No dice necessary, directly possible with convert-context // List<Node[]> dicehierarchysignatureB1G = null; // List<List<Node[]>> dicemembercombinationsB1G = null; // LogicalOlapOp b1g = new DiceOp(mioeur2eur, // dicehierarchysignatureB1G, // dicemembercombinationsB1G); // Slice indic_na. // No slice necessary, directly possible with convert-context // List<Node[]> sliceB1G = null; // LogicalOlapOp slicedb1g = new SliceOp(b1g, sliceB1G); // Dice mioeur2eur for D21_M_D31. // No dice necessary, directly possible with convert-context // List<Node[]> dicehierarchysignatureD21_M_D31 = null; // List<List<Node[]>> dicemembercombinationsD21_M_D31 = null; // LogicalOlapOp d21_m_d31 = new DiceOp(mioeur2eur, // dicehierarchysignatureD21_M_D31, // dicemembercombinationsD21_M_D31); // Slice indic_na. // List<Node[]> sliceD21_M_D31 = null; // LogicalOlapOp slicedd21_m_d31 = new SliceOp(d21_m_d31, // sliceD21_M_D31); // Drill-across B1G and D21... // LogicalOlapOp drillacross = new DrillAcrossOp(slicedb1g, // slicedd21_m_d31); // Computing Nominal GDP from single parts in new EUR dataset. // XXX: ComplexMeasureOp // LogicalOlapOp computegdp = new ConvertContextOp(drillacross, 1, // domainUri); List<Node[]> computegdp_inputmembers1 = new ArrayList<Node[]>(); computegdp_inputmembers1 .add(new Node[] { new Resource( "http://ontologycentral.com/2009/01/eurostat/ns#indic_na"), new Resource( "http://estatwrap.ontologycentral.com/dic/indic_na#B1G") }); List<Node[]> computegdp_inputmembers2 = new ArrayList<Node[]>(); computegdp_inputmembers2 .add(new Node[] { new Resource( "http://ontologycentral.com/2009/01/eurostat/ns#indic_na"), new Resource( "http://estatwrap.ontologycentral.com/dic/indic_na#D21_M_D31") }); List<Node[]> computegdp_outputmembers = new ArrayList<Node[]>(); computegdp_outputmembers .add(new Node[] { new Resource( "http://ontologycentral.com/2009/01/eurostat/ns#indic_na"), new Resource( "http://estatwrap.ontologycentral.com/dic/indic_na#NGDP") }); String computegdp_function = "(x1 + x2)"; ReconciliationCorrespondence computegdp_correspondence = new ReconciliationCorrespondence( "computegdp", computegdp_inputmembers1, computegdp_inputmembers2, computegdp_outputmembers, computegdp_function); // String computegdp = "{" + // "?obs1 <http://ontologycentral.com/2009/01/eurostat/ns#indic_na> <http://estatwrap.ontologycentral.com/dic/indic_na#B1G> .\n" // + // "?obs1 <http://purl.org/linked-data/sdmx/2009/measure#obsValue> ?value1 .\n" // + // "?obs2 <http://ontologycentral.com/2009/01/eurostat/ns#indic_na> <http://estatwrap.ontologycentral.com/dic/indic_na#D21_M_D31> .\n" // + // "?obs2 <http://purl.org/linked-data/sdmx/2009/measure#obsValue> ?value2 .\n" // + // "?newvalue <http://www.aifb.kit.edu/project/ld-retriever/qrl#bindas> \"(?value1 + ?value2)\" ." // + // "} => {" + // "_:newobs <http://ontologycentral.com/2009/01/eurostat/ns#indic_na> <http://estatwrap.ontologycentral.com/dic/indic_na#NGDP> .\n" // + // "_:newobs <http://purl.org/linked-data/sdmx/2009/measure#obsValue> ?newvalue " // + // "} . "; // Computing Nominal GDP from single parts in new EUR dataset. LogicalOlapOp computegdp_op = new ConvertCubeOp(mio_eur2eur_op, mio_eur2eur_op, computegdp_correspondence); /* * Should contain: http://estatwrap.ontologycentral.com/dic/geo#UK; * http://estatwrap.ontologycentral.com/dic/indic_na#NGDP; * http://estatwrap.ontologycentral.com/dic/unit#MIO_EUR; 2010; * 1731809.0; */ // XXX Would I need to add to DSD: eurostat:indic_na // dic_indic_na:NGDP; // ? // Second: Population dataset Node populationuri = new Resource( "http://estatwrap.ontologycentral.com/id/demo_pjan#ds"); Restrictions populationrestrictions = new Restrictions(); populationrestrictions.cubeNamePattern = populationuri; // Base-cube // XXX: We need to make sure that only the lowest members are // queried // from each cube. // In order to fill the engine with data // XXX: Should be part of base-cube operator List<Node[]> populationcube = lde.getCubes(populationrestrictions); assertEquals(2, populationcube.size()); Map<String, Integer> populationcubemap = Olap4ldLinkedDataUtil .getNodeResultFields(populationcube.get(0)); System.out .println("CUBE_NAME: " + populationcube.get(1)[populationcubemap .get("?CUBE_NAME")]); List<Node[]> populationcubemeasures = lde .getMeasures(populationrestrictions); List<Node[]> populationcubedimensions = lde .getDimensions(populationrestrictions); assertEquals(true, populationcubedimensions.size() > 1); List<Node[]> populationcubehierarchies = lde .getHierarchies(populationrestrictions); List<Node[]> populationcubelevels = lde .getLevels(populationrestrictions); List<Node[]> populationcubemembers = lde .getMembers(populationrestrictions); BaseCubeOp populationbasecube = new BaseCubeOp( populationuri.toString()); // XXX Would I need to add: Add indicator and unit to population // dataset // Compute "slice" of population that does not use sex and age // dimensions // List<Node[]> slicesexage = null; // LogicalOlapOp slicedsexage = new SliceOp(populationbasecube, // slicesexage); // Drill-across gdp and population dataset // LogicalOlapOp drillacrossgdppopulation = new DrillAcrossOp( // slicedsexage, computegdp); List<Node[]> computegdppercapita_inputmembers1 = new ArrayList<Node[]>(); computegdppercapita_inputmembers1 .add(new Node[] { new Resource( "http://ontologycentral.com/2009/01/eurostat/ns#indic_na"), new Resource( "http://estatwrap.ontologycentral.com/dic/indic_na#NGDP") }); computegdppercapita_inputmembers1 .add(new Node[] { new Resource( "http://ontologycentral.com/2009/01/eurostat/ns#unit"), new Resource( "http://estatwrap.ontologycentral.com/dic/unit#EUR") }); List<Node[]> computegdppercapita_inputmembers2 = new ArrayList<Node[]>(); computegdppercapita_inputmembers2 .add(new Node[] { new Resource( "http://ontologycentral.com/2009/01/eurostat/ns#sex"), new Resource( "http://estatwrap.ontologycentral.com/dic/sex#T") }); computegdppercapita_inputmembers2 .add(new Node[] { new Resource( "http://ontologycentral.com/2009/01/eurostat/ns#age"), new Resource( "http://estatwrap.ontologycentral.com/dic/age#TOTAL") }); List<Node[]> computegdppercapita_outputmembers = new ArrayList<Node[]>(); computegdppercapita_outputmembers .add(new Node[] { new Resource( "http://ontologycentral.com/2009/01/eurostat/ns#indic_na"), new Resource( "http://estatwrap.ontologycentral.com/dic/indic_na#NGDPH") }); computegdppercapita_outputmembers .add(new Node[] { new Resource( "http://ontologycentral.com/2009/01/eurostat/ns#unit"), new Resource( "http://estatwrap.ontologycentral.com/dic/unit#EUR_HAB") }); String computegdppercapita_function = "(x1 / x2)"; ReconciliationCorrespondence computegdppercapita_correspondence = new ReconciliationCorrespondence( "computegdppercapita", computegdppercapita_inputmembers1, computegdppercapita_inputmembers2, computegdppercapita_outputmembers, computegdppercapita_function); // Compute GDP per Capita from GDP and Population // XXX: ComplexMeasureOp LogicalOlapOp computegdppercapita_op = new ConvertCubeOp( computegdp_op, populationbasecube, computegdppercapita_correspondence); // XXX: Complex: Compare with: // http://estatwrap.ontologycentral.com/id/nama_aux_gph#ds // Third: GDP per Capita Node gdpalreadycomputeduri = new Resource( "http://estatwrap.ontologycentral.com/id/nama_aux_gph#ds"); Restrictions gdpalreadycomputedrestrictions = new Restrictions(); gdpalreadycomputedrestrictions.cubeNamePattern = gdpalreadycomputeduri; // Base-cube // XXX: We need to make sure that only the lowest members are // queried // from each cube. // In order to fill the engine with data // XXX: Should be part of base-cube operator List<Node[]> gdpalreadycomputedcube = lde .getCubes(gdpalreadycomputedrestrictions); assertEquals(2, gdpalreadycomputedcube.size()); Map<String, Integer> gdpalreadycomputedcubemap = Olap4ldLinkedDataUtil .getNodeResultFields(gdpalreadycomputedcube.get(0)); System.out.println("CUBE_NAME: " + gdpalreadycomputedcube.get(1)[gdpalreadycomputedcubemap .get("?CUBE_NAME")]); List<Node[]> gdpalreadycomputedcubemeasures = lde .getMeasures(gdpalreadycomputedrestrictions); List<Node[]> gdpalreadycomputedcubedimensions = lde .getDimensions(gdpalreadycomputedrestrictions); assertEquals(true, gdpalreadycomputedcubedimensions.size() > 1); List<Node[]> gdpalreadycomputedcubehierarchies = lde .getHierarchies(gdpalreadycomputedrestrictions); List<Node[]> gdpalreadycomputedcubelevels = lde .getLevels(gdpalreadycomputedrestrictions); List<Node[]> gdpalreadycomputedcubemembers = lde .getMembers(gdpalreadycomputedrestrictions); BaseCubeOp gdpalreadycomputedbasecube = new BaseCubeOp( gdpalreadycomputeduri.toString()); // Projection needed List<Node[]> gdppcprojectedmeasures = new ArrayList<Node[]>(); Map<String, Integer> gdpdimensionmap = Olap4ldLinkedDataUtil .getNodeResultFields(gdpalreadycomputedcubemeasures.get(0)); // Header gdppcprojectedmeasures.add(gdpalreadycomputedcubemeasures.get(0)); for (Node[] nodes : gdpalreadycomputedcubemeasures) { String measurename = nodes[gdpdimensionmap .get("?MEASURE_UNIQUE_NAME")].toString(); System.out.println(measurename); if (measurename .equals("http://purl.org/linked-data/sdmx/2009/measure#obsValue")) { gdppcprojectedmeasures.add(nodes); } } ProjectionOp gdpalreadycomputedprojected = new ProjectionOp( gdpalreadycomputedbasecube, gdppcprojectedmeasures); DrillAcrossOp comparegdppercapita_op = new DrillAcrossOp( computegdppercapita_op, gdpalreadycomputedprojected); LogicalOlapQueryPlan myplan = new LogicalOlapQueryPlan( comparegdppercapita_op); System.out.println("Logical Query Plan:" + myplan.toString()); String result = executeStatement(myplan); // XXX: Apparently the nested-loop join does not work. This may be // because the headers of the outputs are different: /* * [httplodgesisorglodpilotALLBUSvocabrdfgeo, * httpontologycentralcom200901eurostatnsgeo, * httpontologycentralcom200901eurostatnsindicna, * httpontologycentralcom200901eurostatnsunit, * httppurlorgdctermsdate, * httppurlorglinkeddatasdmx2009measureobsValue] * * * [httplodgesisorglodpilotALLBUSvocabrdfgeo0, * httpontologycentralcom200901eurostatnsgeo0, * httpontologycentralcom200901eurostatnsindicna0, * httpontologycentralcom200901eurostatnsunit0, * httppurlorgdctermsdate0, * httppurlorglinkeddatasdmx2009measureobsValue_new] */ assertContains( "http://estatwrap.ontologycentral.com/dic/geo#UK; http://estatwrap.ontologycentral.com/dic/geo#UK; http://estatwrap.ontologycentral.com/dic/indic_na#NGDPH; http://estatwrap.ontologycentral.com/dic/unit#EUR_HAB; 2010; 27704.423967820803 / 27800;", result); } catch (OlapException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void test1() { executeGDP_Per_Capita_Compared(); } public void test2() { executeGDP_Per_Capita_Compared(); } public void test3() { executeGDP_Per_Capita_Compared(); } public void test4() { executeGDP_Per_Capita_Compared(); } public void test5() { executeGDP_Per_Capita_Compared(); } public void test6() { executeGDP_Per_Capita_Compared(); } private void assertContains(String seek, String s) { if (s.indexOf(seek) < 0) { fail("expected to find '" + seek + "' in '" + s + "'"); } } private String executeStatement(LogicalOlapQueryPlan queryplan) { String resultString = ""; 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) { resultString += node.toString() + "; "; System.out.print(node.toString() + "; "); } resultString += "\n"; System.out.println(); } System.out.println("--------------"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } 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