/* //$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.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.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.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. * * * @version $Id: MetadataTest.java 482 2012-01-05 23:27:27Z jhyde $ */ public class Drill_across_QueryTest extends TestCase { private LinkedDataCubesEngine lde; public Drill_across_QueryTest() throws SQLException { Olap4ldUtil.prepareLogging(); // Logging // For debugging purposes // Olap4ldUtil._log.setLevel(Level.CONFIG); // For monitoring usage Olap4ldUtil._log.setLevel(Level.CONFIG); // For warnings (and errors) only // Olap4ldUtil._log.setLevel(Level.WARNING); 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 { } public void test_Example_GDP_per_capita_in_PPS_vs_Employment_rate_by_sex() throws OlapException { // We query one integrated dataset from GDP per capita dataset and // Employment rate, by sex dataset. // However, the MDX query on that integrated dataset is then transformed // into a // logical query plan that explicitly does drill-across over single // datasets. // First: GDP per capita dataset Node gdpdsuri = new Resource("http://olap4ld.googlecode.com/git/OLAP4LD-trunk/tests/estatwrap/tec00114_ds.rdf#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); 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} List<Node[]> gdpsliceddimensions = new ArrayList<Node[]>(); System.out.println("DIMENSION_UNIQUE_NAMES:"); Map<String, Integer> gdpdimensionmap = Olap4ldLinkedDataUtil .getNodeResultFields(gdpcubedimensions.get(0)); // Header gdpsliceddimensions.add(gdpcubedimensions.get(0)); for (Node[] nodes : gdpcubedimensions) { String dimensionname = nodes[gdpdimensionmap .get("?DIMENSION_UNIQUE_NAME")].toString(); System.out.println(dimensionname); if (dimensionname.equals("http://ontologycentral.com/2009/01/eurostat/ns#aggreg95") || dimensionname.equals("http://ontologycentral.com/2009/01/eurostat/ns#indic_na")) { gdpsliceddimensions.add(nodes); } } // XXX: SliceOp has to make sure that it does not do anything, if: // sliceddimension is empty, contains only one (header), or is null. LogicalOlapOp gdpslice = new SliceOp(gdpbasecube, gdpsliceddimensions); // Roll-up // XXX: We do not need roll-up // Second: Employment rate, by sex dataset Node emplratedsuri = new Resource("http://olap4ld.googlecode.com/git/OLAP4LD-trunk/tests/estatwrap/tsdec420_ds.rdf#ds"); Restrictions emplraterestrictions = new Restrictions(); emplraterestrictions.cubeNamePattern = emplratedsuri; // 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[]> emplratecube = lde.getCubes(emplraterestrictions); assertEquals(2, emplratecube.size()); Map<String, Integer> emplratecubemap = Olap4ldLinkedDataUtil .getNodeResultFields(emplratecube.get(0)); System.out.println("CUBE_NAME: " + emplratecube.get(1)[emplratecubemap.get("?CUBE_NAME")]); List<Node[]> emplratecubemeasures = lde .getMeasures(emplraterestrictions); List<Node[]> emplratecubedimensions = lde .getDimensions(emplraterestrictions); assertEquals(true, emplratecubedimensions.size() > 1); List<Node[]> emplratecubehierarchies = lde .getHierarchies(emplraterestrictions); List<Node[]> emplratecubelevels = lde.getLevels(emplraterestrictions); List<Node[]> emplratecubemembers = lde.getMembers(emplraterestrictions); BaseCubeOp emplratebasecube = new BaseCubeOp(emplratedsuri.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} List<Node[]> emplratesliceddimensions = new ArrayList<Node[]>(); System.out.println("DIMENSION_UNIQUE_NAMES:"); Map<String, Integer> emplratedimensionmap = Olap4ldLinkedDataUtil .getNodeResultFields(emplratecubedimensions.get(0)); // Header emplratesliceddimensions.add(emplratecubedimensions.get(0)); for (Node[] nodes : emplratecubedimensions) { String dimensionname = nodes[emplratedimensionmap .get("?DIMENSION_UNIQUE_NAME")].toString(); System.out.println(dimensionname); if (dimensionname.equals("http://ontologycentral.com/2009/01/eurostat/ns#sex")) { emplratesliceddimensions.add(nodes); } } // XXX: SliceOp has to make sure that it does not do anything, if: // sliceddimension is empty, contains only one (header), or is null. LogicalOlapOp emplrateslice = new SliceOp(emplratebasecube, emplratesliceddimensions); // Roll-up // XXX: We do not need roll-up // Drill-across DrillAcrossOp drillacross = new DrillAcrossOp(gdpslice, emplrateslice); LogicalOlapQueryPlan myplan = new LogicalOlapQueryPlan(drillacross); String result = executeStatement(myplan); assertContains("http://olap4ld.googlecode.com/dic/geo#US; 2011; 70.433333333333333333333333; 148.0; 1; 148.0; 148.0; 148;", result); } 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