public void SparqlBgpEvaluation() { //Prepare the Store TripleStore store = new TripleStore(); Graph g = new Graph(); FileLoader.Load(g, "Turtle.ttl"); store.Add(g); SparqlQueryParser parser = new SparqlQueryParser(); SparqlQuery q = parser.ParseFromString(@"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> SELECT * WHERE {?s ?p ?o . ?s rdfs:label ?label}"); Object testResult = store.ExecuteQuery(q); ISparqlAlgebra testAlgebra = q.ToAlgebra(); if (testResult is SparqlResultSet) { SparqlResultSet rset = (SparqlResultSet)testResult; Console.WriteLine(rset.Count + " Results"); foreach (SparqlResult r in rset) { Console.WriteLine(r.ToString()); } Console.WriteLine(); } //Create some Triple Patterns TriplePattern t1 = new TriplePattern(new VariablePattern("?s"), new VariablePattern("?p"), new VariablePattern("?o")); TriplePattern t2 = new TriplePattern(new VariablePattern("?s"), new NodeMatchPattern(g.CreateUriNode("rdfs:label")), new VariablePattern("?label")); TriplePattern t3 = new TriplePattern(new VariablePattern("?x"), new VariablePattern("?y"), new VariablePattern("?z")); TriplePattern t4 = new TriplePattern(new VariablePattern("?s"), new NodeMatchPattern(g.CreateUriNode(":name")), new VariablePattern("?name")); //Build some BGPs Bgp selectNothing = new Bgp(); Bgp selectAll = new Bgp(t1); Bgp selectLabelled = new Bgp(new List<ITriplePattern>() { t1, t2 }); Bgp selectAllDisjoint = new Bgp(new List<ITriplePattern>() { t1, t3 }); Bgp selectLabels = new Bgp(t2); Bgp selectNames = new Bgp(t4); //LeftJoin selectOptionalNamed = new LeftJoin(selectAll, new Optional(selectNames)); LeftJoin selectOptionalNamed = new LeftJoin(selectAll, selectNames); Union selectAllUnion = new Union(selectAll, selectAll); Union selectAllUnion2 = new Union(selectAllUnion, selectAll); Filter selectAllUriObjects = new Filter(selectAll, new UnaryExpressionFilter(new IsUriFunction(new VariableExpressionTerm("o")))); //Test out the BGPs //Console.WriteLine("{}"); //this.ShowMultiset(selectNothing.Evaluate(new SparqlEvaluationContext(null, store))); //Console.WriteLine("{?s ?p ?o}"); //this.ShowMultiset(selectAll.Evaluate(new SparqlEvaluationContext(null, store))); //Console.WriteLine("{?s ?p ?o . ?s rdfs:label ?label}"); //SparqlEvaluationContext context = new SparqlEvaluationContext(null, store); //this.ShowMultiset(selectLabelled.Evaluate(context)); //SparqlResultSet lvnResult = new SparqlResultSet(context); //Console.WriteLine("{?s ?p ?o . ?x ?y ?z}"); //this.ShowMultiset(selectAllDisjoint.Evaluate(new SparqlEvaluationContext(null, store))); //Console.WriteLine("{?s ?p ?o . OPTIONAL {?s :name ?name}}"); //this.ShowMultiset(selectOptionalNamed.Evaluate(new SparqlEvaluationContext(null, store))); Console.WriteLine("{{?s ?p ?o} UNION {?s ?p ?o}}"); this.ShowMultiset(selectAllUnion.Evaluate(new SparqlEvaluationContext(null, new InMemoryDataset(store)))); Console.WriteLine("{{?s ?p ?o} UNION {?s ?p ?o} UNION {?s ?p ?o}}"); this.ShowMultiset(selectAllUnion2.Evaluate(new SparqlEvaluationContext(null, new InMemoryDataset(store)))); Console.WriteLine("{?s ?p ?o FILTER (ISURI(?o))}"); this.ShowMultiset(selectAllUriObjects.Evaluate(new SparqlEvaluationContext(null, new InMemoryDataset(store)))); }
/// <summary> /// Optimises BGPs in the Algebra to use Filter() and Extend() rather than the embedded FILTER and BIND /// </summary> /// <param name="algebra">Algebra to optimise</param> /// <returns></returns> public ISparqlAlgebra Optimise(ISparqlAlgebra algebra) { if (algebra is IAbstractJoin) { return ((IAbstractJoin)algebra).Transform(this); } else if (algebra is IUnaryOperator) { return ((IUnaryOperator)algebra).Transform(this); } else if (algebra is IBgp) { //Don't integerfer with other optimisers which have added custom BGP implementations if (!(algebra is Bgp)) return algebra; IBgp current = (IBgp)algebra; if (current.PatternCount == 0) { return current; } else { ISparqlAlgebra result = new Bgp(); List<ITriplePattern> patterns = new List<ITriplePattern>(); List<ITriplePattern> ps = new List<ITriplePattern>(current.TriplePatterns.ToList()); for (int i = 0; i < current.PatternCount; i++) { //Can't split the BGP if there are Blank Nodes present if (!ps[i].HasNoBlankVariables) return current; if (!(ps[i] is TriplePattern)) { //First ensure that if we've found any other Triple Patterns up to this point //we dump this into a BGP and join with the result so far if (patterns.Count > 0) { result = Join.CreateJoin(result, new Bgp(patterns)); patterns.Clear(); } //Then generate the appropriate strict algebra operator if (ps[i] is FilterPattern) { result = new Filter(result, ((FilterPattern)ps[i]).Filter); } else if (ps[i] is BindPattern) { BindPattern bind = (BindPattern)ps[i]; result = new Extend(result, bind.AssignExpression, bind.VariableName); } else if (ps[i] is LetPattern) { LetPattern let = (LetPattern)ps[i]; result = new Extend(result, let.AssignExpression, let.VariableName); } else if (ps[i] is SubQueryPattern) { SubQueryPattern sq = (SubQueryPattern)ps[i]; result = Join.CreateJoin(result, new SubQuery(sq.SubQuery)); } else if (ps[i] is PropertyPathPattern) { PropertyPathPattern pp = (PropertyPathPattern)ps[i]; result = Join.CreateJoin(result, new PropertyPath(pp.Subject, pp.Path, pp.Object)); } } else { patterns.Add(ps[i]); } } if (patterns.Count == current.PatternCount) { //If count of remaining patterns same as original pattern count there was no optimisation //to do so return as is return current; } else if (patterns.Count > 0) { //If any patterns left at end join as a BGP with result so far result = Join.CreateJoin(result, new Bgp(patterns)); return result; } else { return result; } } } else if (algebra is ITerminalOperator) { return algebra; } else { return algebra; } }
/// <summary> /// Optimises BGPs in the Algebra to use Filter() and Extend() rather than the embedded FILTER and BIND /// </summary> /// <param name="algebra">Algebra to optimise</param> /// <returns></returns> public ISparqlAlgebra Optimise(ISparqlAlgebra algebra) { if (algebra is IAbstractJoin) { return ((IAbstractJoin)algebra).Transform(this); } else if (algebra is IUnaryOperator) { return ((IUnaryOperator)algebra).Transform(this); } else if (algebra is IBgp) { IBgp current = (IBgp)algebra; if (current.PatternCount == 0) { return current; } else { ISparqlAlgebra result = new Bgp(); List<ITriplePattern> patterns = new List<ITriplePattern>(); List<ITriplePattern> ps = new List<ITriplePattern>(current.TriplePatterns.ToList()); for (int i = 0; i < current.PatternCount; i++) { if (ps[i] is FilterPattern || ps[i] is BindPattern) { //First ensure that if we've found any other Triple Patterns up to this point //we dump this into a BGP and join with the result so far if (patterns.Count > 0) { result = Join.CreateJoin(result, new Bgp(patterns)); patterns.Clear(); } if (ps[i] is FilterPattern) { result = new Filter(result, ((FilterPattern)ps[i]).Filter); } else { BindPattern bind = (BindPattern)ps[i]; result = new Extend(result, bind.AssignExpression, bind.VariableName); } } else { patterns.Add(ps[i]); } } if (patterns.Count == current.PatternCount) { //If count of remaining patterns same as original pattern count there was no optimisation //to do so return as is return current; } else if (patterns.Count > 0) { //If any patterns left at end join as a BGP with result so far result = Join.CreateJoin(result, new Bgp(patterns)); return result; } else { return result; } } } else if (algebra is ITerminalOperator) { return algebra; } else { return algebra; } }
/// <summary> /// Runs the optimisation against a Filter algebra /// </summary> /// <param name="filter">The Filter algebra to optimise</param> /// <param name="optimisedAlgebra">Receives the optimised algebra if optimisation was performed or the input algebra otherwise</param> /// <returns>True if an optimisation was performed, false otherwise</returns> /// <remarks> /// <para>This implementation currently handles the simple case of a Filter applied to a BGP where the filter /// expression is either a single EqualsExpression or SameTermExpression or an AndExpression containing one or more /// EqualsExpression or SameTermExpression arguments. The implementation ensures that the replaced variable is still /// available to the outer algebra by inserting a BindPattern into the BGP. If the filter expression is a single /// EqualsExpression or SameTermExpression, the optimiser will also strip this out of the algebra, but with an /// AndExpression it will leave the full filter expression untouched.</para> /// <para>The implementation will replace only URI and PlainLiteral types</para> /// TODO: It should be possible to remove EqualsExpression and SameTermExpression instances from the AndExpression arguments and then either strip it out (if it has no remaining arguments), or optimise it to a single expression (if it has one remaining argument) /// </remarks> private bool OptimiseFilter(IFilter filter, out ISparqlAlgebra optimisedAlgebra) { if (!(filter.InnerAlgebra is Bgp)) { // Need a BGP to be able to insert BindPatterns for replaced variables optimisedAlgebra = filter; return false; } var filterExpression = filter.SparqlFilter.Expression; var replacementTerms = new Dictionary<string, INode>(); string var; INode term; bool equals; // Currently only handle the simple filter cases of a single identity expression // or an AND of expressions if (IsIdentityExpression(filterExpression, out var, out term, out equals)) { if (CanOptimize(term)) { replacementTerms.Add(var, term); } } else if (filterExpression is AndExpression) { foreach (var arg in filterExpression.Arguments) { if (IsIdentityExpression(arg, out var, out term, out equals) && CanOptimize(term)) { replacementTerms.Add(var, term); } else { foreach (var variable in arg.Variables) { // Cannot guarantee that the argument doesn't imply some other possible binding for the variables replacementTerms.Remove(variable); } } } } if (replacementTerms.Any()) { var optimisedInner = filter.InnerAlgebra as Bgp; foreach (var replacementEntry in replacementTerms) { try { // Replace the variable with a constant term wherever it appears and then add a Bind pattern // to ensure that the variable is bound for use in the outer algebra var t = new VariableSubstitutionTransformer(replacementEntry.Key, replacementEntry.Value); optimisedInner = t.Optimise(optimisedInner) as Bgp; optimisedInner = new Bgp( optimisedInner.TriplePatterns.Concat(new[] {new BindPattern(replacementEntry.Key, new ConstantTerm(replacementEntry.Value))})); } catch (RdfQueryException) { // Could not perform this replacement. } } if (filterExpression is AndExpression) { // Keep the filter as it may contain other necessary expressions // TODO: Could try to remove the identity expressions here ? optimisedAlgebra = new Filter(optimisedInner, filter.SparqlFilter); } else { // Can optimise away the filter entirely optimisedAlgebra = optimisedInner; } return true; } optimisedAlgebra = filter; return false; }
/// <summary> /// Converts the algebra back into a Graph Pattern. /// </summary> /// <returns></returns> public Patterns.GraphPattern ToGraphPattern() { ISparqlAlgebra algebra = new Filter(new Join(_lhs, _rhs), new UnaryExpressionFilter(_expr)); return(algebra.ToGraphPattern()); }
/// <summary> /// Converts the algebra back into a query. /// </summary> /// <returns></returns> public SparqlQuery ToQuery() { ISparqlAlgebra algebra = new Filter(new Join(_lhs, _rhs), new UnaryExpressionFilter(_expr)); return(algebra.ToQuery()); }