/// <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 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); } }
private void TryParseLetAssignment(SparqlQueryParserContext context, GraphPattern p) { if (context.SyntaxMode == SparqlQuerySyntax.Sparql_1_0) throw new RdfParseException("LET assignment is not supported in SPARQL 1.0"); if (context.SyntaxMode == SparqlQuerySyntax.Sparql_1_1) throw new RdfParseException("LET assignment is not supported in SPARQL 1.1 - use BIND assignment instead"); IToken variable; ISparqlExpression expr; //Firstly we expect an opening bracket, a variable and then an assignment operator IToken next = context.Tokens.Dequeue(); if (next.TokenType == Token.LEFTBRACKET) { next = context.Tokens.Dequeue(); if (next.TokenType == Token.VARIABLE) { variable = next; context.Query.AddVariable(variable.Value, false); next = context.Tokens.Dequeue(); if (next.TokenType == Token.ASSIGNMENT) { //See if there is a valid expression for the right hand side of the assignment next = context.Tokens.Peek(); switch (next.TokenType) { case Token.ABS: case Token.BNODE: case Token.BOUND: case Token.CEIL: case Token.COALESCE: case Token.CONCAT: case Token.DATATYPEFUNC: case Token.DAY: case Token.ENCODEFORURI: case Token.EXISTS: case Token.FLOOR: case Token.HOURS: case Token.IF: case Token.IRI: case Token.ISBLANK: case Token.ISIRI: case Token.ISLITERAL: case Token.ISNUMERIC: case Token.ISURI: case Token.LANG: case Token.LANGMATCHES: case Token.LCASE: case Token.MINUTES: case Token.MONTH: case Token.NOTEXISTS: case Token.NOW: case Token.RAND: case Token.REGEX: case Token.ROUND: case Token.SAMETERM: case Token.SECONDS: case Token.SHA1: case Token.SHA224: case Token.SHA256: case Token.SHA384: case Token.SHA512: case Token.STR: case Token.CONTAINS: case Token.STRDT: case Token.STRENDS: case Token.STRLANG: case Token.STRLEN: case Token.STRSTARTS: case Token.SUBSTR: case Token.TIMEZONE: case Token.TZ: case Token.UCASE: case Token.URIFUNC: case Token.YEAR: case Token.URI: case Token.QNAME: expr = this.TryParseFunctionExpression(context); break; case Token.LEFTBRACKET: context.Tokens.Dequeue(); expr = this.TryParseExpression(context, false); break; case Token.VARIABLE: context.Tokens.Dequeue(); expr = new VariableExpressionTerm(next.Value); break; default: throw ParserHelper.Error("Unexpected Token '" + next.GetType().ToString() + "' encountered, expected a Token which was valid as the start of an expression for the right hand side of a LET assignment", next); } //Finally expect a Right Bracket to terminate the LET next = context.Tokens.Dequeue(); if (next.TokenType != Token.RIGHTBRACKET) { throw ParserHelper.Error("Unexpected Token '" + next.GetType().ToString() + "' encountered, expected a Right Bracket to terminate the LET assignment", next); } //Create a Let Pattern and add to the Query appropriately LetPattern let = new LetPattern(variable.Value.Substring(1), expr); if (Options.QueryOptimisation) { p.AddAssignment(let); } else { //When Optimisation is turned off we'll just stick the Let in the Triples Pattern where it occurs //since we're not going to do any Triple Pattern ordering, Assignment or FILTER placement p.AddTriplePattern(let); } } else { throw ParserHelper.Error("Unexpected Token '" + next.GetType().ToString() + "' encountered, expected an Assignment operator as part of a LET assignment", next); } } else { throw ParserHelper.Error("Unexpected Token '" + next.GetType().ToString() + "' encountered, expected a Variable as the first item in a LET assignment", next); } } else { throw ParserHelper.Error("Unexpected Token '" + next.GetType().ToString() + "' encountered, expected a Left Bracket to start a LET assignment after a LET Keyword", next); } }
/// <summary> /// Formats a Triple Pattern in nicely formatted SPARQL syntax /// </summary> /// <param name="tp">Triple Pattern</param> /// <returns></returns> public virtual String Format(ITriplePattern tp) { StringBuilder output = new StringBuilder(); if (tp is TriplePattern) { TriplePattern match = (TriplePattern)tp; output.Append(this.Format(match.Subject, TripleSegment.Subject)); output.Append(' '); output.Append(this.Format(match.Predicate, TripleSegment.Predicate)); output.Append(' '); output.Append(this.Format(match.Object, TripleSegment.Object)); output.Append(" ."); } else if (tp is FilterPattern) { FilterPattern filter = (FilterPattern)tp; output.Append("FILTER("); output.Append(this.FormatExpression(filter.Filter.Expression)); output.Append(")"); } else if (tp is SubQueryPattern) { SubQueryPattern subquery = (SubQueryPattern)tp; output.AppendLine("{"); output.AppendLineIndented(this.Format(subquery.SubQuery), 2); output.AppendLine("}"); } else if (tp is PropertyPathPattern) { PropertyPathPattern path = (PropertyPathPattern)tp; output.Append(this.Format(path.Subject, TripleSegment.Subject)); output.Append(' '); output.Append(this.FormatPath(path.Path)); output.Append(' '); output.Append(this.Format(path.Object, TripleSegment.Object)); output.Append(" ."); } else if (tp is LetPattern) { LetPattern let = (LetPattern)tp; output.Append("LET(?"); output.Append(let.VariableName); output.Append(" := "); output.Append(this.FormatExpression(let.AssignExpression)); output.Append(")"); } else if (tp is BindPattern) { BindPattern bind = (BindPattern)tp; output.Append("BIND ("); output.Append(this.FormatExpression(bind.AssignExpression)); output.Append(" AS ?"); output.Append(bind.VariableName); output.Append(")"); } else { throw new RdfOutputException("Unable to Format an unknown ITriplePattern implementation as a String"); } return(output.ToString()); }
/// <summary> /// Attempts to do variable substitution within the given algebra /// </summary> /// <param name="algebra">Algebra</param> /// <returns></returns> public ISparqlAlgebra Optimise(ISparqlAlgebra algebra) { //By default we are only safe to replace objects in a scope if we are replacing with a constant //Note that if we also make a replace in a subject/predicate position for a variable replace then //that makes object replacement safe for that scope only bool canReplaceObjects = (this._canReplaceCustom ? this._canReplaceObjects : this._replaceItem is NodeMatchPattern); if (algebra is IBgp) { IBgp bgp = (IBgp)algebra; if (bgp.PatternCount == 0) { return(bgp); } //Do variable substitution on the patterns List <ITriplePattern> ps = new List <ITriplePattern>(); foreach (ITriplePattern p in bgp.TriplePatterns) { if (p is TriplePattern) { TriplePattern tp = (TriplePattern)p; PatternItem subj = tp.Subject.VariableName != null && tp.Subject.VariableName.Equals(this._findVar) ? this._replaceItem : tp.Subject; if (ReferenceEquals(subj, this._replaceItem)) { canReplaceObjects = (this._canReplaceCustom ? this._canReplaceObjects : true); } PatternItem pred = tp.Predicate.VariableName != null && tp.Predicate.VariableName.Equals(this._findVar) ? this._replaceItem : tp.Predicate; if (ReferenceEquals(pred, this._replaceItem)) { canReplaceObjects = (this._canReplaceCustom ? this._canReplaceObjects : true); } PatternItem obj = tp.Object.VariableName != null && tp.Object.VariableName.Equals(this._findVar) ? this._replaceItem : tp.Object; if (ReferenceEquals(obj, this._replaceItem) && !canReplaceObjects) { throw new Exception("Unable to substitute a variable into the object position in this scope"); } ps.Add(new TriplePattern(subj, pred, obj)); } else if (p is FilterPattern) { FilterPattern fp = (FilterPattern)p; ps.Add(new FilterPattern(new UnaryExpressionFilter(this.Transform(fp.Filter.Expression)))); } else if (p is BindPattern) { BindPattern bp = (BindPattern)p; ps.Add(new BindPattern(bp.VariableName, this.Transform(bp.AssignExpression))); } else if (p is LetPattern) { LetPattern lp = (LetPattern)p; ps.Add(new LetPattern(lp.VariableName, this.Transform(lp.AssignExpression))); } else if (p is SubQueryPattern) { throw new RdfQueryException("Cannot do variable substitution when a sub-query is present"); } else if (p is PropertyPathPattern) { throw new RdfQueryException("Cannot do variable substitution when a property path is present"); } else { throw new RdfQueryException("Cannot do variable substitution on unknown triple patterns"); } } return(new Bgp(ps)); } else if (algebra is Service) { throw new RdfQueryException("Cannot do variable substitution when a SERVICE clause is present"); } else if (algebra is SubQuery) { throw new RdfQueryException("Cannot do variable substitution when a sub-query is present"); } else if (algebra is IPathOperator) { throw new RdfQueryException("Cannot do variable substitution when a property path is present"); } else if (algebra is Algebra.Graph) { Algebra.Graph g = (Algebra.Graph)((IUnaryOperator)algebra).Transform(this); if (g.GraphSpecifier is VariableToken && g.GraphSpecifier.Value.Equals("?" + this._findVar)) { if (this._replaceToken != null) { return(new Algebra.Graph(g.InnerAlgebra, this._replaceToken)); } else { throw new RdfQueryException("Cannot do a variable substitution when the variable is used for a GRAPH specifier and the replacement term is not a URI"); } } else { return(g); } } else if (algebra is IUnaryOperator) { return(((IUnaryOperator)algebra).Transform(this)); } else if (algebra is IAbstractJoin) { return(((IAbstractJoin)algebra).Transform(this)); } else if (algebra is ITerminalOperator) { return(algebra); } else { throw new RdfQueryException("Cannot do variable substitution on unknown algebra"); } }