/* // // 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.driver.olap4ld.linkeddata; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.olap4j.OlapException; import org.olap4j.Position; import org.olap4j.driver.olap4ld.Olap4ldUtil; import org.olap4j.driver.olap4ld.helper.Olap4ldLinkedDataUtil; import org.olap4j.metadata.Cube; import org.olap4j.metadata.Level; import org.olap4j.metadata.Measure; import org.semanticweb.yars.nx.Literal; import org.semanticweb.yars.nx.Node; import org.semanticweb.yars.nx.Resource; import org.semanticweb.yars.nx.Variable; import org.semanticweb.yars.nx.parser.NxParser; /** * The OpenVirtuosoEngine uses an Open Virtuoso SPARQL endpoint for executing * metadata or olap queries. * * @author b-kaempgen * */ public class OpenVirtuosoEngine implements LinkedDataCubesEngine { /** * The type of SPARQL endpoint should be found out automatically and with * the server string */ private static String SPARQLSERVERURL; /* * Open Virtuoso is default triple store */ private int SPARQLSERVERTYPE = 2; private static final int QCRUMB = 1; private static final int OPENVIRTUOSO = 2; private static final int OPENVIRTUOSORULESET = 3; // Meta data attributes private static final String DATASOURCEDESCRIPTION = "OLAP data from the statistical Linked Data cloud."; private static final String PROVIDERNAME = "The community."; private static String URL; private static final String DATASOURCEINFO = "Data following the Linked Data principles."; private static final String TABLE_CAT = "LdCatalogSchema"; private static final String TABLE_SCHEM = "LdCatalogSchema"; public String DATASOURCENAME; public String DATASOURCEVERSION; // Each typical sparql query assumes the following prefixes. public String TYPICALPREFIXES = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> PREFIX skos: <http://www.w3.org/2004/02/skos/core#> PREFIX qb: <http://purl.org/linked-data/cube#> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX owl: <http://www.w3.org/2002/07/owl#> "; // Helper attributes /** * Map of locations that have been loaded into the embedded triple store. */ private HashMap<Integer, Boolean> loadedMap = new HashMap<Integer, Boolean>(); private Integer MAX_LOAD_TRIPLE_SIZE = 1000000000; private Integer MAX_COMPLEX_CONSTRAINTS_TRIPLE_SIZE = 5000; private Integer LOADED_TRIPLE_SIZE = 0; private PhysicalOlapQueryPlan execplan; private HashMap<Integer, List<Node[]>> sparqlResultMap; // Not needed any more since we use materialisation. // private List<List<Node>> equivalenceList; public OpenVirtuosoEngine(URL serverUrlObject, List<String> datastructuredefinitions, List<String> datasets, String databasename) throws OlapException { // That is the SPARQL engine URL URL = serverUrlObject.toString(); if (databasename.equals("OPENVIRTUOSO")) { SPARQLSERVERTYPE = OPENVIRTUOSO; DATASOURCENAME = databasename; DATASOURCEVERSION = "1.0"; } if (databasename.equals("OPENVIRTUOSORULESET")) { SPARQLSERVERTYPE = OPENVIRTUOSORULESET; DATASOURCENAME = databasename; DATASOURCEVERSION = "1.0"; } // Set URL SPARQLSERVERURL = URL.toString(); initialize(); } private PhysicalOlapQueryPlan createExecplan(LogicalOlapQueryPlan queryplan) throws OlapException { /* * Currently, I distinguish: * * Drill-Across for queries with Drill-Across at the top, OLAP-to-SPARQL * queries below. * * DerivedDataset for queries with Convert-Cube or BaseCubeOp. * * OLAP-to-SPARQL for queries with OLAP-to-SPARQL queries * * I want to have Drill-Across for queries with Drill-Across at the top, * OLAP-to-SPARQL queries below, and Convert-Cube or Merge-Cubes at the * end. */ LogicalToPhysical logicaltophysical = new LogicalToPhysical(this); PhysicalOlapIterator newRoot; // Transform into physical query plan newRoot = (PhysicalOlapIterator) logicaltophysical .compile(queryplan._root); PhysicalOlapQueryPlan execplan = new PhysicalOlapQueryPlan(newRoot); return execplan; } public PhysicalOlapQueryPlan getExecplan() { return this.execplan; } /** * For OPENVIRTUOSO, no specific initialisation is needed. */ private void initialize() { ; } /** * We now implement the pre-processing pipeline that shall result in a fully * integrated database (triple store, data warehouse). (Cal, A., Calvanese, * D., Giacomo, G. De, & Lenzerini, M. (2002). Data Integration under * Integrity Constraints, 262–279.) * * @throws * @throws OlapException */ private void preload() throws OlapException { // Not needed here. } /** * Returns from String in order to retrieve information about these URIs * * * Properly access the triple store: For dsd and ds we query separate * graphs. * * @param uris * @return fromResult */ @Deprecated private String askForFrom(boolean isDsdQuery) { return ""; } public void executeSparqlConstructQuery(String constructquery) { // Not needed. } /** * I think, caching some sparql results would be very useful. * * I create a map between hash value of sparql query and the Nodes. * * If the value is available, I return it. * * However, when to empty the cache? I empty the cache if I populate a new * cube. * * @param query * @param caching * (not used) * @return */ public List<Node[]> executeSparqlSelectQuery(String query, boolean caching) { Olap4ldUtil._log.config("SPARQL query: " + query); // XXX: Caching false caching = false; Integer hash = null; if (caching) { hash = query.hashCode(); } if (caching) { if (this.sparqlResultMap.containsKey(hash)) { return this.sparqlResultMap.get(hash); } } Olap4ldUtil._log.config("SPARQL query: " + query); List<Node[]> result; switch (SPARQLSERVERTYPE) { case QCRUMB: //result = sparqlQcrumb(query); result = null; break; case OPENVIRTUOSO: result = sparqlOpenVirtuoso(query); break; case OPENVIRTUOSORULESET: //result = sparqlOpenVirtuosoRuleset(query); result = null; break; default: result = null; break; } if (caching) { if (this.sparqlResultMap != null && !this.sparqlResultMap.containsKey(hash)) { this.sparqlResultMap.put(hash, result); } else { // TODO: Problem: If this method is run simultaneously, it could // be that this happens. // throw new UnsupportedOperationException( // "Hash key should not be contained."); } } return result; } private List<Node[]> sparqlOpenVirtuoso(String query) { List<Node[]> myBindings = new ArrayList<Node[]>(); try { // Better? ISO-8859-1 String querysuffix = "?query=" + URLEncoder.encode(query, "UTF-8"); /* * Curly brackets should not be encoded, says qcrumb * * querysuffix = querysuffix.replaceAll("%7B", "{"); querysuffix = * querysuffix.replaceAll("%7D", "}"); * * Stimmt nicht: Das Problem ist NX-Darstellung und leere bindings. * * Momentan wird dazu einfach <null> verwendet. */ // String acceptsuffix = "&accept=text%2Fn3"; String fullurl = SPARQLSERVERURL + querysuffix; // String fullurl = // "http://qcrumb.com/sparql?query=PREFIX+sdmx-measure%3A+<http%3A%2F%2Fpurl.org%2Flinked-data%2Fsdmx%2F2009%2Fmeasure%23>%0D%0APREFIX+dcterms%3A+<http%3A%2F%2Fpurl.org%2Fdc%2Fterms%2F>%0D%0APREFIX+eus%3A+<http%3A%2F%2Fontologycentral.com%2F2009%2F01%2Feurostat%2Fns%23>%0D%0APREFIX+rdf%3A+<http%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23>%0D%0APREFIX+qb%3A+<http%3A%2F%2Fpurl.org%2Flinked-data%2Fcube%23>%0D%0APREFIX+rdfs%3A+<http%3A%2F%2Fwww.w3.org%2F2000%2F01%2Frdf-schema%23>%0D%0A%0D%0ASELECT+%3Ftime+%3Fvalue+%3Fgeo%0D%0AFROM+<http%3A%2F%2Festatwrap.ontologycentral.com%2Fdata%2Ftsieb020>%0D%0AFROM+<http%3A%2F%2Festatwrap.ontologycentral.com%2Fdic%2Fgeo>%0D%0AWHERE+{%0D%0A++%3Fs+qb%3Adataset+<http%3A%2F%2Festatwrap.ontologycentral.com%2Fid%2Ftsieb020%23ds>+.%0D%0A++%3Fs+dcterms%3Adate+%3Ftime+.%0D%0A++%3Fs+eus%3Ageo+%3Fg+.%0D%0A++%3Fg+rdfs%3Alabel+%3Fgeo+.%0D%0A++%3Fs+sdmx-measure%3AobsValue+%3Fvalue+.%0D%0A++FILTER+(lang(%3Fgeo)+%3D+\"\")%0D%0A}+ORDER+BY+%3Fgeo%0D%0A&rules=&accept=application%2Fsparql-results%2Bjson" HttpURLConnection con = (HttpURLConnection) new URL(fullurl) .openConnection(); // TODO: How to properly set header? Does not work therefore // manually // added // con.setRequestProperty("Accept", // "application/sparql-results+json"); // con.setRequestProperty("Accept", "text/n3"); con.setRequestProperty("Accept", "application/sparql-results+xml"); con.setRequestMethod("POST"); if (con.getResponseCode() != 200) { throw new RuntimeException("lookup on " + fullurl + " resulted HTTP in status code " + con.getResponseCode()); } // InputStream inputStream = con.getInputStream(); // String test = XmlaOlap4jUtil.convertStreamToString(inputStream); // _log.config("XML output: " + test); // Transform sparql xml to nx InputStream nx = Olap4ldLinkedDataUtil.transformSparqlXmlToNx(con .getInputStream()); String test2 = Olap4ldLinkedDataUtil.convertStreamToString(nx); Olap4ldUtil._log.config("NX output: " + test2); nx.reset(); NxParser nxp = new NxParser(nx); Node[] nxx; while (nxp.hasNext()) { try { nxx = nxp.next(); myBindings.add(nxx); } catch (Exception e) { Olap4ldUtil._log .warning("NxParser: Could not parse properly: " + e.getMessage()); } ; } } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return myBindings; } public boolean isLoaded(URL resource) { if (loadedMap.get(resource.toString().hashCode()) != null && loadedMap.get(resource.toString().hashCode()) == true) { Olap4ldUtil._log.info("Is loaded: " + resource.toString() + ", Hash: " + resource.toString().hashCode()); return true; } else { Olap4ldUtil._log.info("Is not yet loaded: " + resource.toString() + ", Hash: " + resource.toString().hashCode()); return false; } } public void setLoaded(URL resource) { Olap4ldUtil._log.info("Set loaded: " + resource.toString() + ", Hash: " + resource.toString().hashCode()); loadedMap.put(resource.toString().hashCode(), true); } /** * Loads resource in store if 1) URI and location of resource not already * loaded 2) number of triples has not reached maximum. * * @param location * @throws OlapException */ private void loadInStore(URL noninformationuri) throws OlapException { // Not needed. } /** * We load all data for a cube. We also normalise and do integrity checks. * * @param location */ private void loadCube(URL noninformationuri) throws OlapException { // Not needed. } public static List<ReconciliationCorrespondence> getReconciliationCorrespondences( boolean askForMergeCorrespondences) { List<ReconciliationCorrespondence> correspondences = new ArrayList<ReconciliationCorrespondence>(); // // MIO2EUR // 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( // "MIO2EUR", mio_eur2eur_inputmembers, null, // mio_eur2eur_outputmembers, mio_eur2eur_function); // if (!askForMergeCorrespondences) { // correspondences.add(mio_eur2eur_correspondence); // } // // // COMPUTE_GDP // // 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( // "COMP_GDP", computegdp_inputmembers1, computegdp_inputmembers2, // computegdp_outputmembers, computegdp_function); // if (askForMergeCorrespondences) { // correspondences.add(computegdp_correspondence); // } // // // COMPUTE_GDP_PER_CAPITA // // 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( // "COMP_GDP_CAP", computegdppercapita_inputmembers1, // computegdppercapita_inputmembers2, // computegdppercapita_outputmembers, computegdppercapita_function); // if (askForMergeCorrespondences) { // correspondences.add(computegdppercapita_correspondence); // } // COMPUTE_YES // ReconciliationCorrespondence computeyes_correspondence; // List<Node[]> computeyes_inputmembers1 = new ArrayList<Node[]>(); // computeyes_inputmembers1 // .add(new Node[] { // new Resource( // "http://lod.gesis.org/lodpilot/ALLBUS/vocab.rdf#variable"), // new Resource( // "http://lod.gesis.org/lodpilot/ALLBUS/variable.rdf#v590_2") }); // // List<Node[]> computeyes_inputmembers2 = new ArrayList<Node[]>(); // computeyes_inputmembers2 // .add(new Node[] { // new Resource( // "http://lod.gesis.org/lodpilot/ALLBUS/vocab.rdf#variable"), // new Resource( // "http://lod.gesis.org/lodpilot/ALLBUS/variable.rdf#v590_3") }); // // List<Node[]> computeyes_outputmembers = new ArrayList<Node[]>(); // computeyes_outputmembers // .add(new Node[] { // new Resource( // "http://lod.gesis.org/lodpilot/ALLBUS/vocab.rdf#variable"), // new Resource( // "http://lod.gesis.org/lodpilot/ALLBUS/variable.rdf#v590_2+3") }); // // String computeyes_function = "(x1 + x2)"; // // computeyes_correspondence = new ReconciliationCorrespondence( // "COMP_YES", computeyes_inputmembers1, computeyes_inputmembers2, // computeyes_outputmembers, computeyes_function); // // if (askForMergeCorrespondences) { // correspondences.add(computeyes_correspondence); // } // COMPUTE\_PERCENTAGENOS // ReconciliationCorrespondence computepercentagenos_correspondence; // List<Node[]> computepercentagenos_inputmembers1 = new // ArrayList<Node[]>(); // computepercentagenos_inputmembers1 // .add(new Node[] { // new Resource( // "http://lod.gesis.org/lodpilot/ALLBUS/vocab.rdf#variable"), // new Resource( // "http://lod.gesis.org/lodpilot/ALLBUS/variable.rdf#v590_1") }); // // List<Node[]> computepercentagenos_inputmembers2 = new // ArrayList<Node[]>(); // computepercentagenos_inputmembers2 // .add(new Node[] { // new Resource( // "http://lod.gesis.org/lodpilot/ALLBUS/vocab.rdf#variable"), // new Resource( // "http://lod.gesis.org/lodpilot/ALLBUS/variable.rdf#v590_2+3") }); // // List<Node[]> computepercentagenos_outputmembers = new // ArrayList<Node[]>(); // computepercentagenos_outputmembers // .add(new Node[] { // new Resource( // "http://lod.gesis.org/lodpilot/ALLBUS/vocab.rdf#variable"), // new Resource( // "http://lod.gesis.org/lodpilot/ALLBUS/variable.rdf#v590_2+3") }); // // Not yet needed since manual drill-across: // // computepercentagenos_outputmembers // // .add(new Node[] { // // new Resource( // // "http://ontologycentral.com/2009/01/eurostat/ns#indic_na"), // // new Resource( // // "http://estatwrap.ontologycentral.com/dic/indic_na#RGDPG") }); // // String computepercentagenos_function = "(x1 / (x1 + x2))"; // // computepercentagenos_correspondence = new // ReconciliationCorrespondence( // "COMP_PERCNOS", computepercentagenos_inputmembers1, // computepercentagenos_inputmembers2, // computepercentagenos_outputmembers, // computepercentagenos_function); // // if (askForMergeCorrespondences) { // correspondences.add(computepercentagenos_correspondence); // } return correspondences; } /** * Check whether we query for "Measures". * * @param dimensionUniqueName * @param hierarchyUniqueName * @param levelUniqueName * @return */ private boolean isMeasureQueriedForExplicitly(Node dimensionUniqueName, Node hierarchyUniqueName, Node levelUniqueName) { // If one is set, it should not be Measures, not. // Watch out: no square brackets are needed. boolean explicitlyStated = (dimensionUniqueName != null && dimensionUniqueName .toString() .equals(Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) || (hierarchyUniqueName != null && hierarchyUniqueName .toString().equals( Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) || (levelUniqueName != null && levelUniqueName.toString() .equals(Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)); return explicitlyStated; } /** * * @return */ public List<Node[]> getDatabases(Restrictions restrictions) { /* * DISCOVER_DATASOURCES(new MetadataColumn("DataSourceName"), new * MetadataColumn("DataSourceDescription"), new MetadataColumn("URL"), * new MetadataColumn("DataSourceInfo"), new MetadataColumn( * "ProviderName"), new MetadataColumn("ProviderType"), new * MetadataColumn("AuthenticationMode")), */ List<Node[]> results = new ArrayList<Node[]>(); Node[] bindingNames = new Node[] { new Variable("?DATA_SOURCE_NAME"), new Variable("?DATA_SOURCE_DESCRIPTION"), new Variable("?PROVIDER_NAME"), new Variable("?URL"), new Variable("?DATA_SOURCE_INFO") }; results.add(bindingNames); Node[] triple = new Node[] { new Literal(DATASOURCENAME), new Literal(DATASOURCEDESCRIPTION), new Literal(PROVIDERNAME), new Literal(URL), new Literal(DATASOURCEINFO) }; results.add(triple); return results; } public List<Node[]> getCatalogs(Restrictions restrictions) { /* * DBSCHEMA_CATALOGS( new MetadataColumn("CATALOG_NAME"), new * MetadataColumn( "DESCRIPTION"), new MetadataColumn("ROLES"), new * MetadataColumn("DATE_MODIFIED")) */ List<Node[]> results = new ArrayList<Node[]>(); Node[] bindingNames = new Node[] { new Variable("?TABLE_CAT") }; results.add(bindingNames); Node[] triple = new Node[] { new Literal(TABLE_CAT) }; results.add(triple); return results; } /** * * @return */ public List<Node[]> getSchemas(Restrictions restrictions) { List<Node[]> results = new ArrayList<Node[]>(); /* * DBSCHEMA_SCHEMATA(new MetadataColumn( "CATALOG_NAME"), new * MetadataColumn("SCHEMA_NAME"), new MetadataColumn("SCHEMA_OWNER")) */ Node[] bindingNames = new Node[] { new Variable("?TABLE_SCHEM"), new Variable("?TABLE_CAT") }; results.add(bindingNames); Node[] triple = new Node[] { new Literal(TABLE_SCHEM), new Literal(TABLE_CAT), // No owner new Literal("") }; results.add(triple); return results; } /** * * Get Cubes from the triple store. * * Here, the restrictions are strict restrictions without patterns. * * This is both called for metadata queries and OLAP queries. * * @return Node[]{} */ public List<Node[]> getCubes(Restrictions restrictions) throws OlapException { Olap4ldUtil._log.config("Linked Data Engine: Get Cubes..."); // I once preloaded some data. // We now assume that we can also query for a global dataset identified // by a // comma separated list of datasets. For now, since we are interested in // all possible // derived datasets from a set of datasets. // Yet, we probably should either start with an MDX query or an Logical // Operator Tree // defining an information need. Although we might also just be // interested in all // possible derived datasets of a set of datasets. try { preload(); } catch (OlapException e) { // TODO Auto-generated catch block e.printStackTrace(); } List<Node[]> result = new ArrayList<Node[]>(); // Check whether Drill-across query // XXX: Wildcard delimiter if (restrictions.cubeNamePattern != null) { String[] datasets = restrictions.cubeNamePattern.toString().split( ","); Olap4ldUtil._log.info("Load dataset: " + datasets.length + " datasets crawled."); for (int i = 0; i < datasets.length; i++) { String dataset = datasets[i]; Restrictions newrestrictions = new Restrictions(); newrestrictions.cubeNamePattern = new Resource(dataset); List<Node[]> intermediaryresult = getCubesPerDataSet(newrestrictions); // Add to result boolean first = true; for (Node[] nodes : intermediaryresult) { if (first) { if (i == 0) { result.add(nodes); } first = false; continue; } // We do not want to have the single datasets returned. // We always have only one cube, the global cube. // result.add(nodes); } } } else { result = getCubesPerDataSet(restrictions); } /* * Now that we have loaded all cube, we need to implement entity * consolidation. * * We create an equivalence table. Then, for each dimension unique name, * we have one equivalence class. Then we can do as before. */ // List<Node[]> myresult = sparql(querytemplate, true); // // Add all of result2 to result // boolean first = true; // for (Node[] nodes : myresult) { // if (first) { // first = false; // continue; // } // result.add(nodes); // } // We do not do that anymore but use materialisation. // Now that we have loaded all data cubes, we can compute the // equivalence list. /* * Load equivalence statements from triple store */ // Olap4ldUtil._log.info("Load dataset: create equivalence list started."); // long time = System.currentTimeMillis(); // // List<Node[]> equivs = getEquivalenceStatements(); // // this.equivalenceList = createEquivalenceList(equivs); // // time = System.currentTimeMillis() - time; // Olap4ldUtil._log // .info("Load dataset: create equivalence list finished in " // + time + "ms."); // Now, add "virtual cube" // ?CATALOG_NAME ?SCHEMA_NAME ?CUBE_NAME ?CUBE_TYPE ?CUBE_CAPTION // ?DESCRIPTION String globalcubename = ""; if (restrictions.cubeNamePattern == null) { Map<String, Integer> cubemap = Olap4ldLinkedDataUtil .getNodeResultFields(result.get(0)); // Concatenate all cubes. boolean first = true; for (Node[] nodes : result) { if (first) { // First header; first = false; continue; } if (!globalcubename.equals("")) { globalcubename += ","; } globalcubename += nodes[cubemap.get("?CUBE_NAME")].toString(); } } else { globalcubename = restrictions.cubeNamePattern.toString(); } // XXX: The virtual cube should actually not be given to users. Users // simply issue queries over available datasets. Node[] virtualcube = new Node[] { new Literal(TABLE_CAT), new Literal(TABLE_SCHEM), new Resource(globalcubename), new Literal("CUBE"), new Literal("Global Cube"), new Literal("This is the global cube.") }; result.add(virtualcube); Olap4ldUtil._log .info("Load datasets: Number of loaded triples for all datasets: " + this.LOADED_TRIPLE_SIZE); // Check max loaded String query = "PREFIX qb: <http://purl.org/linked-data/cube#> select (count(?s) as ?count) where {?s qb:dataSet ?ds}"; List<Node[]> countobservationsresult = executeSparqlSelectQuery(query, false); Integer countobservation = new Integer( countobservationsresult.get(1)[0].toString()); Olap4ldUtil._log .info("Load datasets: Number of observations for all datasets: " + countobservation); /* * Check on restrictions that the interface makes: * * Restrictions are strong restrictions, no fuzzy, since those wild * cards have been eliminated before. */ // List<Node[]> result = applyRestrictions(cubeUris, restrictions); return result; } private List<Node[]> getCubesPerDataSet(Restrictions restrictions) throws OlapException { List<Node[]> result = new ArrayList<Node[]>(); // Before loading, I should check first, whether already loaded. URL noninformationuri; try { if (restrictions.cubeNamePattern == null) { // There is nothing to load Olap4ldUtil._log .config("If no cubeNamePattern is given, we cannot load a cube."); } else { noninformationuri = new URL( restrictions.cubeNamePattern.toString()); URL informationuri = Olap4ldLinkedDataUtil .askForLocation(noninformationuri); if (!isLoaded(noninformationuri) || !isLoaded(informationuri)) { // For now, we simply preload. loadCube(noninformationuri); } setLoaded(noninformationuri); setLoaded(informationuri); } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } String additionalFilters = createFilterForRestrictions(restrictions); String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getCubes_regular.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); result = executeSparqlSelectQuery(querytemplate, true); return result; } private void checkIntegrityConstraints() throws OlapException { // Possibly needed later. } /** * According to QB specification, a cube may be provided in abbreviated form * so that inferences first have to be materialised to properly query a * cube. * * @throws OlapException */ public void runNormalizationAlgorithm() throws OlapException { // Not needed. } /** * Returns canonical for a node. * * If none is found, simply the node is returned. * * @param canonical * @return */ @SuppressWarnings("unused") @Deprecated private Node getCanonical(Node canonical) { // for (List<Node> equivalenceClass : equivalenceList) { // for (Node node : equivalenceClass) { // // if (node.equals(canonical)) { // canonical = equivalenceClass.get(0); // break; // } // } // } // return canonical; return null; } /** * * @param equivs * - equiv[0] first same as and equiv[1] second same as entity. * @return */ @SuppressWarnings("unused") @Deprecated private List<List<Node>> createEquivalenceList(List<Node[]> equivs) { List<List<Node>> newequivalenceList = new ArrayList<List<Node>>(); // HashMap<String, Integer> invertedindex = new HashMap<String, // Integer>(); // // boolean first = true; // for (Node[] equiv : equivs) { // // // First is header // if (first) { // first = false; // continue; // } // // String A = equiv[0].toString(); // String B = equiv[1].toString(); // // // Store equiv // // // Find A // Integer rA = invertedindex.get(A); // // // Find B // Integer rB = invertedindex.get(B); // // if (rA == null && rB == null) { // // new row rAB // // // ArrayList<Node> newEquivalenceClass = new ArrayList<Node>(); // newEquivalenceClass.add(A); // newEquivalenceClass.add(B); // newequivalenceList.add(newEquivalenceClass); // // } else if (rA != null && rB != null) { // // merge rA and rB // // // We create new list // // List<Node> rAB = new ArrayList<Node>(); // // for (Node node : rB) { // rAB.add(node); // } // for (Node node : rA) { // rAB.add(node); // } // newequivalenceList.remove(rB); // newequivalenceList.remove(rA); // newequivalenceList.add(rAB); // // } else if (rA != null) { // // add B to rA // // rA.add(B); // // } else if (rB != null) { // // add A to rB // // rB.add(A); // } // // } return newequivalenceList; } @Deprecated private List<Node> getEquivalenceClassOfNode(Node resource) { // List<Node> equivalenceClass = new ArrayList<Node>(); // equivalenceClass.add(resource); // for (List<Node> iterable_element : this.equivalenceList) { // for (Node node : iterable_element) { // if (resource.equals(node)) { // equivalenceClass = iterable_element; // break; // } // } // } // return equivalenceClass; return null; } @Deprecated public List<Node[]> getEquivalenceStatements() { // /* // * More directed better? // * // * {$this->getStandardPrefixes()} select ?same1 ?same2 // * {$this->getStandardFrom()} where { ?dsd qb:component ?comp. ?comp // * ?componentProp ?same1. ?same1 owl:sameAs ?same2 } // */ // // // Same as between dimensions and members. // String query = // "PREFIX owl: <http://www.w3.org/2002/07/owl#> PREFIX qb: <http://purl.org/linked-data/cube#> select ?same1 ?same2 where " // + // "{ { ?dsd qb:component ?comp. ?comp ?componentProp ?same1. ?same1 owl:sameAs ?same2 } UNION { ?obs a qb:Observation. ?obs ?dimension ?same1. ?same1 owl:sameAs ?same2 } } "; // // List<Node[]> myresult = sparql(query, true); // // Olap4ldUtil._log.info("Number of equivalence statements: " // + (myresult.size() - 1)); // // return myresult; return null; } /** * Get possible dimensions (component properties) for each cube from the * triple store. * * Approach: I create the output from Linked Data, and then I filter it * using the restrictions. * * I have to also return the Measures dimension for each cube. * * @return Node[]{?dsd ?dimension ?compPropType ?name} * @throws MalformedURLException */ public List<Node[]> getDimensions(Restrictions restrictions) throws OlapException { Olap4ldUtil._log.config("Linked Data Engine: Get Dimensions..."); List<Node[]> result = new ArrayList<Node[]>(); // Check whether Drill-across query // XXX: Wildcard delimiter if (restrictions.cubeNamePattern != null) { String[] datasets = restrictions.cubeNamePattern.toString().split( ","); for (int i = 0; i < datasets.length; i++) { String dataset = datasets[i]; // Should make sure that the full restrictions are used. Node saverestrictioncubePattern = restrictions.cubeNamePattern; restrictions.cubeNamePattern = new Resource(dataset); List<Node[]> intermediaryresult = getDimensionsPerDataSet(restrictions); restrictions.cubeNamePattern = saverestrictioncubePattern; // Add to result boolean first = true; for (Node[] anIntermediaryresult : intermediaryresult) { if (first) { if (i == 0) { result.add(anIntermediaryresult); } first = false; continue; } // Add the single dimensions of the datasets to be // transformed with createGlobalDimensions. result.add(anIntermediaryresult); } } } else { result = getDimensionsPerDataSet(restrictions); } // Create global cube which is intersection of all dimensions and new // cube name return createGlobalDimensions(restrictions, result); } private List<Node[]> createGlobalDimensions(Restrictions restrictions, List<Node[]> intermediaryresult) { List<Node[]> result = new ArrayList<Node[]>(); Map<String, Integer> dimensionmap = Olap4ldLinkedDataUtil .getNodeResultFields(intermediaryresult.get(0)); // Add to result boolean first = true; for (Node[] anIntermediaryresult : intermediaryresult) { if (first) { first = false; result.add(anIntermediaryresult); continue; } // Also add dimension to global cube Node[] newnode = new Node[9]; newnode[dimensionmap.get("?CATALOG_NAME")] = anIntermediaryresult[dimensionmap .get("?CATALOG_NAME")]; newnode[dimensionmap.get("?SCHEMA_NAME")] = anIntermediaryresult[dimensionmap .get("?SCHEMA_NAME")]; // New cube name of global cube if (restrictions.cubeNamePattern == null) { newnode[dimensionmap.get("?CUBE_NAME")] = anIntermediaryresult[dimensionmap .get("?CUBE_NAME")]; } else { newnode[dimensionmap.get("?CUBE_NAME")] = restrictions.cubeNamePattern; } newnode[dimensionmap.get("?DIMENSION_NAME")] = anIntermediaryresult[dimensionmap .get("?DIMENSION_NAME")]; // Needs to be canonical name newnode[dimensionmap.get("?DIMENSION_UNIQUE_NAME")] = anIntermediaryresult[dimensionmap .get("?DIMENSION_UNIQUE_NAME")]; newnode[dimensionmap.get("?DIMENSION_CAPTION")] = anIntermediaryresult[dimensionmap .get("?DIMENSION_CAPTION")]; newnode[dimensionmap.get("?DIMENSION_ORDINAL")] = anIntermediaryresult[dimensionmap .get("?DIMENSION_ORDINAL")]; newnode[dimensionmap.get("?DIMENSION_TYPE")] = anIntermediaryresult[dimensionmap .get("?DIMENSION_TYPE")]; newnode[dimensionmap.get("?DESCRIPTION")] = anIntermediaryresult[dimensionmap .get("?DESCRIPTION")]; // Only add if not already contained. boolean contained = false; for (Node[] aResult : result) { boolean sameDimension = aResult[dimensionmap .get("?DIMENSION_UNIQUE_NAME")].toString().equals( newnode[dimensionmap.get("?DIMENSION_UNIQUE_NAME")] .toString()); boolean sameCube = aResult[dimensionmap.get("?CUBE_NAME")] .toString().equals( newnode[dimensionmap.get("?CUBE_NAME")] .toString()); if (sameDimension && sameCube) { contained = true; } } if (!contained) { result.add(newnode); } } return result; } private List<Node[]> getDimensionsPerDataSet(Restrictions restrictions) { String additionalFilters = createFilterForRestrictions(restrictions); List<Node[]> result = new ArrayList<Node[]>(); // Create header Node[] header = new Node[] { new Variable("?CATALOG_NAME"), new Variable("?SCHEMA_NAME"), new Variable("?CUBE_NAME"), new Variable("?DIMENSION_NAME"), new Variable("?DIMENSION_UNIQUE_NAME"), new Variable("?DIMENSION_CAPTION"), new Variable("?DIMENSION_ORDINAL"), new Variable("?DIMENSION_TYPE"), new Variable("?DESCRIPTION") }; result.add(header); if (!isMeasureQueriedForExplicitly(restrictions.dimensionUniqueName, restrictions.hierarchyUniqueName, restrictions.levelUniqueName)) { // Get all dimensions String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getDimensions_regular.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> myresult = executeSparqlSelectQuery(querytemplate, true); // Add all of result2 to result boolean first = true; for (Node[] anIntermediaryresult : myresult) { if (first) { first = false; continue; } result.add(anIntermediaryresult); } } // We try to find measures if (true) { // In this case, we do ask for a measure dimension. String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getDimensions_measure_dimension.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> myresult = executeSparqlSelectQuery(querytemplate, true); // List<Node[]> result2 = applyRestrictions(memberUris2, // restrictions); // Add all of result2 to result boolean first = true; for (Node[] nodes : myresult) { if (first) { first = false; continue; } result.add(nodes); } } // Use canonical identifier // result = replaceIdentifiersWithCanonical(result); return result; } /** * Every measure also needs to be listed as member. When I create the dsd, I * add obsValue as a dimension, but also as a measure. However, members of * the measure dimension would typically all be named differently from the * measure (e.g., obsValue5), therefore, we do not find a match. The problem * is, that getMembers() has to return the measures. So, either, in the dsd, * we need to add a dimension with the measure as a member, or, the query * for the members should return for measures the measure property as * member. * * * Here, all the measure properties are returned. * * @param context * @param metadataRequest * @param restrictions * @return */ public List<Node[]> getMeasures(Restrictions restrictions) throws OlapException { Olap4ldUtil._log.config("Linked Data Engine: Get Measures..."); List<Node[]> result = new ArrayList<Node[]>(); // Check whether Drill-across query // XXX: Wildcard delimiter if (restrictions.cubeNamePattern != null) { String[] datasets = restrictions.cubeNamePattern.toString().split( ","); for (int i = 0; i < datasets.length; i++) { String dataset = datasets[i]; // Should make sure that the full restrictions are used. Node saverestrictioncubePattern = restrictions.cubeNamePattern; restrictions.cubeNamePattern = new Resource(dataset); List<Node[]> intermediaryresult = getMeasuresPerDataSet(restrictions); restrictions.cubeNamePattern = saverestrictioncubePattern; // Add to result boolean first = true; for (Node[] anIntermediaryresult : intermediaryresult) { if (first) { if (i == 0) { result.add(anIntermediaryresult); } first = false; continue; } // We do not want to have the single datasets returned. // result.add(anIntermediaryresult); // Also add measure to global cube Map<String, Integer> map = Olap4ldLinkedDataUtil .getNodeResultFields(intermediaryresult.get(0)); Node[] newnode = new Node[10]; newnode[map.get("?CATALOG_NAME")] = anIntermediaryresult[map .get("?CATALOG_NAME")]; newnode[map.get("?SCHEMA_NAME")] = anIntermediaryresult[map .get("?SCHEMA_NAME")]; newnode[map.get("?CUBE_NAME")] = restrictions.cubeNamePattern; newnode[map.get("?MEASURE_UNIQUE_NAME")] = anIntermediaryresult[map .get("?MEASURE_UNIQUE_NAME")]; newnode[map.get("?MEASURE_NAME")] = anIntermediaryresult[map .get("?MEASURE_NAME")]; newnode[map.get("?MEASURE_CAPTION")] = anIntermediaryresult[map .get("?MEASURE_CAPTION")]; newnode[map.get("?DATA_TYPE")] = anIntermediaryresult[map .get("?DATA_TYPE")]; newnode[map.get("?MEASURE_IS_VISIBLE")] = anIntermediaryresult[map .get("?MEASURE_IS_VISIBLE")]; newnode[map.get("?MEASURE_AGGREGATOR")] = anIntermediaryresult[map .get("?MEASURE_AGGREGATOR")]; newnode[map.get("?EXPRESSION")] = anIntermediaryresult[map .get("?EXPRESSION")]; // Only add if not already contained. // For measures, we add them all. // boolean contained = false; // for (Node[] aResult : result) { // boolean sameDimension = aResult[map // .get("?MEASURE_UNIQUE_NAME")].toString() // .equals(newnode[map // .get("?MEASURE_UNIQUE_NAME")] // .toString()); // boolean sameCube = aResult[map // .get("?CUBE_NAME")].toString().equals( // newnode[map.get("?CUBE_NAME")] // .toString()); // // if (sameDimension && sameCube) { // contained = true; // } // } // // if (!contained) { // result.add(newnode); // } result.add(newnode); } } } else { result = getMeasuresPerDataSet(restrictions); } return result; } private List<Node[]> getMeasuresPerDataSet(Restrictions restrictions) { String additionalFilters = createFilterForRestrictions(restrictions); // ///////////QUERY////////////////////////// /* * TODO: How to consider equal measures? */ // Boolean values need to be returned as "true" or "false". // Get all measures String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("virtuoso_getMeasures.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> result = executeSparqlSelectQuery(querytemplate, true); // Here, we also include measures without aggregation function. // We have also added these measures as members to getMembers(). querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getMeasures_withoutimplicit.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> result2 = executeSparqlSelectQuery(querytemplate, true); // List<Node[]> result = applyRestrictions(measureUris, restrictions); // Add all of result2 to result boolean first = true; for (Node[] nodes : result2) { if (first) { first = false; continue; } result.add(nodes); } // Use canonical identifier // result = replaceIdentifiersWithCanonical(result); return result; } /** * * Return hierarchies * * @param context * @param metadataRequest * @param restrictions * @return */ public List<Node[]> getHierarchies(Restrictions restrictions) throws OlapException { Olap4ldUtil._log.config("Linked Data Engine: Get Hierarchies..."); List<Node[]> result = new ArrayList<Node[]>(); // Check whether Drill-across query // XXX: Wildcard delimiter if (restrictions.cubeNamePattern != null) { String[] datasets = restrictions.cubeNamePattern.toString().split( ","); for (int i = 0; i < datasets.length; i++) { String dataset = datasets[i]; // Should make sure that the full restrictions are used. Node saverestrictioncubePattern = restrictions.cubeNamePattern; restrictions.cubeNamePattern = new Resource(dataset); List<Node[]> intermediaryresult = getHierarchiesPerDataSet(restrictions); restrictions.cubeNamePattern = saverestrictioncubePattern; // Add to result boolean first = true; for (Node[] anIntermediaryresult : intermediaryresult) { if (first) { if (i == 0) { result.add(anIntermediaryresult); } first = false; continue; } // Add the single hierarchies of the datasets to be // transformed with createGlobalHierarchies. result.add(anIntermediaryresult); } } } else { result = getHierarchiesPerDataSet(restrictions); } // Create global hierarchies which is intersection of all hierarchies // and new // cube name return createGlobalHierarchies(restrictions, result); } private List<Node[]> createGlobalHierarchies(Restrictions restrictions, List<Node[]> intermediaryresult) { List<Node[]> result = new ArrayList<Node[]>(); boolean first = true; for (Node[] anIntermediaryresult : intermediaryresult) { if (first) { first = false; result.add(anIntermediaryresult); continue; } // Also add hierarchy to global cube Map<String, Integer> hierarchymap = Olap4ldLinkedDataUtil .getNodeResultFields(intermediaryresult.get(0)); Node[] newnode = new Node[9]; newnode[hierarchymap.get("?CATALOG_NAME")] = anIntermediaryresult[hierarchymap .get("?CATALOG_NAME")]; newnode[hierarchymap.get("?SCHEMA_NAME")] = anIntermediaryresult[hierarchymap .get("?SCHEMA_NAME")]; // New cube name of global cube if (restrictions.cubeNamePattern == null) { newnode[hierarchymap.get("?CUBE_NAME")] = anIntermediaryresult[hierarchymap .get("?CUBE_NAME")]; } else { newnode[hierarchymap.get("?CUBE_NAME")] = restrictions.cubeNamePattern; } newnode[hierarchymap.get("?DIMENSION_UNIQUE_NAME")] = anIntermediaryresult[hierarchymap .get("?DIMENSION_UNIQUE_NAME")]; newnode[hierarchymap.get("?HIERARCHY_UNIQUE_NAME")] = anIntermediaryresult[hierarchymap .get("?HIERARCHY_UNIQUE_NAME")]; newnode[hierarchymap.get("?HIERARCHY_NAME")] = anIntermediaryresult[hierarchymap .get("?HIERARCHY_NAME")]; newnode[hierarchymap.get("?HIERARCHY_CAPTION")] = anIntermediaryresult[hierarchymap .get("?HIERARCHY_CAPTION")]; newnode[hierarchymap.get("?DESCRIPTION")] = anIntermediaryresult[hierarchymap .get("?DESCRIPTION")]; newnode[hierarchymap.get("?HIERARCHY_MAX_LEVEL_NUMBER")] = anIntermediaryresult[hierarchymap .get("?HIERARCHY_MAX_LEVEL_NUMBER")]; // Only add if not already contained. boolean contained = false; for (Node[] aResult : result) { boolean sameDimension = aResult[hierarchymap .get("?DIMENSION_UNIQUE_NAME")].toString().equals( newnode[hierarchymap.get("?DIMENSION_UNIQUE_NAME")] .toString()); boolean sameHierarchy = aResult[hierarchymap .get("?HIERARCHY_UNIQUE_NAME")].toString().equals( newnode[hierarchymap.get("?HIERARCHY_UNIQUE_NAME")] .toString()); boolean sameCube = aResult[hierarchymap.get("?CUBE_NAME")] .toString().equals( newnode[hierarchymap.get("?CUBE_NAME")] .toString()); if (sameDimension && sameHierarchy && sameCube) { contained = true; } } if (!contained) { result.add(newnode); } } return result; } private List<Node[]> getHierarchiesPerDataSet(Restrictions restrictions) { String additionalFilters = createFilterForRestrictions(restrictions); List<Node[]> result = new ArrayList<Node[]>(); // Create header Node[] header = new Node[] { new Variable("?CATALOG_NAME"), new Variable("?SCHEMA_NAME"), new Variable("?CUBE_NAME"), new Variable("?DIMENSION_UNIQUE_NAME"), new Variable("?HIERARCHY_UNIQUE_NAME"), new Variable("?HIERARCHY_NAME"), new Variable("?HIERARCHY_CAPTION"), new Variable("?DESCRIPTION"), new Variable("?HIERARCHY_MAX_LEVEL_NUMBER") }; result.add(header); if (!isMeasureQueriedForExplicitly(restrictions.dimensionUniqueName, restrictions.hierarchyUniqueName, restrictions.levelUniqueName)) { // Get all hierarchies with codeLists String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getHierarchies_regular.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> myresult = executeSparqlSelectQuery(querytemplate, true); // Add all of result to result boolean first = true; for (Node[] nodes : myresult) { if (first) { first = false; continue; } result.add(nodes); } } // List<Node[]> result = applyRestrictions(hierarchyResults, // restrictions); // Try to find measure dimensions. if (true) { // In this case, we do ask for a measure hierarchy. String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getHierarchies_measure_dimension.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> myresult = executeSparqlSelectQuery(querytemplate, true); // List<Node[]> result2 = applyRestrictions(memberUris2, // restrictions); // Add all of result2 to result boolean first = true; for (Node[] nodes : myresult) { if (first) { first = false; continue; } result.add(nodes); } } // Get dimension hierarchies without codeList, but only if hierarchy is // not set and different from dimension unique name /* * * Note in spec: * "Every dimension declared in a qb:DataStructureDefinition must have a declared rdfs:range." * Note in spec: * "Every dimension with range skos:Concept must have a qb:codeList." <= * This means, we do not necessarily need a code list in many cases. * But, if we have a code list, then: "If a dimension property has a * qb:codeList, then the value of the dimension property on every * qb:Observation must be in the code list." */ if (!isMeasureQueriedForExplicitly(restrictions.dimensionUniqueName, restrictions.hierarchyUniqueName, restrictions.levelUniqueName)) { String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getHierarchies_without_codelist.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> myresult = executeSparqlSelectQuery(querytemplate, true); // List<Node[]> result3 = applyRestrictions(memberUris3, // restrictions); // Add all of result2 to result boolean first = true; for (Node[] nodes : myresult) { if (first) { first = false; continue; } result.add(nodes); } } // Use canonical identifier // result = replaceIdentifiersWithCanonical(result); return result; } @Deprecated public List<Node[]> replaceIdentifiersWithCanonical(List<Node[]> result) { // // List<Node[]> newresult = new ArrayList<Node[]>(); // // for (Node[] anIntermediaryresult : result) { // Node[] newnode = new Node[anIntermediaryresult.length]; // for (int i = 0; i < anIntermediaryresult.length; i++) { // newnode[i] = getCanonical(anIntermediaryresult[i]); // } // newresult.add(newnode); // } // // return newresult; return null; } /** * * @param context * @param metadataRequest * @param restrictions * @return */ public List<Node[]> getLevels(Restrictions restrictions) throws OlapException { Olap4ldUtil._log.config("Linked Data Engine: Get Levels..."); List<Node[]> result = new ArrayList<Node[]>(); // Check whether Drill-across query // XXX: Wildcard delimiter if (restrictions.cubeNamePattern != null) { String[] datasets = restrictions.cubeNamePattern.toString().split( ","); for (int i = 0; i < datasets.length; i++) { String dataset = datasets[i]; // Should make sure that the full restrictions are used. Node saverestrictioncubePattern = restrictions.cubeNamePattern; restrictions.cubeNamePattern = new Resource(dataset); List<Node[]> intermediaryresult = getLevelsPerDataSet(restrictions); restrictions.cubeNamePattern = saverestrictioncubePattern; // Add to result boolean first = true; for (Node[] anIntermediaryresult : intermediaryresult) { if (first) { if (i == 0) { result.add(anIntermediaryresult); } first = false; continue; } // We do not want to have the single datasets returned. // result.add(anIntermediaryresult); // Also add dimension to global cube Map<String, Integer> levelmap = Olap4ldLinkedDataUtil .getNodeResultFields(intermediaryresult.get(0)); Node[] newnode = new Node[12]; newnode[levelmap.get("?CATALOG_NAME")] = anIntermediaryresult[levelmap .get("?CATALOG_NAME")]; newnode[levelmap.get("?SCHEMA_NAME")] = anIntermediaryresult[levelmap .get("?SCHEMA_NAME")]; newnode[levelmap.get("?CUBE_NAME")] = restrictions.cubeNamePattern; newnode[levelmap.get("?DIMENSION_UNIQUE_NAME")] = anIntermediaryresult[levelmap .get("?DIMENSION_UNIQUE_NAME")]; newnode[levelmap.get("?HIERARCHY_UNIQUE_NAME")] = anIntermediaryresult[levelmap .get("?HIERARCHY_UNIQUE_NAME")]; newnode[levelmap.get("?LEVEL_UNIQUE_NAME")] = anIntermediaryresult[levelmap .get("?LEVEL_UNIQUE_NAME")]; newnode[levelmap.get("?LEVEL_CAPTION")] = anIntermediaryresult[levelmap .get("?LEVEL_CAPTION")]; newnode[levelmap.get("?LEVEL_NAME")] = anIntermediaryresult[levelmap .get("?LEVEL_NAME")]; newnode[levelmap.get("?DESCRIPTION")] = anIntermediaryresult[levelmap .get("?DESCRIPTION")]; newnode[levelmap.get("?LEVEL_NUMBER")] = anIntermediaryresult[levelmap .get("?LEVEL_NUMBER")]; newnode[levelmap.get("?LEVEL_CARDINALITY")] = anIntermediaryresult[levelmap .get("?LEVEL_CARDINALITY")]; newnode[levelmap.get("?LEVEL_TYPE")] = anIntermediaryresult[levelmap .get("?LEVEL_TYPE")]; // Only add if not already contained. boolean contained = false; for (Node[] aResult : result) { boolean sameDimension = aResult[levelmap .get("?DIMENSION_UNIQUE_NAME")].toString() .equals(newnode[levelmap .get("?DIMENSION_UNIQUE_NAME")] .toString()); boolean sameHierarchy = aResult[levelmap .get("?HIERARCHY_UNIQUE_NAME")].toString() .equals(newnode[levelmap .get("?HIERARCHY_UNIQUE_NAME")] .toString()); boolean sameLevel = aResult[levelmap .get("?LEVEL_UNIQUE_NAME")].toString().equals( newnode[levelmap.get("?LEVEL_UNIQUE_NAME")] .toString()); boolean sameCube = aResult[levelmap.get("?CUBE_NAME")] .toString().equals( newnode[levelmap.get("?CUBE_NAME")] .toString()); if (sameDimension && sameHierarchy && sameLevel && sameCube) { contained = true; } } if (!contained) { result.add(newnode); } } } } else { result = getLevelsPerDataSet(restrictions); } return result; } private List<Node[]> getLevelsPerDataSet(Restrictions restrictions) { String additionalFilters = createFilterForRestrictions(restrictions); List<Node[]> result = new ArrayList<Node[]>(); // Create header Node[] header = new Node[] { new Variable("?CATALOG_NAME"), new Variable("?SCHEMA_NAME"), new Variable("?CUBE_NAME"), new Variable("?DIMENSION_UNIQUE_NAME"), new Variable("?HIERARCHY_UNIQUE_NAME"), new Variable("?LEVEL_UNIQUE_NAME"), new Variable("?LEVEL_CAPTION"), new Variable("?LEVEL_NAME"), new Variable("?DESCRIPTION"), new Variable("?LEVEL_NUMBER"), new Variable("?LEVEL_CARDINALITY"), new Variable("?LEVEL_TYPE") }; result.add(header); if (!isMeasureQueriedForExplicitly(restrictions.dimensionUniqueName, restrictions.hierarchyUniqueName, restrictions.levelUniqueName)) { // TODO: Add regularly modeled levels (without using xkos) String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getLevels_regular.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> myresult = executeSparqlSelectQuery(querytemplate, true); // Add all of result2 to result boolean first = true; for (Node[] nodes : myresult) { if (first) { first = false; continue; } result.add(nodes); } // Get all levels of code lists using xkos // TODO: LEVEL_CARDINALITY is not solved, yet. querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getLevels_xkos.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); myresult = executeSparqlSelectQuery(querytemplate, true); // Add all of result2 to result first = true; for (Node[] nodes : myresult) { if (first) { first = false; continue; } result.add(nodes); } } // Distinct for several measures per cube. // Add measures levels // Second, ask for the measures (which are also members), but only if // measure if (true) { // In this case, we do ask for a measure dimension. String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getLevels_measure_dimension.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> myresult = executeSparqlSelectQuery(querytemplate, true); // List<Node[]> result2 = applyRestrictions(memberUris2, // restrictions); // Add all of result2 to result boolean first = true; for (Node[] nodes : myresult) { if (first) { first = false; continue; } result.add(nodes); } } // Add levels for dimensions without codelist, but only if hierarchy and // dimension names are equal if (!isMeasureQueriedForExplicitly(restrictions.dimensionUniqueName, restrictions.hierarchyUniqueName, restrictions.levelUniqueName)) { String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getLevels_without_codelist.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); // Second, ask for the measures (which are also members) List<Node[]> myresult = executeSparqlSelectQuery(querytemplate, true); // List<Node[]> result3 = applyRestrictions(memberUris3, // restrictions); // Add all of result3 to result boolean first = true; for (Node[] nodes : myresult) { if (first) { first = false; continue; } result.add(nodes); } } // Use canonical identifier // result = replaceIdentifiersWithCanonical(result); return result; } /** * Important issues to remember: Every measure also needs to be listed as * member. When I create the dsd, I add obsValue as a dimension, but also as * a measure. However, members of the measure dimension would typically all * be named differently from the measure (e.g., obsValue5), therefore, we do * not find a match. The problem is, that getMembers() has to return the * measures. So, either, in the dsd, we need to add a dimension with the * measure as a member, or, the query for the members should return for * measures the measure property as member. * * The dimension/hierarchy/level of a measure should always be "Measures". * * Typically, a measure should not have a codeList, since we can have many * many members. If a measure does not have a codelist, the bounding would * still work, since The componentProperty is existing, but no hierarchy... * * For caption of members, we should eventually use * http://www.w3.org/2004/02/skos/core#notation skos:notation, since members * are in rdf represented as skos:Concept and this is the proper way to give * them a representation. * * Assumptions of this method: * * The restrictions are set up only as follows 1) cube, dim, hier, level 2) * cube, dim, hier, level, member, null 3) cube, dim, hier, level, member, * treeOp * * The members are only modelled as follows 1) Measure Member (member of the * measure dimension) 2) Level Member (member of a regular dimension) 3) Top * Concept Member (member via skos:topConcept) 4) Degenerated Member (member * without code list) * * @return Node[]{?memberURI ?name} * @throws MalformedURLException */ public List<Node[]> getMembers(Restrictions restrictions) throws OlapException { Olap4ldUtil._log.config("Linked Data Engine: Get Members..."); List<Node[]> result = new ArrayList<Node[]>(); // Check whether Drill-across query // XXX: Wildcard delimiter if (restrictions.cubeNamePattern != null) { String[] datasets = restrictions.cubeNamePattern.toString().split( ","); for (int i = 0; i < datasets.length; i++) { String dataset = datasets[i]; // Should make sure that the full restrictions are used. // XXX: Refactor: used at every getXXX() Node saverestrictioncubePattern = restrictions.cubeNamePattern; restrictions.cubeNamePattern = new Resource(dataset); List<Node[]> intermediaryresult = getMembersPerDataSet(restrictions); restrictions.cubeNamePattern = saverestrictioncubePattern; // Add to result boolean first = true; for (Node[] anIntermediaryresult : intermediaryresult) { if (first) { if (i == 0) { result.add(anIntermediaryresult); } first = false; continue; } // We do not want to have the single datasets returned. // result.add(anIntermediaryresult); // Also add dimension to global cube Map<String, Integer> membermap = Olap4ldLinkedDataUtil .getNodeResultFields(intermediaryresult.get(0)); Node[] newnode = new Node[13]; newnode[membermap.get("?CATALOG_NAME")] = anIntermediaryresult[membermap .get("?CATALOG_NAME")]; newnode[membermap.get("?SCHEMA_NAME")] = anIntermediaryresult[membermap .get("?SCHEMA_NAME")]; newnode[membermap.get("?CUBE_NAME")] = restrictions.cubeNamePattern; newnode[membermap.get("?DIMENSION_UNIQUE_NAME")] = anIntermediaryresult[membermap .get("?DIMENSION_UNIQUE_NAME")]; newnode[membermap.get("?HIERARCHY_UNIQUE_NAME")] = anIntermediaryresult[membermap .get("?HIERARCHY_UNIQUE_NAME")]; newnode[membermap.get("?LEVEL_UNIQUE_NAME")] = anIntermediaryresult[membermap .get("?LEVEL_UNIQUE_NAME")]; newnode[membermap.get("?LEVEL_NUMBER")] = anIntermediaryresult[membermap .get("?LEVEL_NUMBER")]; newnode[membermap.get("?MEMBER_UNIQUE_NAME")] = anIntermediaryresult[membermap .get("?MEMBER_UNIQUE_NAME")]; newnode[membermap.get("?MEMBER_NAME")] = anIntermediaryresult[membermap .get("?MEMBER_NAME")]; newnode[membermap.get("?MEMBER_CAPTION")] = anIntermediaryresult[membermap .get("?MEMBER_CAPTION")]; newnode[membermap.get("?MEMBER_TYPE")] = anIntermediaryresult[membermap .get("?MEMBER_TYPE")]; newnode[membermap.get("?PARENT_UNIQUE_NAME")] = anIntermediaryresult[membermap .get("?PARENT_UNIQUE_NAME")]; newnode[membermap.get("?PARENT_LEVEL")] = anIntermediaryresult[membermap .get("?PARENT_LEVEL")]; // Only add if not already contained. boolean contained = false; for (Node[] aResult : result) { boolean sameDimension = aResult[membermap .get("?DIMENSION_UNIQUE_NAME")].toString() .equals(newnode[membermap .get("?DIMENSION_UNIQUE_NAME")] .toString()); boolean sameHierarchy = aResult[membermap .get("?HIERARCHY_UNIQUE_NAME")].toString() .equals(newnode[membermap .get("?HIERARCHY_UNIQUE_NAME")] .toString()); boolean sameLevel = aResult[membermap .get("?LEVEL_UNIQUE_NAME")].toString().equals( newnode[membermap.get("?LEVEL_UNIQUE_NAME")] .toString()); boolean sameMember = aResult[membermap .get("?MEMBER_UNIQUE_NAME")].toString().equals( newnode[membermap.get("?MEMBER_UNIQUE_NAME")] .toString()); boolean sameCube = aResult[membermap.get("?CUBE_NAME")] .toString().equals( newnode[membermap.get("?CUBE_NAME")] .toString()); if (sameDimension && sameHierarchy && sameLevel && sameMember && sameCube) { contained = true; } } if (!contained) { result.add(newnode); } } } } else { result = getMembersPerDataSet(restrictions); } return result; } private List<Node[]> getMembersPerDataSet(Restrictions restrictions) { List<Node[]> result = new ArrayList<Node[]>(); List<Node[]> intermediaryresult = null; // Create header Node[] header = new Node[] { new Variable("?CATALOG_NAME"), new Variable("?SCHEMA_NAME"), new Variable("?CUBE_NAME"), new Variable("?DIMENSION_UNIQUE_NAME"), new Variable("?HIERARCHY_UNIQUE_NAME"), new Variable("?LEVEL_UNIQUE_NAME"), new Variable("?LEVEL_NUMBER"), new Variable("?MEMBER_NAME"), new Variable("?MEMBER_UNIQUE_NAME"), new Variable("?MEMBER_CAPTION"), new Variable("?MEMBER_TYPE"), new Variable("?PARENT_UNIQUE_NAME"), new Variable("?PARENT_LEVEL") }; result.add(header); // Measure Member if (true) { // XXX From now on, I return only one measure. intermediaryresult = getMeasureMembers(restrictions); addToResult(intermediaryresult, result); } // Regular members if (!isMeasureQueriedForExplicitly(restrictions.dimensionUniqueName, restrictions.hierarchyUniqueName, restrictions.levelUniqueName)) { intermediaryresult = getHasTopConceptMembers(restrictions); addToResult(intermediaryresult, result); } // // Xkos members // // Watch out: No square brackets // if (!isMeasureQueriedForExplicitly(restrictions.dimensionUniqueName, // restrictions.hierarchyUniqueName, restrictions.levelUniqueName)) { // // intermediaryresult = getXkosMembers(restrictions); // // addToResult(intermediaryresult, result); // // } // // // If we still do not have members, then we might have degenerated // // members // if (!isMeasureQueriedForExplicitly(restrictions.dimensionUniqueName, // restrictions.hierarchyUniqueName, restrictions.levelUniqueName)) { // // Members without codeList // intermediaryresult = getDegeneratedMembers(restrictions); // // addToResult(intermediaryresult, result); // // } // Use canonical identifier // result = replaceIdentifiersWithCanonical(result); return result; } private List<Node[]> getMeasureMembers(Restrictions restrictions) { String additionalFilters = createFilterForRestrictions(restrictions); /* * I would assume that if TREE_OP is set, we have a unique member given * and either want its children, its siblings, its parent, self, * ascendants, or descendants. */ if (restrictions.tree != null && (restrictions.tree & 8) != 8) { // Assumption 1: Treeop only uses Member if (restrictions.memberUniqueName == null) { throw new UnsupportedOperationException( "If a treeMask is given, we should also have a unique member name!"); } if ((restrictions.tree & 1) == 1) { // CHILDREN Olap4ldUtil._log.config("TreeOp:CHILDREN"); } if ((restrictions.tree & 2) == 2) { // SIBLINGS Olap4ldUtil._log.config("TreeOp:SIBLINGS"); if (restrictions.cubeNamePattern != null) { additionalFilters += " FILTER (?CUBE_NAME = <" + restrictions.cubeNamePattern + ">) "; } } if ((restrictions.tree & 4) == 4) { // PARENT Olap4ldUtil._log.config("TreeOp:PARENT"); } if ((restrictions.tree & 16) == 16) { // DESCENDANTS Olap4ldUtil._log.config("TreeOp:DESCENDANTS"); } if ((restrictions.tree & 32) == 32) { // ANCESTORS Olap4ldUtil._log.config("TreeOp:ANCESTORS"); } } else { // TreeOp = Self or null Olap4ldUtil._log.config("TreeOp:SELF"); } // Second, ask for the measures (which are also members) String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("virtuoso_getMembers_measure_members.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> memberUris2 = executeSparqlSelectQuery(querytemplate, true); return memberUris2; } /** * Finds specific typical members. * * @param restrictions * * @return */ private List<Node[]> getXkosMembers(Restrictions restrictions) { String additionalFilters = createFilterForRestrictions(restrictions); /* * I would assume that if TREE_OP is set, we have a unique member given * and either want its children, its siblings, its parent, self, * ascendants, or descendants. */ if (restrictions.tree != null && (restrictions.tree & 8) != 8) { // Assumption 1: Treeop only uses Member if (restrictions.memberUniqueName == null) { throw new UnsupportedOperationException( "If a treeMask is given, we should also have a unique member name!"); } if ((restrictions.tree & 1) == 1) { // CHILDREN Olap4ldUtil._log.config("TreeOp:CHILDREN"); // Here, we need a specific filter additionalFilters = " FILTER (?PARENT_UNIQUE_NAME = <" + restrictions.memberUniqueName + ">) "; } if ((restrictions.tree & 2) == 2) { // SIBLINGS Olap4ldUtil._log.config("TreeOp:SIBLINGS"); } if ((restrictions.tree & 4) == 4) { // PARENT Olap4ldUtil._log.config("TreeOp:PARENT"); } if ((restrictions.tree & 16) == 16) { // DESCENDANTS Olap4ldUtil._log.config("TreeOp:DESCENDANTS"); } if ((restrictions.tree & 32) == 32) { // ANCESTORS Olap4ldUtil._log.config("TreeOp:ANCESTORS"); } throw new UnsupportedOperationException( "TreeOp and getLevelMember failed."); } else { // TreeOp = Self or null Olap4ldUtil._log.config("TreeOp:SELF"); } String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getMembers_xkos.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> memberUris2 = executeSparqlSelectQuery(querytemplate, true); return memberUris2; } /** * Returns all hasTopConcept members of the cube. * * @param dimensionUniqueName * @param cubeNamePattern * * @param cubeNamePattern * @return */ private List<Node[]> getHasTopConceptMembers(Restrictions restrictions) { String additionalFilters = createFilterForRestrictions(restrictions); /* * I would assume that if TREE_OP is set, we have a unique member given * and either want its children, its siblings, its parent, self, * ascendants, or descendants. */ if (restrictions.tree != null && (restrictions.tree & 8) != 8) { // Assumption 1: Treeop only uses Member if (restrictions.memberUniqueName == null) { throw new UnsupportedOperationException( "If a treeMask is given, we should also have a unique member name!"); } if ((restrictions.tree & 1) == 1) { // CHILDREN Olap4ldUtil._log.config("TreeOp:CHILDREN"); } if ((restrictions.tree & 2) == 2) { // SIBLINGS Olap4ldUtil._log.config("TreeOp:SIBLINGS"); } if ((restrictions.tree & 4) == 4) { // PARENT Olap4ldUtil._log.config("TreeOp:PARENT"); } if ((restrictions.tree & 16) == 16) { // DESCENDANTS Olap4ldUtil._log.config("TreeOp:DESCENDANTS"); } if ((restrictions.tree & 32) == 32) { // ANCESTORS Olap4ldUtil._log.config("TreeOp:ANCESTORS"); } throw new UnsupportedOperationException( "TreeOp and getLevelMember failed."); } else { // TreeOp = Self or null Olap4ldUtil._log.config("TreeOp:SELF"); // First, ask for all members // Get all members of hierarchies without levels, that simply // define // skos:hasTopConcept members with skos:notation. String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getMembers_topConcept.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> memberUris = executeSparqlSelectQuery(querytemplate, true); return memberUris; } } /** * For degenerated dimensions, we have to assume that either dim, hier, or * level are given. * * @return */ private List<Node[]> getDegeneratedMembers(Restrictions restrictions) { String additionalFilters = createFilterForRestrictions(restrictions); if (restrictions.tree != null && (restrictions.tree & 8) != 8) { // Assumption 1: Treeop only uses Member if (restrictions.memberUniqueName == null) { throw new UnsupportedOperationException( "If a treeMask is given, we should also have a unique member name!"); } if ((restrictions.tree & 1) == 1) { // CHILDREN Olap4ldUtil._log.config("TreeOp:CHILDREN"); } if ((restrictions.tree & 2) == 2) { // SIBLINGS Olap4ldUtil._log.config("TreeOp:SIBLINGS"); } if ((restrictions.tree & 4) == 4) { // PARENT Olap4ldUtil._log.config("TreeOp:PARENT"); } if ((restrictions.tree & 16) == 16) { // DESCENDANTS Olap4ldUtil._log.config("TreeOp:DESCENDANTS"); } if ((restrictions.tree & 32) == 32) { // ANCESTORS Olap4ldUtil._log.config("TreeOp:ANCESTORS"); } throw new UnsupportedOperationException( "TreeOp and getLevelMember failed."); } else { // TreeOp = Self or null Olap4ldUtil._log.config("TreeOp:SELF"); } String querytemplate = Olap4ldLinkedDataUtil .readInQueryTemplate("sesame_getMembers_degenerated.txt"); querytemplate = querytemplate.replace("{{{STANDARDFROM}}}", askForFrom(true)); querytemplate = querytemplate.replace("{{{TABLE_CAT}}}", TABLE_CAT); querytemplate = querytemplate.replace("{{{TABLE_SCHEM}}}", TABLE_SCHEM); querytemplate = querytemplate.replace("{{{FILTERS}}}", additionalFilters); List<Node[]> memberUris1 = executeSparqlSelectQuery(querytemplate, true); return memberUris1; } @SuppressWarnings("unused") private boolean isResourceAndNotLiteral(String resource) { return resource.startsWith("http:"); } private String createFilterForRestrictions(Restrictions restrictions) { String filter = ""; // We need to create a filter for the specific restriction filter += (restrictions.cubeNamePattern != null) ? " FILTER (?CUBE_NAME = <" + restrictions.cubeNamePattern + ">) " : ""; if (restrictions.dimensionUniqueName != null && !restrictions.dimensionUniqueName.toString().equals( Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) { // filter += " filter(" // + createConditionConsiderEquivalences( // restrictions.dimensionUniqueName, new Variable( // "DIMENSION_UNIQUE_NAME")) + ") "; filter += " filter(str(?DIMENSION_UNIQUE_NAME) = \"" + restrictions.dimensionUniqueName + "\") "; } if (restrictions.hierarchyUniqueName != null && !restrictions.hierarchyUniqueName.toString().equals( Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) { // This we do since ranges may be blank nodes, e.g., of ical:dtend // XXX: Workaround if (restrictions.hierarchyUniqueName.toString().startsWith("node")) { filter += ""; } else { // filter += " filter(" // + createConditionConsiderEquivalences( // restrictions.hierarchyUniqueName, new Variable( // "HIERARCHY_UNIQUE_NAME")) + ") "; filter += " filter(str(?HIERARCHY_UNIQUE_NAME) = \"" + restrictions.hierarchyUniqueName + "\") "; } } if (restrictions.levelUniqueName != null && !restrictions.levelUniqueName.toString().equals( Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) { // This we do since ranges may be blank nodes, e.g., of ical:dtend // XXX: Workaround if (restrictions.hierarchyUniqueName != null && restrictions.hierarchyUniqueName.toString().startsWith( "node")) { filter += ""; } else { // filter += " filter(" // + createConditionConsiderEquivalences( // restrictions.levelUniqueName, new Variable( // "LEVEL_UNIQUE_NAME")) + ") "; filter += " filter(str(?LEVEL_UNIQUE_NAME) = \"" + restrictions.levelUniqueName + "\") "; } } if (restrictions.memberUniqueName != null && !restrictions.memberUniqueName.toString().equals( Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) { // filter += " filter(" // + createConditionConsiderEquivalences( // restrictions.memberUniqueName, new Variable( // "MEMBER_UNIQUE_NAME")) + ") "; filter += " filter(str(?MEMBER_UNIQUE_NAME) = \"" + restrictions.memberUniqueName + "\") "; } return filter; } /** * This method creates a filter string for a Resource with a specific * variable for all equivalences. * * @param canonicalResource * @param variableName * @return */ @SuppressWarnings("unused") @Deprecated private String createConditionConsiderEquivalences(Node canonicalResource, Variable variable) { List<Node> equivalenceClass = getEquivalenceClassOfNode(canonicalResource); // Since we sometimes manually build member names, we have to check // on strings String[] filterString = new String[equivalenceClass.size()]; for (int i = 0; i < filterString.length; i++) { filterString[i] = "str(?" + variable + ") = \"" + equivalenceClass.get(i) + "\""; } return "(" + Olap4ldLinkedDataUtil.implodeArray(filterString, " || ") + ")"; } /** * Adds intermediary results to result. * * @param intermediaryresult * @param result */ private void addToResult(List<Node[]> intermediaryresult, List<Node[]> result) { boolean first = true; for (Node[] nodes : intermediaryresult) { if (first) { first = false; continue; } result.add(nodes); } } public List<Node[]> getSets(Restrictions restrictions) { // TODO Auto-generated method stub return null; } @Override public List<Node[]> executeOlapQuery(LogicalOlapQueryPlan queryplan) throws OlapException { // Log logical query plan Olap4ldUtil._log.config("Logical query plan: " + queryplan.toString()); Olap4ldUtil._log .info("Execute logical query plan: Generate physical query plan."); long time = System.currentTimeMillis(); // Create physical query plan this.execplan = createExecplan(queryplan); Olap4ldUtil._log .info("Execute logical query plan: Physical query plan: " + execplan.toString()); time = System.currentTimeMillis() - time; Olap4ldUtil._log .info("Execute logical query plan: Generate physical query plan finished in " + time + "ms."); Olap4ldUtil._log .info("Execute logical query plan: Execute physical query plan."); time = System.currentTimeMillis(); PhysicalOlapIterator resultIterator = this.execplan.getIterator(); /* * We create our own List<Node[]> result with every item * * Every Node[] contains for each dimension in the dimension list of the * metadata a member and for each measure in the measure list a value. */ List<Node[]> result = new ArrayList<Node[]>(); while (resultIterator.hasNext()) { Object nextObject = resultIterator.next(); // Will be Node[] Node[] node = (Node[]) nextObject; result.add(node); } time = System.currentTimeMillis() - time; Olap4ldUtil._log .info("Execute logical query plan: Execute physical query plan finished in " + time + "ms."); return result; } @Override public List<Node[]> executeOlapQuery(Cube cube, List<Level> slicesrollups, List<Position> dices, List<Measure> projections) throws OlapException { throw new UnsupportedOperationException( "Only LogicalOlapQuery trees can be executed!"); } /** * Empties store and locationMap. */ public void rollback() { initialize(); } }