/// <summary> /// Tries to place assignments at the earliest point possible i.e. the first point after which all required variables have occurred /// </summary> /// <param name="gp">Graph Pattern</param> /// <param name="assignment">Assignment (LET/BIND)</param> /// <returns></returns> private bool TryPlaceAssignment(GraphPattern gp, IAssignmentPattern assignment) { //Firstly we need to find out what variables are needed in the Assignment //The Variables property will include the variable that the Assignment assigns to so we can safely remove this List<String> variablesNeeded = assignment.Variables.Distinct().ToList(); variablesNeeded.Remove(assignment.VariableName); //If there are no Variables Needed we can just place the assignment at the start //This implies that the assignment sets something to a fixed value if (variablesNeeded.Count == 0) { gp.InsertAssignment(assignment, 0); return true; } //Then we need to move through the Triple Patterns and find the first place at which all the //Variables used in the Assignment have been used in ordinary Triple Patterns List<String> variablesUsed = new List<string>(); for (int p = 0; p < gp.TriplePatterns.Count; p++) { if (gp.TriplePatterns[p] is TriplePattern || gp.TriplePatterns[p] is IAssignmentPattern) { foreach (String var in gp.TriplePatterns[p].Variables) { if (!variablesUsed.Contains(var)) variablesUsed.Add(var); } //Have all the Variables we need now been used in a Pattern? if (variablesNeeded.All(v => variablesUsed.Contains(v))) { //We can place this Assignment after the Pattern we were just looking at gp.InsertAssignment(assignment, p + 1); return true; } } } //If we reach here then this means that all the Variables used in the Assignment did not occur //in the Triple Patterns which means they likely occur in child graph patterns (or the query //is malformed). In this case we cannot place the Assignment and it has to be applied post-commit //rather than during Triple Pattern execution return false; }
/// <summary> /// Adds an Assignment to the Graph Pattern respecting any BGP breaks /// </summary> /// <param name="p">Assignment Pattern</param> internal void AddAssignment(IAssignmentPattern p) { if (this._break) { if (this._broken) { this._graphPatterns.Last().AddAssignment(p); } else { GraphPattern breakPattern = new GraphPattern(); breakPattern.AddAssignment(p); this._graphPatterns.Add(breakPattern); } } else { this._unplacedAssignments.Add(p); this.BreakBGP(); } }
/// <summary> /// Inserts an Assignment at a given position /// </summary> /// <param name="assignment">Assignment</param> /// <param name="i">Position to insert at</param> /// <remarks> /// Intended for use by Query Optimisers /// </remarks> public void InsertAssignment(IAssignmentPattern assignment, int i) { if (!this._unplacedAssignments.Contains(assignment)) throw new RdfQueryException("Cannot Insert an Assignment that is not currently an unplaced Assignment in this Graph Pattern"); this._unplacedAssignments.Remove(assignment); this._triplePatterns.Insert(i, assignment); }
/// <summary> /// Compares this Let to another Let. /// </summary> /// <param name="other">Let to compare to.</param> /// <returns>Just calls the base compare method since that implements all the logic we need.</returns> public int CompareTo(IAssignmentPattern other) { return(base.CompareTo(other)); }
/// <summary> /// Optimises the algebra so that all Node terms are virtualised /// </summary> /// <param name="algebra">Algebra</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()); TNodeID nullID = this._provider.NullID; for (int i = 0; i < current.PatternCount; i++) { if (ps[i].PatternType == TriplePatternType.Filter || ps[i].PatternType == TriplePatternType.BindAssignment || ps[i].PatternType == TriplePatternType.LetAssignment) { // 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].PatternType == TriplePatternType.Filter) { result = new Filter(result, new UnaryExpressionFilter(this.Transform(((IFilterPattern)ps[i]).Filter.Expression))); } else { IAssignmentPattern bind = (IAssignmentPattern)ps[i]; result = new Extend(result, this.Transform(bind.AssignExpression), bind.VariableName); } } else if (ps[i].PatternType == TriplePatternType.Match) { // Convert Terms in the Pattern into Virtual Nodes IMatchTriplePattern tp = (IMatchTriplePattern)ps[i]; PatternItem subj, pred, obj; if (tp.Subject is NodeMatchPattern) { TNodeID id = this._provider.GetID(((NodeMatchPattern)tp.Subject).Node); if (id == null || id.Equals(nullID)) { result = new NullOperator(current.Variables); break; } else { subj = new NodeMatchPattern(this.CreateVirtualNode(id, ((NodeMatchPattern)tp.Subject).Node)); } } else { subj = tp.Subject; } if (tp.Predicate is NodeMatchPattern) { TNodeID id = this._provider.GetID(((NodeMatchPattern)tp.Predicate).Node); if (id == null || id.Equals(nullID)) { result = new NullOperator(current.Variables); break; } else { pred = new NodeMatchPattern(this.CreateVirtualNode(id, ((NodeMatchPattern)tp.Predicate).Node)); } } else { pred = tp.Predicate; } if (tp.Object is NodeMatchPattern) { TNodeID id = this._provider.GetID(((NodeMatchPattern)tp.Object).Node); if (id == null || id.Equals(nullID)) { result = new NullOperator(current.Variables); break; } else { obj = new NodeMatchPattern(this.CreateVirtualNode(id, ((NodeMatchPattern)tp.Object).Node)); } } else { obj = tp.Object; } patterns.Add(new TriplePattern(subj, pred, obj)); } else { // Can't optimize if other pattern types involved return(current); } } if (result is NullOperator) { return(result); } 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> /// 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(); switch (tp.PatternType) { case TriplePatternType.Match: IMatchTriplePattern match = (IMatchTriplePattern)tp; output.Append(Format(match.Subject, TripleSegment.Subject)); output.Append(' '); output.Append(Format(match.Predicate, TripleSegment.Predicate)); output.Append(' '); output.Append(Format(match.Object, TripleSegment.Object)); output.Append(" ."); break; case TriplePatternType.Filter: IFilterPattern filter = (IFilterPattern)tp; output.Append("FILTER("); output.Append(FormatExpression(filter.Filter.Expression)); output.Append(")"); break; case TriplePatternType.SubQuery: ISubQueryPattern subquery = (ISubQueryPattern)tp; output.AppendLine("{"); output.AppendLineIndented(Format(subquery.SubQuery), 2); output.AppendLine("}"); break; case TriplePatternType.Path: IPropertyPathPattern path = (IPropertyPathPattern)tp; output.Append(Format(path.Subject, TripleSegment.Subject)); output.Append(' '); output.Append(FormatPath(path.Path)); output.Append(' '); output.Append(Format(path.Object, TripleSegment.Object)); output.Append(" ."); break; case TriplePatternType.LetAssignment: IAssignmentPattern let = (IAssignmentPattern)tp; output.Append("LET(?"); output.Append(let.VariableName); output.Append(" := "); output.Append(FormatExpression(let.AssignExpression)); output.Append(")"); break; case TriplePatternType.BindAssignment: IAssignmentPattern bind = (IAssignmentPattern)tp; output.Append("BIND ("); output.Append(FormatExpression(bind.AssignExpression)); output.Append(" AS ?"); output.Append(bind.VariableName); output.Append(")"); break; case TriplePatternType.PropertyFunction: IPropertyFunctionPattern propFunc = (IPropertyFunctionPattern)tp; if (propFunc.SubjectArgs.Count() > 1) { output.Append("( "); foreach (PatternItem arg in propFunc.SubjectArgs) { output.Append(Format(arg, TripleSegment.Subject)); output.Append(' '); } output.Append(')'); } else { output.Append(Format(propFunc.SubjectArgs.First(), TripleSegment.Subject)); } output.Append(" <"); output.Append(FormatUri(propFunc.PropertyFunction.FunctionUri)); output.Append("> "); if (propFunc.ObjectArgs.Count() > 1) { output.Append("( "); foreach (PatternItem arg in propFunc.ObjectArgs) { output.Append(Format(arg, TripleSegment.Object)); output.Append(' '); } output.Append(')'); } else { output.Append(Format(propFunc.ObjectArgs.First(), TripleSegment.Object)); } output.Append(" ."); break; default: 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 = (_canReplaceCustom ? _canReplaceObjects : _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) { switch (p.PatternType) { case TriplePatternType.Match: IMatchTriplePattern tp = (IMatchTriplePattern)p; PatternItem subj = tp.Subject.VariableName != null && tp.Subject.VariableName.Equals(_findVar) ? _replaceItem : tp.Subject; if (ReferenceEquals(subj, _replaceItem)) { canReplaceObjects = (_canReplaceCustom ? _canReplaceObjects : true); } PatternItem pred = tp.Predicate.VariableName != null && tp.Predicate.VariableName.Equals(_findVar) ? _replaceItem : tp.Predicate; if (ReferenceEquals(pred, _replaceItem)) { canReplaceObjects = (_canReplaceCustom ? _canReplaceObjects : true); } PatternItem obj = tp.Object.VariableName != null && tp.Object.VariableName.Equals(_findVar) ? _replaceItem : tp.Object; if (ReferenceEquals(obj, _replaceItem) && !canReplaceObjects) { throw new Exception("Unable to substitute a variable into the object position in this scope"); } ps.Add(new TriplePattern(subj, pred, obj)); break; case TriplePatternType.Filter: IFilterPattern fp = (IFilterPattern)p; ps.Add(new FilterPattern(new UnaryExpressionFilter(Transform(fp.Filter.Expression)))); break; case TriplePatternType.BindAssignment: IAssignmentPattern bp = (IAssignmentPattern)p; ps.Add(new BindPattern(bp.VariableName, Transform(bp.AssignExpression))); break; case TriplePatternType.LetAssignment: IAssignmentPattern lp = (IAssignmentPattern)p; ps.Add(new LetPattern(lp.VariableName, Transform(lp.AssignExpression))); break; case TriplePatternType.SubQuery: throw new RdfQueryException("Cannot do variable substitution when a sub-query is present"); case TriplePatternType.Path: throw new RdfQueryException("Cannot do variable substitution when a property path is present"); case TriplePatternType.PropertyFunction: throw new RdfQueryException("Cannot do variable substituion when a property function is present"); default: 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("?" + _findVar)) { if (_replaceToken != null) { return(new Algebra.Graph(g.InnerAlgebra, _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"); } }
/// <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].PatternType != TriplePatternType.Match) { // 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 switch (ps[i].PatternType) { case TriplePatternType.Filter: result = new Filter(result, ((IFilterPattern)ps[i]).Filter); break; case TriplePatternType.BindAssignment: case TriplePatternType.LetAssignment: IAssignmentPattern assignment = (IAssignmentPattern)ps[i]; result = new Extend(result, assignment.AssignExpression, assignment.VariableName); break; case TriplePatternType.SubQuery: ISubQueryPattern sq = (ISubQueryPattern)ps[i]; result = Join.CreateJoin(result, new SubQuery(sq.SubQuery)); break; case TriplePatternType.Path: IPropertyPathPattern pp = (IPropertyPathPattern)ps[i]; result = Join.CreateJoin(result, new PropertyPath(pp.Subject, pp.Path, pp.Object)); break; case TriplePatternType.PropertyFunction: IPropertyFunctionPattern pf = (IPropertyFunctionPattern)ps[i]; result = new PropertyFunction(result, pf.PropertyFunction); break; default: throw new RdfQueryException("Cannot apply strict algebra form to a BGP containing a unknown triple pattern type"); } } 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); } }