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.helper.Olap4ldLinkedDataUtil;
import org.semanticweb.yars.nx.Literal;
import org.semanticweb.yars.nx.Node;
import org.semanticweb.yars.nx.Resource;
/**
* This iterator simply computes the nested-loop join of input iterators
* (mostly, the Olap2SparqlAlgorithmSesameIterator).
*
* @author benedikt
*
*/
public class DrillAcrossNestedLoopJoinSesameIterator 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 Iterator<Node[]> iterator;
private List<Node[]> results;
private PhysicalOlapIterator inputiterator1;
private PhysicalOlapIterator inputiterator2;
public DrillAcrossNestedLoopJoinSesameIterator(PhysicalOlapIterator inputiterator1,
PhysicalOlapIterator inputiterator2) {
this.inputiterator1 = inputiterator1;
this.inputiterator2 = inputiterator2;
// We simply assume two cubes
// Metadata we do directly.
createMetadata();
}
private void createMetadata() {
// We assume that there is at least one input iterator that we can use
// the metadata from.
cubes = new ArrayList<Node[]>();
measures = new ArrayList<Node[]>();
dimensions = new ArrayList<Node[]>();
hierarchies = new ArrayList<Node[]>();
levels = new ArrayList<Node[]>();
members = new ArrayList<Node[]>();
Restrictions restrictions = new Restrictions();
try {
// this.cubes = root1.getCubes(restrictions);
// this.measures = root1.getMeasures(restrictions);
// this.dimensions = root1.getDimensions(restrictions);
// this.hierarchies = root1.getHierarchies(restrictions);
// this.levels = root1.getLevels(restrictions);
// // One problem could be that this may be a huge number of
// members.
// this.members = root1.getMembers(restrictions);
// Now, add "virtual cube"
// Needs to be concatenation of single cubes
Map<String, Integer> cubemap = Olap4ldLinkedDataUtil
.getNodeResultFields(inputiterator1.getCubes(restrictions).get(0));
cubes.add(inputiterator1.getCubes(restrictions).get(0));
// XXX2C is separator in MDX, , in Linked Data
String cubename = inputiterator1.getCubes(restrictions).get(1)[cubemap
.get("?CUBE_NAME")].toString()
+ ","
+ inputiterator2.getCubes(restrictions).get(1)[cubemap
.get("?CUBE_NAME")].toString();
// ?CATALOG_NAME ?SCHEMA_NAME ?CUBE_NAME ?CUBE_TYPE ?CUBE_CAPTION
// ?DESCRIPTION
Node[] newnode = new Node[6];
newnode[cubemap.get("?CATALOG_NAME")] = inputiterator1
.getCubes(restrictions).get(1)[cubemap.get("?CATALOG_NAME")];
newnode[cubemap.get("?SCHEMA_NAME")] = inputiterator1.getCubes(restrictions)
.get(1)[cubemap.get("?SCHEMA_NAME")];
newnode[cubemap.get("?CUBE_NAME")] = new Literal(cubename);
newnode[cubemap.get("?CUBE_TYPE")] = new Literal("CUBE");
newnode[cubemap.get("?CUBE_CAPTION")] = new Literal("Global Cube");
newnode[cubemap.get("?DESCRIPTION")] = new Literal(
"This is the global cube.");
cubes.add(newnode);
// Now, add Measures
// Also add measure to global cube
// From first cube
Map<String, Integer> measuremap = Olap4ldLinkedDataUtil
.getNodeResultFields(inputiterator1.getMeasures(restrictions).get(0));
// Add to result from first cube
boolean first = true;
for (Node[] anIntermediaryresult : inputiterator1.getMeasures(restrictions)) {
if (first) {
measures.add(anIntermediaryresult);
first = false;
continue;
}
// We do not want to have the single datasets returned.
// result.add(anIntermediaryresult);
newnode = new Node[10];
newnode[measuremap.get("?CATALOG_NAME")] = anIntermediaryresult[measuremap
.get("?CATALOG_NAME")];
newnode[measuremap.get("?SCHEMA_NAME")] = anIntermediaryresult[measuremap
.get("?SCHEMA_NAME")];
newnode[measuremap.get("?CUBE_NAME")] = new Literal(cubename);
newnode[measuremap.get("?MEASURE_UNIQUE_NAME")] = anIntermediaryresult[measuremap
.get("?MEASURE_UNIQUE_NAME")];
newnode[measuremap.get("?MEASURE_NAME")] = anIntermediaryresult[measuremap
.get("?MEASURE_NAME")];
newnode[measuremap.get("?MEASURE_CAPTION")] = anIntermediaryresult[measuremap
.get("?MEASURE_CAPTION")];
newnode[measuremap.get("?DATA_TYPE")] = anIntermediaryresult[measuremap
.get("?DATA_TYPE")];
newnode[measuremap.get("?MEASURE_IS_VISIBLE")] = anIntermediaryresult[measuremap
.get("?MEASURE_IS_VISIBLE")];
newnode[measuremap.get("?MEASURE_AGGREGATOR")] = anIntermediaryresult[measuremap
.get("?MEASURE_AGGREGATOR")];
newnode[measuremap.get("?EXPRESSION")] = anIntermediaryresult[measuremap
.get("?EXPRESSION")];
// Only add if not already contained.
// For measures, we add them all.
measures.add(newnode);
}
// Do join
Restrictions emptyrestrictions = new Restrictions();
List<Node[]> root1_measures = inputiterator1.getMeasures(emptyrestrictions);
List<Node[]> root2_measures = inputiterator2.getMeasures(emptyrestrictions);
// We check if all measures are the same
boolean allmeasuresthesame = areAllMeasuresTheSame(root1_measures,
root2_measures);
// If all the same then we do not need to add any more.
if (!allmeasuresthesame) {
// From second cube
measuremap = Olap4ldLinkedDataUtil.getNodeResultFields(inputiterator2
.getMeasures(restrictions).get(0));
// Add to result from first cube
first = true;
for (Node[] anIntermediaryresult : inputiterator2
.getMeasures(restrictions)) {
if (first) {
// Do not add header twice.
// measures.add(anIntermediaryresult);
first = false;
continue;
}
// We do not want to have the single datasets returned.
// result.add(anIntermediaryresult);
newnode = new Node[10];
newnode[measuremap.get("?CATALOG_NAME")] = anIntermediaryresult[measuremap
.get("?CATALOG_NAME")];
newnode[measuremap.get("?SCHEMA_NAME")] = anIntermediaryresult[measuremap
.get("?SCHEMA_NAME")];
newnode[measuremap.get("?CUBE_NAME")] = new Literal(
cubename);
newnode[measuremap.get("?MEASURE_UNIQUE_NAME")] = anIntermediaryresult[measuremap
.get("?MEASURE_UNIQUE_NAME")];
newnode[measuremap.get("?MEASURE_NAME")] = anIntermediaryresult[measuremap
.get("?MEASURE_NAME")];
newnode[measuremap.get("?MEASURE_CAPTION")] = anIntermediaryresult[measuremap
.get("?MEASURE_CAPTION")];
newnode[measuremap.get("?DATA_TYPE")] = anIntermediaryresult[measuremap
.get("?DATA_TYPE")];
newnode[measuremap.get("?MEASURE_IS_VISIBLE")] = anIntermediaryresult[measuremap
.get("?MEASURE_IS_VISIBLE")];
newnode[measuremap.get("?MEASURE_AGGREGATOR")] = anIntermediaryresult[measuremap
.get("?MEASURE_AGGREGATOR")];
newnode[measuremap.get("?EXPRESSION")] = anIntermediaryresult[measuremap
.get("?EXPRESSION")];
// Only add if not already contained.
// For measures, we add them all.
measures.add(newnode);
}
}
// Assume dimensions / hierarchies / levels to be used from first
// cube.
this.dimensions = inputiterator1.getDimensions(restrictions);
this.hierarchies = inputiterator1.getHierarchies(restrictions);
this.levels = inputiterator1.getLevels(restrictions);
// Can we use members?
// One problem could be that this may be a huge number of members
this.members = inputiterator1.getMembers(restrictions);
} catch (OlapException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
private void createData(PhysicalOlapIterator root1,
PhysicalOlapIterator root2) {
List<Node[]> results = new ArrayList<Node[]>();
Restrictions emptyrestrictions = new Restrictions();
try {
List<Node[]> root1_dimensions = root1
.getDimensions(emptyrestrictions);
List<Node[]> root2_dimensions = root2
.getDimensions(emptyrestrictions);
// First check whether equal dimensions.
boolean equal = true;
// For now, we assume the same ordering of dimensions (probably the
// case due to the query, anyway)
Map<String, Integer> dimensionmap = Olap4ldLinkedDataUtil
.getNodeResultFields(root1_dimensions.get(0));
for (int i = 0; i < root1_dimensions.size(); i++) {
Node[] root1_dimension = root1_dimensions.get(i);
Node[] root2_dimension = root2_dimensions.get(i);
// Measure dimension should also be the same
// if
// (!root1_dimension[dimensionmap.get("?DIMENSION_UNIQUE_NAME")]
// .toString().equals(Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME))
// {
// continue;
// }
// XXX: Maybe consider reasoning
if (!root1_dimension[dimensionmap.get("?DIMENSION_UNIQUE_NAME")]
.toString().equals(
root2_dimension[dimensionmap
.get("?DIMENSION_UNIQUE_NAME")]
.toString())) {
equal = false;
}
}
if (!equal) {
throw new UnsupportedOperationException(
"Drill-across only over equally-structured cubes!");
} else {
// Do join
List<Node[]> root1_measures = root1
.getMeasures(emptyrestrictions);
List<Node[]> root2_measures = root2
.getMeasures(emptyrestrictions);
// We check if all measures are the same
boolean allmeasuresthesame = areAllMeasuresTheSame(
root1_measures, root2_measures);
// Nested-loop
boolean firstroot1 = true;
while (root1.hasNext()) {
Node[] root1_node = (Node[]) root1.next();
List<Node> result = new ArrayList<Node>();
try {
root2.init();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while (root2.hasNext()) {
Node[] root2_node = (Node[]) root2.next();
// Consider measure dimension and header
equal = false;
// XXX: Check whether root1_node = root2_node if so,
// then...
// Currently we assume same dimension ordering and same
// members.
String concat1 = "";
String concat2 = "";
for (int i = 0; i < root1_dimensions.size() - 2; i++) {
concat1 += root1_node[i].toString();
}
for (int i = 0; i < root2_dimensions.size() - 2; i++) {
concat2 += root2_node[i].toString();
}
if (concat1.hashCode() == concat2.hashCode()) {
equal = true;
}
if (equal) {
// Add to result
// Add dimensions
for (int i = 0; i < root1_dimensions.size() - 2; i++) {
result.add(root1_node[i]);
}
if (allmeasuresthesame) {
// Concat measures of both cubes
// Under assumptions both cubes have the same
// measures.
for (int i = 0; i < root1_measures.size() - 1; i++) {
Resource newnode;
// Here, header is different, we do not want to concat.
if (firstroot1 == true) {
newnode = new Resource(
root1_node[root1_dimensions
.size() - 2 + i]
.toString());
firstroot1 = false;
} else {
// Need to create new node
newnode = new Resource(
root1_node[root1_dimensions
.size() - 2 + i]
+ " / "
+ root2_node[root1_dimensions
.size() - 2 + i]);
}
result.add(newnode);
}
} else {
// Here first (header) is same as all other.
// Add measures of cube one
for (int i = root1_dimensions.size() - 2; i < root1_dimensions
.size() - 2 + root1_measures.size() - 1; i++) {
result.add(root1_node[i]);
}
// Add measures of cube two
for (int i = root1_dimensions.size() - 2
+ root1_measures.size() - 1; i < root1_dimensions
.size()
- 2
+ root1_measures.size()
- 1
+ root2_measures.size() - 1; i++) {
result.add(root2_node[i
- root1_measures.size() + 1]);
}
}
results.add(result.toArray(new Node[1]));
/*
* Performance optimisation: Shall we have an outer
* loop join? The problem would be that then for the
* same dimension members, we would have several
* facts. Can we be sure that this really is the
* case? Yes, if we have integrity constraint checks
* on the original data. Therefore, we assume it.
*/
break;
}
}
}
// Outer join
if (allmeasuresthesame) {
// Left outer
try {
root1.init();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
while (root1.hasNext()) {
Node[] root1_node = (Node[]) root1.next();
List<Node> result = new ArrayList<Node>();
try {
root2.init();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while (root2.hasNext()) {
Node[] root2_node = (Node[]) root2.next();
// Consider measure dimension and header
equal = false;
// XXX: Check whether root1_node = root2_node if so,
// then...
// Currently we assume same dimension ordering and
// same
// members.
String concat1 = "";
String concat2 = "";
for (int i = 0; i < root1_dimensions.size() - 2; i++) {
concat1 += root1_node[i].toString();
}
for (int i = 0; i < root2_dimensions.size() - 2; i++) {
concat2 += root2_node[i].toString();
}
if (concat1.hashCode() == concat2.hashCode()) {
equal = true;
}
if (equal) {
/*
* Performance optimisation: Shall we have an
* outer loop join? The problem would be that
* then for the same dimension members, we would
* have several facts. Can we be sure that this
* really is the case? Yes, if we have integrity
* constraint checks on the original data.
* Therefore, we assume it.
*/
break;
}
}
if (!equal) {
// Add dimensions
for (int i = 0; i < root1_dimensions.size() - 2; i++) {
result.add(root1_node[i]);
}
// Add measures of cube one
for (int i = root1_dimensions.size() - 2; i < root1_dimensions
.size() - 2 + root1_measures.size() - 1; i++) {
result.add(root1_node[i]);
}
// Status: Why geo?
results.add(result.toArray(new Node[1]));
}
}
// Right outer
try {
root2.init();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
while (root2.hasNext()) {
Node[] root2_node = (Node[]) root2.next();
List<Node> result = new ArrayList<Node>();
try {
root1.init();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while (root1.hasNext()) {
Node[] root1_node = (Node[]) root1.next();
// Consider measure dimension and header
equal = false;
// XXX: Check whether root1_node = root2_node if so,
// then...
// Currently we assume same dimension ordering and
// same
// members.
String concat1 = "";
String concat2 = "";
for (int i = 0; i < root1_dimensions.size() - 2; i++) {
concat1 += root1_node[i].toString();
}
for (int i = 0; i < root2_dimensions.size() - 2; i++) {
concat2 += root2_node[i].toString();
}
if (concat1.hashCode() == concat2.hashCode()) {
equal = true;
}
if (equal) {
/*
* Performance optimisation: Shall we have an
* outer loop join? The problem would be that
* then for the same dimension members, we would
* have several facts. Can we be sure that this
* really is the case? Yes, if we have integrity
* constraint checks on the original data.
* Therefore, we assume it.
*/
break;
}
}
if (!equal) {
// Add dimensions
for (int i = 0; i < root2_dimensions.size() - 2; i++) {
result.add(root2_node[i]);
}
// Add measures of cube two
for (int i = root1_dimensions.size() - 2
+ root1_measures.size() - 1; i < root1_dimensions
.size()
- 2
+ root1_measures.size()
- 1
+ root2_measures.size() - 1; i++) {
result.add(root2_node[i - root1_measures.size()
+ 1]);
}
// Status: Why geo?
results.add(result.toArray(new Node[1]));
}
}
}
}
} catch (OlapException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.results = results;
}
private boolean areAllMeasuresTheSame(List<Node[]> root1_measures,
List<Node[]> root2_measures) {
Map<String, Integer> measurenmap = Olap4ldLinkedDataUtil
.getNodeResultFields(root1_measures.get(0));
boolean allmeasuresthesame = true;
if (root1_measures.size() != root2_measures.size()) {
allmeasuresthesame = false;
} else {
for (int i = 1; i < root1_measures.size(); i++) {
if (!root1_measures.get(i)[measurenmap
.get("?MEASURE_UNIQUE_NAME")].toString().equals(
root2_measures.get(i)[measurenmap
.get("?MEASURE_UNIQUE_NAME")].toString())) {
allmeasuresthesame = false;
}
}
}
return allmeasuresthesame;
}
@Override
public boolean hasNext() {
if (this.results == null) {
try {
init();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return iterator.hasNext();
}
@Override
public Object next() {
if (this.results == null) {
try {
init();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return iterator.next();
}
@Override
public void remove() {
// TODO Auto-generated method stub
}
@Override
public void init() throws Exception {
if (this.results == null) {
// Init
try {
// Does have input operators, therefore other init necessary.
inputiterator1.init();
inputiterator2.init();
createData(inputiterator1, inputiterator2);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.iterator = results.iterator();
}
@Override
public void close() throws Exception {
// something to do?
}
@Override
public String toString() {
return "Nested-Loop over (" + inputiterator1 + "," + inputiterator2 + ")";
}
@Override
public void accept(LogicalOlapOperatorQueryPlanVisitor v)
throws QueryException {
// Nothing done, yet.
;
}
@Override
public List<Node[]> getCubes(Restrictions restrictions)
throws OlapException {
return cubes;
}
@Override
public List<Node[]> getDimensions(Restrictions restrictions)
throws OlapException {
return dimensions;
}
@Override
public List<Node[]> getMeasures(Restrictions restrictions)
throws OlapException {
return measures;
}
@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;
}
}