package org.aksw.sparqlify.sparqlview;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;
import org.aksw.commons.collections.CartesianProduct;
import org.aksw.commons.collections.stacks.NestedStack;
import org.aksw.commons.util.Pair;
import org.aksw.commons.util.reflect.MultiMethod;
import org.aksw.jena_sparql_api.exprs_ext.E_StrConcatPermissive;
import org.aksw.jena_sparql_api.normal_form.Clause;
import org.aksw.jena_sparql_api.restriction.RestrictionImpl;
import org.aksw.jena_sparql_api.restriction.RestrictionManagerImpl;
import org.aksw.jena_sparql_api.utils.QuadUtils;
import org.aksw.jena_sparql_api.utils.ReplaceConstants;
import org.aksw.jena_sparql_api.utils.expr.NodeValueUtils;
import org.aksw.jena_sparql_api.views.Dialect;
import org.aksw.jena_sparql_api.views.MyOpAsQuery;
import org.aksw.jena_sparql_api.views.PrefixSet;
import org.aksw.jena_sparql_api.views.RdfTermType;
import org.aksw.jena_sparql_api.views.SparqlView;
import org.aksw.jena_sparql_api.views.SparqlifyConstants;
import org.aksw.jena_sparql_api.views.TwoWayBinding;
import org.aksw.sparqlify.database.Constraint;
import org.aksw.sparqlify.database.EqualsConstraint;
import org.aksw.sparqlify.database.IndexMetaNode;
import org.aksw.sparqlify.database.IsPrefixOfConstraint;
import org.aksw.sparqlify.database.MetaIndexFactory;
import org.aksw.sparqlify.database.OpFilterIndexed;
import org.aksw.sparqlify.database.PrefixIndex;
import org.aksw.sparqlify.database.PrefixIndexMetaFactory;
import org.aksw.sparqlify.database.StartsWithConstraint;
import org.aksw.sparqlify.database.Table;
import org.aksw.sparqlify.database.TableBuilder;
import org.aksw.sparqlify.database.TreeIndex;
import org.aksw.sparqlify.database.VariableConstraint;
import org.apache.commons.collections15.Transformer;
import org.apache.jena.graph.Node;
import org.apache.jena.query.Query;
import org.apache.jena.sparql.algebra.Algebra;
import org.apache.jena.sparql.algebra.Op;
import org.apache.jena.sparql.algebra.op.Op1;
import org.apache.jena.sparql.algebra.op.OpDisjunction;
import org.apache.jena.sparql.algebra.op.OpDistinct;
import org.apache.jena.sparql.algebra.op.OpExtend;
import org.apache.jena.sparql.algebra.op.OpFilter;
import org.apache.jena.sparql.algebra.op.OpGroup;
import org.apache.jena.sparql.algebra.op.OpJoin;
import org.apache.jena.sparql.algebra.op.OpLeftJoin;
import org.apache.jena.sparql.algebra.op.OpOrder;
import org.apache.jena.sparql.algebra.op.OpProject;
import org.apache.jena.sparql.algebra.op.OpQuadPattern;
import org.apache.jena.sparql.algebra.op.OpSlice;
import org.apache.jena.sparql.algebra.op.OpUnion;
import org.apache.jena.sparql.core.Quad;
import org.apache.jena.sparql.core.QuadPattern;
import org.apache.jena.sparql.core.Var;
import org.apache.jena.sparql.expr.E_Equals;
import org.apache.jena.sparql.expr.E_StrConcat;
import org.apache.jena.sparql.expr.Expr;
import org.apache.jena.sparql.expr.ExprFunction;
import org.apache.jena.sparql.expr.ExprList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
interface ViewFactory<T>
{
T create(Quad queryQuad, Quad viewQuad, int instanceId, int subId, T parent, TwoWayBinding binding);
}
class ViewQuad {
private SparqlView view;
private Quad quad;
// TODO Maybe another field for some constraints
public ViewQuad(SparqlView view, Quad quad) {
this.view = view;
this.quad = quad;
}
public SparqlView getView() {
return view;
}
public Quad getQuad() {
return quad;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((quad == null) ? 0 : quad.hashCode());
result = prime * result + ((view == null) ? 0 : view.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ViewQuad other = (ViewQuad) obj;
if (quad == null) {
if (other.quad != null)
return false;
} else if (!quad.equals(other.quad))
return false;
if (view == null) {
if (other.view != null)
return false;
} else if (!view.equals(other.view))
return false;
return true;
}
@Override
public String toString() {
return view.toString() + ":" + quad.toString();
}
}
class Context {
}
//class NestedStack<T>
//{
// private NestedStack<T> parent;
// private T value;
//
// public NestedStack(NestedStack<T> parent, T value) {
// super();
// this.parent = parent;
// this.value = value;
// }
//
// public NestedStack<T> getParent() {
// return parent;
// }
//
// public T getValue() {
// return value;
// }
//
//
// public List<T> asList() {
// List<T> result = new ArrayList<T>();
//
// NestedStack<T> current = this;
// while(current != null) {
// result.add(current.getValue());
// current = current.parent;
// }
//
// Collections.reverse(result);
//
// return result;
// }
//}
interface ISparqlViewSystem {
void addView(SparqlView view);
Op getApplicableViews(Query query);
// List the views registered with the system
Collection<SparqlView> getViews();
}
public class SparqlViewSystem
implements ISparqlViewSystem
{
private Logger logger = LoggerFactory.getLogger(SparqlViewSystem.class);
private int viewId = 1;
private Table<Object> table;
PrefixIndex<Object> idxTest;
private Set<SparqlView> views = new HashSet<SparqlView>();
public SparqlViewSystem() {
TableBuilder<Object> builder = new TableBuilder<Object>();
builder.addColumn("g_prefix", String.class);
builder.addColumn("s_prefix", String.class);
builder.addColumn("p_prefix", String.class);
builder.addColumn("o_type", Integer.class);
builder.addColumn("o_prefix", String.class);
builder.addColumn("view", ViewQuad.class);
table = builder.create();
Transformer<Object, Set<String>> prefixExtractor = new Transformer<Object, Set<String>>() {
@Override
public Set<String> transform(Object input) {
return Collections.singleton(input.toString());
}
};
MetaIndexFactory factory = new PrefixIndexMetaFactory(prefixExtractor);
//MetaIndexFactory factory = new PatriciaAccessorFactory(prefixExtractor);
IndexMetaNode root = IndexMetaNode.create(table, factory, "s_prefix");
IndexMetaNode s = IndexMetaNode.create(root, factory, "p_prefix");
TreeIndex.attach(table, root);
//IndexMetaNode o = IndexMetaNode.create(s, factory, "o");
IndexMetaNode root2 = IndexMetaNode.create(table, factory, "p_prefix");
IndexMetaNode s2 = IndexMetaNode.create(root2, factory, "s_prefix");
TreeIndex.attach(table, root2);
//IndexMetaNode o = IndexMetaNode.create(s, factory, "o");
/*
idxS = PrefixIndex.attach(prefixExtractor, table, "s_prefix");
PrefixIndex.attach(, "s_prefix");
idxTest = PrefixIndex.attach(prefixExtractor, table, "p_prefix");
idxTest = PrefixIndex.attach(prefixExtractor, table, "o_prefix");
*/
}
public static Query rewrite(Query query, SparqlViewSystem system, Dialect dialect) {
Op rewrittenOp = system.getApplicableViews(query);
Query result = MyOpAsQuery.asQuery(rewrittenOp, dialect);
System.out.println("Rewritten query: " + result);
return result;
}
@Override
public void addView(SparqlView view) {
++viewId;
Set<Var> vars = view.getVarsMentioned();
Map<Node, Node> rename = new HashMap<Node, Node>();
for(Var var : vars) {
rename.put(var, Var.alloc("view" + viewId + "_" + var.getName()));
}
SparqlView copy = view.copySubstitute(rename);
// Rename the variables in the view to make them globally unique
System.out.println("Renamed: " + copy);
this.views.add(copy);
index(copy);
}
public static Constraint deriveConstraint(Expr expr) {
if(expr instanceof E_StrConcat || expr instanceof E_StrConcatPermissive) {
return deriveConstraint(expr);
}
return null;
}
public static StartsWithConstraint deriveConstraint(E_StrConcat expr) {
return deriveConstraint(expr);
}
public static StartsWithConstraint deriveConstraint(E_StrConcatPermissive expr) {
return deriveConstraint(expr);
}
public static StartsWithConstraint deriveConstraintConcat(ExprFunction concat) {
// TODO If all arguments are constant, we could infer a constant constraint
String prefix = "";
for(Expr arg : concat.getArgs()) {
if(arg.isConstant()) {
prefix += arg.getConstant().asUnquotedString();
} else {
break;
}
}
return new StartsWithConstraint(prefix);
}
public Map<Var, RdfTermType> deriveTypeConstraints(SparqlView view) {
Map<Var, RdfTermType> result = new HashMap<Var, RdfTermType>();
for(Entry<Node, Expr> entry : view.getBinding().entrySet()) {
Var var =
(Var)entry.getKey();
ExprFunction termCtor = (ExprFunction)entry.getValue();
// TODO Use the type field of RdfTerm
//String functionIri = termCtor.getFunctionSymbol().toString();
String functionIri = termCtor.getFunctionSymbol().getSymbol();
if(functionIri.equals(SparqlifyConstants.rdfTermLabel)) {
Expr arg = termCtor.getArg(1);
if(arg.isConstant()) {
Object o = NodeValueUtils.getValue(arg.getConstant());
Number number = (Number)o;
switch(number.intValue()) {
case 1:
result.put(var, RdfTermType.URI);
break;
case 2:
case 3:
result.put(var, RdfTermType.LITERAL);
break;
}
}
} else if(functionIri.equals(SparqlifyConstants.uriLabel)) {
result.put(var, RdfTermType.URI);
} else if(functionIri.equals(SparqlifyConstants.plainLiteralLabel) || functionIri.equals(SparqlifyConstants.typedLiteralLabel)) {
result.put(var, RdfTermType.LITERAL);
}
}
return result;
}
/**
* Derive prefix constraints for variables based on
* variable definitions:
*
* concat('constant', var, rest) -> prefix = 'constant'
*
* TODO: Actually we should not add these constraints to the view, but just return them
*
*/
public void deriveRestrictions(SparqlView view) {
}
/*
public void deriveRestrictions(SparqlView view) {
RestrictionManager restrictions = view.getRestrictions();
for(Entry<Var, PrefixSet> entry : view.getConstraints().getVarPrefixConstraints().entrySet()) {
restrictions.stateUriPrefixes(entry.getKey(), entry.getValue());
}
for(Entry<Node, Expr> entry : view.getBinding().entrySet()) {
Var var = (Var)entry.getKey();
ExprFunction termCtor = (ExprFunction)entry.getValue();
/*
if(!(expr instanceof RdfTerm)) {
throw new RuntimeException("RdfTerm expected");
}* /
// TODO We assume RdfTerm here for now, but should check
Expr expr = termCtor.getArgs().get(1);
if(expr instanceof E_StrConcat || expr instanceof E_StrConcatPermissive) {
StartsWithConstraint constraint = deriveConstraintConcat((ExprFunction)expr);
restrictions.stateUriPrefixes(var, new PrefixSet(constraint.getPrefix()));
//RdfTerm<Constraint> constraint = new RdfTerm<Constraint>(null, new StartsWithConstraint(prefix), null, null);
//view.getConstraints().add(var, constraint);
}
}
}
*/
public static RdfTermType getType(Node node, RestrictionManagerImpl restrictions) {
if(node.isVariable()) {
RestrictionImpl r = restrictions.getRestriction((Var)node);
if(r != null) {
return r.getType();
}
} else if(node.isURI()) {
return RdfTermType.URI;
} else if(node.isLiteral()) {
return RdfTermType.LITERAL;
}
return RdfTermType.UNKNOWN;
}
private void index(SparqlView view) {
if(view.getName().equals("lgd_node_tags_string")) {
System.out.println("Debug");
}
RestrictionManagerImpl restrictions = new RestrictionManagerImpl();
view.setRestrictions(restrictions);
deriveRestrictions(view);
//derivePrefixConstraints(view);
// Index the pattern constraints
/*
Map<Var, PrefixSet> prefixConstraints = view.getConstraints().getVarPrefixConstraints();
for(Entry<Var, PrefixSet> entry : prefixConstraints.entrySet()) {
restrictions.stateUriPrefixes(entry.getKey(), entry.getValue());
}
Map<Var, Type> typeConstraints = deriveTypeConstraints(view);
for(Entry<Var, Type> entry : typeConstraints.entrySet()) {
restrictions.stateType(entry.getKey(), entry.getValue());
}
*/
for(Quad quad : view.getQuadPattern()) {
List<Collection<?>> collections = new ArrayList<Collection<?>>();
for(int i = 0; i < 4; ++i) {
Node node = QuadUtils.getNode(quad, i);
if(i == 3) {
RdfTermType type = getType(node, restrictions);
switch(type) {
case URI:
collections.add(Collections.singleton(1));
break;
case LITERAL:
collections.add(Collections.singleton(2));
break;
default:
// Either URI or literal
collections.add(Arrays.asList(1, 2));
break;
}
}
if(node.isVariable()) {
PrefixSet p = null; // TODO prefixConstraints.get(node);
if(p != null) {
collections.add(p.getSet());
} else {
collections.add(Collections.singleton(""));
}
} else if (node.isURI()) {
collections.add(Collections.singleton(node.getURI()));
/* } else if(node.isLiteral()) {
collections.add(Collections.singleton(node.getLiteralLexicalForm()));
*/
} else {
throw new RuntimeException("Should not happen");
}
}
ViewQuad viewQuad = new ViewQuad(view, quad);
CartesianProduct<Object> cartesian = new CartesianProduct<Object>(collections);
for(List<Object> item : cartesian) {
List<Object> row = new ArrayList<Object>(item);
row.add(viewQuad);
table.add(row);
}
}
/*
List<ExprList> clauses = DnfUtils.toClauses(view.getFilter());
System.out.println("DNF = " + clauses);
Set<Set<Expr>> dnf = FilterUtils.toSets(clauses);
for(Quad quad : view.getQuadPattern()) {
Set<Set<Expr>> filter = FilterUtils.determineFilterDnf(quad, dnf);
Map<Var, ValueSet<NodeValue>> constraints = FilterUtils.extractValueConstraintsDnf(filter);
System.out.println("For quad " + quad + " got expr " + filter);
System.out.println("Value const = " + constraints);
//graphs.add(new FilteredGraph(quad, filter));
}*/
}
public Op getApplicableViews(Query query)
{
Op op = Algebra.compile(query);
op = Algebra.toQuadForm(op);
op = ReplaceConstants.replace(op);
//op = FilterPlacementOptimizer.optimize(op);
// Add a projection if the query contains a result star
// in order to filter out auto-generated variables
if(query.isSelectType() && query.isQueryResultStar()) {
List<Var> vars = query.getProjectVars();
op = new OpProject(op, vars);
}
System.out.println("Quad form:" + op);
//Set<OpSparqlViewPattern> result = getApplicableViews(op);
//Set<OpSparqlViewPattern> result = getApplicableViews(op);
//TransformFilterPlacement transformer = new TransformFilterPlacement();
//op.(transformer);
//op = Algebra.optimize(op);
Op augmented = _getApplicableViews(op);
//Op optimizedFilters = FilterPlacementOptimizer2.optimize(augmented);
//System.out.println(optimizedFilters);
Op result = augmented;
//Op result = optimizedFilters;
System.out.println(result);
return result;
//return getApp
}
/**
* If a variable equals a (uri or string) constant, it means that the view must provide
* a prefix for that value.
*
* @param a
* @param b
* @return
*/
public static VariableConstraint deriveIsPrefixOfConstraint(Expr a, Expr b)
{
if(!(a.isVariable() && b.isConstant())) {
return null;
}
Object value = NodeValueUtils.getValue(b.getConstant());
return new VariableConstraint(a.getVarName(), new IsPrefixOfConstraint(value.toString()));
}
/**
* Returns IsPrefixOf Constraints for equality expressions between variables and constants.
*
* Used for looking up view candidates.
* Not used for satisfiability checks.
*
*
* @param expr
* @return
*/
public static VariableConstraint deriveViewLookupConstraint(Expr expr) {
if(expr instanceof E_Equals) {
E_Equals e = (E_Equals)expr;
VariableConstraint c = deriveIsPrefixOfConstraint(e.getArg1(), e.getArg2());
if(c == null) {
c = deriveIsPrefixOfConstraint(e.getArg2(), e.getArg1());
}
return c;
}
else {
return null;
}
}
/**
* Order the quads of the quadPattern by selectivity.
*
* Iterate the quads in order, and for each quad, do a lookup of the
* views that may yield answers to it.
* So for each quad we get a set of candidate bindings to the views.
*
* Binding is a mapping from query-var to a set of view variables/constant
* The view varibale may have constraints on it, which carry over to the query variable.
*
* So given
* Create View x ...
* {?x a Class . }
* With ?x.value prefix "foo"
*
* a query Select {?s ?p ?o }
* and a binding(?s = ?x),
* then we can infer the constraint on ?x.value prefix "foo"
*
* So if there is a second pattern
* { ?x a Class. ?x label ?z }
*
* Then we can use this constraint on the lookup at the second pattern.
*
* Note that the selectivity of query quad pattern change with the bindings,
* so we should be able to quickly update the selectivity of the quads in the presence of bindings.
*
* Another note: Maybe we could treat constants as specially delimited prefixes,
* such as 'aaaa' = prefix
* 'aaaaa$' = constant
*/
public List<SparqlViewConjunction> getApplicableViewsBase(OpQuadPattern op, RestrictionManagerImpl restrictions)
{
List<SparqlViewConjunction> result = new ArrayList<SparqlViewConjunction>();
QuadPattern queryQuads = op.getPattern(); //PatternUtils.collectQuads(op);
//RestrictionManager restrictions = new RestrictionManager(exprs);
Pair<NavigableMap<Integer, Set<Quad>>, Map<Quad, Set<ViewQuad>>> candidates = findQuadWithFewestViewCandidates(queryQuads, restrictions);
NavigableMap<Integer, Set<Quad>> nToQuads = candidates.getKey();
Map<Quad, Set<ViewQuad>> quadToCandidates = candidates.getValue();
List<Quad> order = new ArrayList<Quad>();
for(Set<Quad> quads : nToQuads.values()) {
order.addAll(quads);
}
System.out.println("Order:\n" + Joiner.on("\n").join(order));
Set<ViewQuad> viewQuads = quadToCandidates.get(order.get(0));
getApplicableViewsRec2(0, order, viewQuads, quadToCandidates, restrictions, null, result);
return result;
}
private static final String[] columnNames = new String[]{"g_prefix", "s_prefix", "p_prefix", "o_prefix"};
public Set<ViewQuad> findCandidates(Quad quad, RestrictionManagerImpl restrictions) {
//Multimap<Quad, ViewQuad> quadToView = HashMultimap.create();
Set<Map<String, Constraint>> constraints = new HashSet<Map<String, Constraint>>();
Set<Var> quadVars = QuadUtils.getVarsMentioned(quad);
Set<Clause> dnf = restrictions.getEffectiveDnf(quadVars);
// TODO Get clauses by var
RestrictionImpl[] termRestriction = new RestrictionImpl[4];
for(Clause clause : dnf) {
Map<String, Constraint> columnConstraints = new HashMap<String, Constraint>();
// Prefix constraints
for(int i = 0; i < 4; ++i) {
Node n = QuadUtils.getNode(quad, i);
if(!(n instanceof Var)) {
System.out.println("debug");
}
Var var = (Var)QuadUtils.getNode(quad, i);
RestrictionImpl r = clause.getRestriction(var);
termRestriction[i] = r;
if(r == null) {
continue;
}
if(r.getType().equals(RdfTermType.URI) && r.hasConstant()) {
String columnName = columnNames[i];
columnConstraints.put(columnName, new IsPrefixOfConstraint(r.getNode().getURI()));
}
}
// Object type constraint
RestrictionImpl r = termRestriction[3];
if(r != null) {
switch(r.getType()) {
case URI:
columnConstraints.put("o_type", new EqualsConstraint(1));
break;
case LITERAL:
columnConstraints.put("o_type", new EqualsConstraint(2));
break;
}
}
// TODO Remove subsumed constraints
constraints.add(columnConstraints);
}
if(constraints.isEmpty()) {
// Add a dummy element to look up all views in the subsequent loop
constraints.add(new HashMap<String, Constraint>());
}
Set<ViewQuad> viewQuads = new HashSet<ViewQuad>();
for(Map<String, Constraint> columnConstraints : constraints) {
Collection<List<Object>> rows = table.select(columnConstraints);
/*
System.out.println("BEGIN");
System.out.println("Constraints: " + columnConstraints);
TableImpl.printTable(rows, System.out);
System.out.println("END");
*/
for(List<Object> row : rows) {
// The view is the last element of the list
ViewQuad viewQuad = (ViewQuad)row.get(row.size() - 1);
viewQuads.add(viewQuad);
}
}
return viewQuads;
}
public Pair<NavigableMap<Integer, Set<Quad>>, Map<Quad, Set<ViewQuad>>> findQuadWithFewestViewCandidates(QuadPattern queryQuads, RestrictionManagerImpl restrictions)
{
//Map<Integer, Map<Quad, Set<ViewQuad>>> quadToView = new TreeMap<Integer, Map<Quad, Set<ViewQuad>>>();
NavigableMap<Integer, Set<Quad>> nToQuads = new TreeMap<Integer, Set<Quad>>();
Map<Quad, Set<ViewQuad>> quadToCandidates = new HashMap<Quad, Set<ViewQuad>>();
for(Quad quad : queryQuads) {
if(quadToCandidates.containsKey(quad)) {
continue;
}
Set<ViewQuad> viewQuads = findCandidates(quad, restrictions);
int n = viewQuads.size();
Set<Quad> nQuads = nToQuads.get(n);
if(nQuads == null) {
nQuads = new HashSet<Quad>();
nToQuads.put(n, nQuads);
}
nQuads.add(quad);
quadToCandidates.put(quad, viewQuads);
}
return Pair.create(nToQuads, quadToCandidates);
}
public static List<String> getCandidateNames(NestedStack<SparqlViewInstance> instances) {
List<String> viewNames = new ArrayList<String>();
if(instances != null) {
for(SparqlViewInstance instance : instances.asList()) {
viewNames.add(instance.getParent().getName());
}
}
return viewNames;
}
/**
*
* @param index
* @param quadOrder
* @param viewQuads The viewQuads provided by the parent invocation - with any restrictions encountered during the rewrite taken into account
* @param candidates The viewQuads here are the ones from only analyzing the query filter
* In some cases (small candidate sets) it might be faster filtering this set of view candidates, rather than doing a new lookup.
* @param restrictions
* @param result
*/
public void getApplicableViewsRec2(int index, List<Quad> quadOrder, Set<ViewQuad> viewQuads, Map<Quad, Set<ViewQuad>> candidates, RestrictionManagerImpl restrictions, NestedStack<SparqlViewInstance> instances, List<SparqlViewConjunction> result)
{
List<String> debug = Arrays.asList("view_nodes", "node_tags_resource_kv"); // "view_lgd_relation_specific_resources");
List<String> viewNames = new ArrayList<String>();
if(instances != null) {
for(SparqlViewInstance instance : instances.asList()) {
viewNames.add(instance.getParent().getName());
}
}
if(index >= quadOrder.size()) {
// We expect at least one quad - Bail out of the recursion happens at the end
throw new RuntimeException("Should not happen");
}
int nextIndex = index + 1;
boolean isRecursionEnd = nextIndex == quadOrder.size();
Quad queryQuad = quadOrder.get(index);
/*
System.out.println(index + " " + queryQuad);
for(ViewQuad viewQuad : viewQuads) {
System.out.println("\t" + viewQuad);
}
System.out.println("");
*/
//Set<ViewQuad> viewQuads = candidates.get(queryQuad);
int subId = 0;
for(ViewQuad viewQuad : viewQuads) {
++subId;
String viewName = viewQuad.getView().getName();
if(viewName.equals("view_nodes")) {
System.out.println("debug");
}
if(viewNames.containsAll(debug) && viewName.equals("view_lgd_relation_specific_resources")) {
System.out.println("debug");
}
if(viewName.equals("view_lgd_relation_specific_resources")) {
System.out.println("debug");
}
RestrictionManagerImpl subRestrictions = new RestrictionManagerImpl(restrictions);
RestrictionManagerImpl viewRestrictions = viewQuad.getView().getRestrictions();
for(int i = 0; i < 4; ++i) {
Var queryVar = (Var)QuadUtils.getNode(queryQuad, i);
Node viewNode = QuadUtils.getNode(viewQuad.getQuad(), i);
if(viewNode.isVariable()) {
Var viewVar = (Var)viewNode;
RestrictionImpl viewRs = viewRestrictions.getRestriction(viewVar);
if(viewRs != null) {
subRestrictions.stateRestriction(queryVar, viewRs);
}
if(subRestrictions.isUnsatisfiable()) {
break;
}
//subRestrictions.stateEqual(queryVar, viewVar);
if(subRestrictions.isUnsatisfiable()) {
break;
}
} else {
subRestrictions.stateNode(queryVar, viewNode);
}
if(subRestrictions.isUnsatisfiable()) {
break;
}
}
if(subRestrictions.isUnsatisfiable()) {
continue;
}
// TODO The restriction manager supersedes the two way binding
// But changing that is a bit of work
TwoWayBinding binding = TwoWayBinding.getVarMappingTwoWay(queryQuad, viewQuad.getQuad());
// Try to join this instance with the candidates of the other quads
int instanceId = index;
// TODO Use a View factory
SparqlViewInstance instance = new SparqlViewInstance(queryQuad, viewQuad.getQuad(), instanceId, subId, viewQuad.getView(), binding);
// Try adding the restrictions of the view to the subRestriction
// TODO: Use the restrictions of the view, rather than using "the inferred defining exprs"
/*
RestrictionManager viewRestrictions = instance.getParent().getRestrictions();
isUnsatisfiable = false;
Set<Var> queryQuadVars = QuadUtils.getVarsMentioned(queryQuad);
for(Var queryQuadVar : queryQuadVars) {
Restriction viewR = viewRestrictions.getRestriction(queryQuadVar);
if(viewR != null) {
subRestrictions.stateRestriction(queryQuadVar, viewR);
}
isUnsatisfiable = subRestrictions.isUnsatisfiable();
if(isUnsatisfiable) {
break;
}
/*
Collection<Expr> es = instance.getInferredDefiningExprs(queryQuadVar);
for(Expr expr : es) {
VariableConstraint vc = deriveViewLookupConstraint(expr);
if(vc != null) {
System.out.println(vc);
}
// Check if this constraint has an impact on the satisfiability
isUnsatisfiable = subRestriction.getSatisfiability() == Boolean.FALSE;
if(isUnsatisfiable) {
break;
}
}
* /
if(isUnsatisfiable) {
break;
}
}
if(isUnsatisfiable) {
continue;
}
*/
NestedStack<SparqlViewInstance> nextInstances = new NestedStack<SparqlViewInstance>(instances, instance);
if(isRecursionEnd) {
System.out.println("got: " + getCandidateNames(nextInstances));
/*
TwoWayBinding completeBinding = new TwoWayBinding();
List<SparqlViewInstance> list = instances.asList();
for(SparqlViewInstance item : list) {
completeBinding.addAll(item.getBinding());
}*/
SparqlViewConjunction viewConjunction = new SparqlViewConjunction(nextInstances.asList(), subRestrictions);
result.add(viewConjunction);
// We have reached the end!
// Yield another view conjunction
continue;
} else {
Quad nextQuad = quadOrder.get(nextIndex);
// With the new restriction do a lookup for the next quad
Set<ViewQuad> nextCandidates = findCandidates(nextQuad, subRestrictions);
getApplicableViewsRec2(nextIndex, quadOrder, nextCandidates, candidates, subRestrictions, nextInstances, result);
}
}
}
// A thing I noticed: Actually I don't want to pick the most selective quad (the one with the most filters/least view candidates),
// but those quads that most likely cause unsatisfiabilities.
// Hm, but actually: If I pick those quads with the least view candidates first, then I will quickly
// Get to those quads causing contradictions
public Op getApplicableViews(OpQuadPattern op, RestrictionManagerImpl restrictions)
{
throw new RuntimeException("Dont use this class anymore");
/*
List<SparqlViewConjunction> conjunctions = getApplicableViewsBase(op, restrictions);
OpDisjunction result = OpDisjunction.create();
for(SparqlViewConjunction item : conjunctions) {
Op tmp = new OpSparqlViewPattern(item);
result.add(tmp);
}
return result;
*/
//return null;
//return new OpRdfUnionViewPattern(conjunctions);
}
public static boolean isSatisfiable(List<SparqlViewInstance> list)
{
TwoWayBinding completeBinding = new TwoWayBinding();
boolean isOk = true;
for(SparqlViewInstance item : list) {
if(!completeBinding.isCompatible(item.getBinding())) {
isOk = false;
break;
}
completeBinding.addAll(item.getBinding());
}
return isOk;
}
/**
* Given a sparql query in quad form, this method replaces
* (sub sets of) quad patterns with view instances (view patterns)
*
* The method also passes the filter conditions that an op must
* satisfy.
*
* @param op
* @return
*/
public Op _getApplicableViews(Op op)
{
return _getApplicableViews(op, new RestrictionManagerImpl());
}
public Op _getApplicableViews(Op op, RestrictionManagerImpl restrictions)
{
return MultiMethod.invoke(this, "getApplicableViews", op, restrictions);
}
public Op getApplicableViews(OpProject op, RestrictionManagerImpl restrictions) {
return new OpProject(_getApplicableViews(op.getSubOp(), restrictions), op.getVars());
}
public Op getApplicableViews(OpOrder op, RestrictionManagerImpl restrictions) {
return new OpOrder(_getApplicableViews(op.getSubOp(), restrictions), op.getConditions());
}
public Op getApplicableViews(OpGroup op, RestrictionManagerImpl restrictions) {
return new OpGroup(_getApplicableViews(op.getSubOp(), restrictions), op.getGroupVars(), op.getAggregators());
}
// We treat OpExtend as a filter for now
// TODO Huh??? Is op extend simply a projection????
/*
public Op getApplicableViews(OpExtend op, RestrictionManager _restrictions) {
RestrictionManager restrictions = new RestrictionManager(_restrictions);
for(Var var : op.getVarExprList().getVars()) {
Expr expr = op.getVarExprList().getExpr(var);
Expr item = new E_Equals(new ExprVar(var), expr);
restrictions.stateExpr(item);
}
//return _getApplicableViews(OpFilterIndexed.filter(restrictions, op.getSubOp()), restrictions);
return OpFilterIndexed.filter(restrictions, _getApplicableViews(op.getSubOp(), restrictions));
}
*/
public Op getApplicableViews(OpExtend op, RestrictionManagerImpl _restrictions) {
return OpExtend.extend(_getApplicableViews(op.getSubOp()), op.getVarExprList());
}
public Op getApplicableViews(OpFilter op, RestrictionManagerImpl restrictions)
{
/*
RestrictionManager subRestrictions = new RestrictionManager(restrictions);
for(Expr expr : op.getExprs()) {
subRestrictions.stateExpr(expr);
}
return OpFilter.filter(op.getExprs(), _getApplicableViews(op.getSubOp(), subRestrictions));
*/
RestrictionManagerImpl subRestrictions = new RestrictionManagerImpl(restrictions);
for(Expr expr : op.getExprs()) {
subRestrictions.stateExpr(expr);
}
return OpFilterIndexed.filter(subRestrictions, _getApplicableViews(op.getSubOp(), subRestrictions));
}
public Op getApplicableViews(OpUnion op, RestrictionManagerImpl restrictions)
{
//ExprList subExprsLeft = new ExprList(exprs);
//ExprList subExprsRight = new ExprList(exprs);
RestrictionManagerImpl subRestrictionsLeft = new RestrictionManagerImpl(restrictions);
RestrictionManagerImpl subRestrictionsRight = new RestrictionManagerImpl(restrictions);
//return new OpDisjunction.
return OpDisjunction.create(_getApplicableViews(op.getLeft(), subRestrictionsLeft), _getApplicableViews(op.getRight(), subRestrictionsRight));
//return new OpUnion(getApplicableViews(op.getLeft(), subExprsLeft), getApplicableViews(op.getRight(), subExprsRight));
}
public Op getApplicableViews(OpJoin op, RestrictionManagerImpl restrictions) {
return OpJoin.create(_getApplicableViews(op.getLeft(), restrictions), _getApplicableViews(op.getRight(), restrictions));
}
public Op getApplicableViews(OpLeftJoin op, RestrictionManagerImpl restrictions)
{
Op left = _getApplicableViews(op.getLeft(), restrictions);
//List<RestrictionManager> moreRestrictions = getRestrictions(left);
RestrictionManagerImpl subRestrictions = new RestrictionManagerImpl(restrictions);
RestrictionManagerImpl moreRestrictions = getRestrictions2(left);
if(moreRestrictions != null) {
subRestrictions.stateRestriction(moreRestrictions);
}
if(op.getExprs() != null) {
for(Expr expr : op.getExprs()) {
subRestrictions.stateExpr(expr);
}
}
//RestrictionManager union = RestrictionManager.createUnion(moreRestrictions);
//System.out.println(union);
Op right = _getApplicableViews(op.getRight(), subRestrictions);
return OpLeftJoin.create(left, right, new ExprList());
}
public Op getApplicableViews(OpSlice op, RestrictionManagerImpl restrictions)
{
return new OpSlice(_getApplicableViews(op.getSubOp(), restrictions), op.getStart(), op.getLength());
}
public Op getApplicableViews(OpDistinct op, RestrictionManagerImpl restrictions)
{
return new OpDistinct(_getApplicableViews(op.getSubOp(), restrictions));
}
public static RestrictionManagerImpl getRestrictions2(Op op) {
if(op instanceof OpFilterIndexed) {
return ((OpFilterIndexed) op).getRestrictions();
} else if(op instanceof Op1) {
return getRestrictions2(((Op1) op).getSubOp());
} else if(op instanceof OpJoin) {
throw new RuntimeException("TODO Merge the restrictions of both sides of the join");
} else if(op instanceof OpLeftJoin) {
return getRestrictions2(((OpLeftJoin) op).getLeft());
} else if(op instanceof OpDisjunction) {
return null; // TODO We could factor out restrictions common to all elements
/*} else if(op instanceof OpSparqlViewPattern) {
return null; */
} else {
throw new RuntimeException("Should not happen");
}
}
public static List<RestrictionManagerImpl> getRestrictions(Op op) {
List<RestrictionManagerImpl> result = new ArrayList<RestrictionManagerImpl>();
getRestrictions(op, result);
return result;
}
/**
*
* Returns a disjunction (list) of restrictions that apply for a given node
*/
public static void getRestrictions(Op op, Collection<RestrictionManagerImpl> result) {
if(op instanceof Op1) {
getRestrictions(((Op1) op).getSubOp(), result);
} else if(op instanceof OpJoin) {
throw new RuntimeException("TODO Merge the restrictions of both sides of the join");
} else if(op instanceof OpLeftJoin) {
getRestrictions(((OpLeftJoin) op).getLeft(), result);
} else if(op instanceof OpDisjunction) {
OpDisjunction o = (OpDisjunction)op;
for(Op subOp : o.getElements()) {
getRestrictions(subOp, result);
}
} else if(op instanceof OpSparqlViewPattern) {
OpSparqlViewPattern o = (OpSparqlViewPattern)op;
result.add(o.getConjunction().getRestrictions());
} else {
throw new RuntimeException("Should not happen");
}
}
/*
public static void main(String[] args) {
Query query = QueryFactory.create("Select Distinct ?s {?s ?p ?o }");
Op op = Algebra.compile(query);
op = Algebra.toQuadForm(op);
System.out.println(op);
}*/
/*
public static void main(String[] args)
throws Exception
{
File configFile = new File("examples/LinkedGeoData - PrefixTest.sparqlify");
ConfigParser parser = new ConfigParser();
InputStream in = new FileInputStream(configFile);
Config config;
try {
config = parser.parse(in);
} finally {
in.close();
}
SparqlViewSystem2 system = new SparqlViewSystem2();
ConfiguratorSparqlViewSystem.configure(config, system);
Query query = QueryFactory.create("Select * { ?s a <http://www.w3.org/2002/07/owl#Class> . ?s ?p ?o . Filter(?o = <http://o1> || ?o = <http://o2>) .}");
system.getApplicableViews(query);
}*/
@Override
public Collection<SparqlView> getViews() {
return views;
}
/**
* About my index:
* Actually i'd like an index that can provide multiple "access-paths":
* So for each column, a set of different indexes can be used:
*
* For instance, I could index by column "o" using a prefix index or a tree set index (supports ordering)
*
* Basically, my idea of indexing is having a map<K, Object>, whereas different map implementations
* support efficiently answering different constraints (?a greater constant, ?a startsWith, etc...)
*
* MultiIndex idx = new MultiIndex();
* SubIndex sub = idx.addTreeMap("columnName")
* sub.add
*
*
* sub.add("columnName",
*
*
*/
}