package org.olap4j.driver.olap4ld.linkeddata; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.olap4j.OlapException; import org.olap4j.driver.olap4ld.Olap4ldUtil; import org.olap4j.driver.olap4ld.helper.Olap4ldLinkedDataUtil; import org.semanticweb.yars.nx.Node; import org.semanticweb.yars.nx.Resource; import org.semanticweb.yars.nx.Variable; /** * * @author benedikt * */ public class BaseCubeSparqlDerivedDatasetIterator implements PhysicalOlapIterator { private List<Node[]> cubes; private List<Node[]> measures; private List<Node[]> dimensions; private List<Node[]> hierarchies; private List<Node[]> levels; private List<Node[]> members; private LinkedDataCubesEngine engine; private Iterator<Node[]> outputiterator; private String query; public BaseCubeSparqlDerivedDatasetIterator(LinkedDataCubesEngine engine, String dataseturi) { this.engine = engine; // First: GDP and main components - Current prices dataset Node gdpdsuri = new Resource(dataseturi); 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 try { cubes = engine.getCubes(gdprestrictions); measures = engine.getMeasures(gdprestrictions); dimensions = engine.getDimensions(gdprestrictions); hierarchies = engine.getHierarchies(gdprestrictions); levels = engine.getLevels(gdprestrictions); members = engine.getMembers(gdprestrictions); } catch (OlapException e) { // TODO Auto-generated catch block e.printStackTrace(); } // We assume that cubes to BaseCubeOp always refer to one single cube assert cubes.size() <= 2; // Instead of simply returning the metadata, we can return the // List of List of nodes representing the observations. // We create a SPARQL query asking for all observations // with all dimensions and measures. // We collect necessary parts of the SPARQL query. String selectClause = " "; String whereClause = " "; // 1. Get cube add triple patterns Map<String, Integer> cubemap = Olap4ldLinkedDataUtil .getNodeResultFields(cubes.get(0)); // Cube gives us the URI of the dataset that we want to resolve String ds = cubes.get(1)[cubemap.get("?CUBE_NAME")].toString(); // We could also make obs a variable. whereClause += "?obs qb:dataSet <" + ds + ">. "; // 2. Dimension triple patterns Map<String, Integer> dimensionmap = Olap4ldLinkedDataUtil .getNodeResultFields(dimensions.get(0)); // First is header for (int i = 1; i < dimensions.size(); i++) { Node dimensionProperty = dimensions.get(i)[dimensionmap .get("?DIMENSION_UNIQUE_NAME")]; // As always disregard measure dimension. // Since we do not know how Measure Dimension is encoded as Node, we // compare the strings. if (dimensionProperty.toString().equals( Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) { continue; } Node dimensionPropertyVariable = Olap4ldLinkedDataUtil .makeUriToVariable(dimensionProperty); whereClause += " ?obs <" + dimensionProperty + "> " + dimensionPropertyVariable.toN3() + ". "; selectClause += " ?" + dimensionPropertyVariable; } // 3. Measure triple patterns Map<String, Integer> measuremap = Olap4ldLinkedDataUtil .getNodeResultFields(measures.get(0)); // First is header for (int i = 1; i < measures.size(); i++) { Node[] measure = measures.get(i); /* * Here we deal with the problem that cubes coming in will possibly * have many "implicit" measures. For now, we assume those to be * disregarded. */ // XXX: I think, this will make problems, later, since OLAP-to-SPARQL requires this information as does the QB dataset tests. // BUT: Only if the result is used. // if (measure[measuremap.get("?MEASURE_UNIQUE_NAME")].toString() // .contains("AGGFUNC")) { // continue; // } // As always, remove Aggregation Function from Measure Name String measureProperty = measure[measuremap .get("?MEASURE_UNIQUE_NAME")].toString().replace( "AGGFUNC" + measure[measuremap.get("?MEASURE_AGGREGATOR")] .toString() .replace("http://purl.org/olap#", "") .toUpperCase(), ""); // XXX: Will not work, currently, since measure names are defined // by: measureProperty, dataset, Aggregation function. See // OLAP-2-SPARQL algorithm iterator. // We also remove aggregation function from Measure Property // Variable so // that the same property is not selected twice. Variable measurePropertyVariable = Olap4ldLinkedDataUtil .makeUriToVariable(new Resource(measureProperty)); // Unique name for variable // Node uniqueMeasurePropertyVariable = Olap4ldLinkedDataUtil // .makeUriToVariable(measure[measuremap // .get("?MEASURE_UNIQUE_NAME")].toString()); // We take the aggregator from the measure // Since we use OPTIONAL, there might be empty columns, // which is why we need // to convert them to decimal. // selectClause += " (" // + measure[measuremap.get("?MEASURE_AGGREGATOR")].toString() // .replace("http://purl.org/olap#", "") + "(?" // + measurePropertyVariable + ") as ?" // + uniqueMeasurePropertyVariable + ")"; selectClause += measurePropertyVariable.toN3() + " "; whereClause += "?obs <" + measureProperty + "> " + measurePropertyVariable.toN3() + "."; } this.query = Olap4ldLinkedDataUtil.getStandardPrefixes() + "select " + selectClause + " where { " + whereClause + " } "; } /** * We only return always the same thing. */ public boolean hasNext() { if (this.outputiterator == null) { try { init(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } return outputiterator.hasNext(); } @Override public Object next() { if (this.outputiterator == null) { try { init(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } return outputiterator.next(); } /** * Simply copied over from embedded sesame. * * @param query * @param caching * @return */ private List<Node[]> sparql(String query) { Olap4ldUtil._log.info("SPARQL query: " + query); List<Node[]> myBindings = new ArrayList<Node[]>(); myBindings = engine.executeSparqlSelectQuery(query, false); return myBindings; } @Override public void remove() { // nothing to do? } public String toString() { Map<String, Integer> map = Olap4ldLinkedDataUtil .getNodeResultFields(cubes.get(0)); Node[] cubeNodes = cubes.get(1); int index = map.get("?CUBE_NAME"); String cubename = cubeNodes[index].toString(); return "BaseCube (" + cubename + "," + query + ")"; } @Override public void init() throws Exception { // Does not have input operators, therefore no other init necessary. this.outputiterator = sparql(this.query).iterator(); } @Override public void close() throws Exception { // TODO Auto-generated method stub } @Override public void accept(LogicalOlapOperatorQueryPlanVisitor v) throws QueryException { // TODO Auto-generated method stub } @Override public List<Node[]> getCubes(Restrictions restrictions) throws OlapException { return cubes; } @Override public List<Node[]> getDimensions(Restrictions restrictions) throws OlapException { return dimensions; } /** * BaseCubeSparql does only return the actual measure. */ public List<Node[]> getMeasures(Restrictions restrictions) throws OlapException { Map<String, Integer> measuremap = Olap4ldLinkedDataUtil .getNodeResultFields(measures.get(0)); // XXX: In this case, we have not yet distinguished measures when creating the metadata. // So, we change. // Convert-Cube would only take the non-aggregated measures List<Node[]> newmeasures = new ArrayList<Node[]>(); newmeasures.add(measures.get(0)); for (int i = 1; i < measures.size(); i++) { Node[] measure = measures.get(i); /* * Here we deal with the problem that cubes coming in will possibly * have many "implicit" measures. For now, we assume those to be * disregarded. */ // XXX: Seems to have been done before, already. // if (measure[measuremap.get("?MEASURE_UNIQUE_NAME")].toString() // .contains("AGGFUNC")) { // continue; // } newmeasures.add(measure); } return newmeasures; } @Override public List<Node[]> getHierarchies(Restrictions restrictions) throws OlapException { return hierarchies; } @Override public List<Node[]> getLevels(Restrictions restrictions) throws OlapException { return levels; } @Override public List<Node[]> getMembers(Restrictions restrictions) throws OlapException { return members; } }