/// <summary> /// Causes the Graph Pattern to be optimised if it isn't already /// </summary> /// <param name="gp">Graph Pattern</param> /// <param name="variables">Variables that have occurred prior to this Pattern</param> public void Optimise(GraphPattern gp, IEnumerable <String> variables) { // Our Variables is initially only those in our Triple Patterns since // anything else is considered to be out of scope List <String> ourVariables = (from tp in gp.TriplePatterns from v in tp.Variables select v).Distinct().ToList(); // Start by sorting the Triple Patterns in the list according to the ranking function gp.TriplePatterns.Sort(GetRankingComparer()); // Apply reordering unless an optimiser has chosen to disable it if (ShouldReorder) { if (gp.TriplePatterns.Count > 0) { // After we sort which gives us a rough optimisation we then may want to reorder // based on the Variables that occurred previous to us OR if we're the Root Graph Pattern if (!variables.Any()) { // Optimise this Graph Pattern // No previously occurring variables so must be the first Graph Pattern if (gp.TriplePatterns.Count > 1) { HashSet <String> currVariables = new HashSet <String>(); gp.TriplePatterns[0].Variables.ForEach(v => currVariables.Add(v)); for (int i = 1; i < gp.TriplePatterns.Count - 1; i++) { if (currVariables.Count == 0) { gp.TriplePatterns[i].Variables.ForEach(v => currVariables.Add(v)); continue; } else if (currVariables.IsDisjoint(gp.TriplePatterns[i].Variables)) { TryReorderPatterns(gp, currVariables.ToList(), i + 1, i); gp.TriplePatterns[i].Variables.ForEach(v => currVariables.Add(v)); } else { gp.TriplePatterns[i].Variables.ForEach(v => currVariables.Add(v)); } } } } else { // Optimise this Graph Pattern based on previously occurring variables if (gp.TriplePatterns.Count > 1 && !gp.TriplePatterns[0].Variables.Any(v => variables.Contains(v)) && variables.Intersect(ourVariables).Any()) { TryReorderPatterns(gp, variables.ToList(), 1, 0); } else if (gp.TriplePatterns.Count > 2) { // In the case where there are more than 2 patterns then we can try and reorder these // in order to further optimise the pattern TryReorderPatterns(gp, gp.TriplePatterns[0].Variables, 2, 1); } } } } if (ShouldPlaceAssignments) { // First we need to place Assignments (LETs) in appropriate places within the Pattern // This happens before Filter placement since Filters may use variables assigned to in LETs if (gp.UnplacedAssignments.Any()) { // Need to ensure that we sort Assignments // This way those that use fewer variables get placed first List <IAssignmentPattern> ps = gp.UnplacedAssignments.OrderBy(x => x).ToList(); // This next bit goes in a do loop as we want to keep attempting to place assignments while // we are able to do so. If the count of unplaced assignments has decreased but is not // zero it may be that we were unable to place some patterns as they relied on variables // assigned in other LETs which weren't placed when we attempted to place them // When we reach the point where no further placements have occurred or all assignments // are placed we stop trying to place assignments int c; do { c = ps.Count; int i = 0; while (i < ps.Count) { if (TryPlaceAssignment(gp, ps[i])) { // Remove from Unplaced Assignments since it's been successfully placed in the Triple Patterns // Don't increment the counter since the next Assignment is now at the index we're already at ps.RemoveAt(i); } else { // Unable to place so increment counter i++; } } } while (c > ps.Count && ps.Count > 0); } } // Regardless of what we've placed already we now place all remaining assignments // foreach (IAssignmentPattern assignment in gp.UnplacedAssignments.ToList()) // { // gp.InsertAssignment(assignment, gp.TriplePatterns.Count); // } if (ShouldPlaceFilters) { // Then we need to place the Filters in appropriate places within the Pattern if (gp.UnplacedFilters.Any()) { if (gp.TriplePatterns.Count == 0) { // Where there are no Triple Patterns the Graph Pattern just contains this Filter and possibly some // child Graph Patterns. In such a case then we shouldn't place the Filters } else { if (ShouldSplitFilters) { // See whether we can split any/all of the Unplaced Filters List <ISparqlFilter> fs = gp.UnplacedFilters.ToList(); for (int i = 0; i < fs.Count; i++) { ISparqlFilter f = fs[i]; if (f.Expression is AndExpression) { // Split the And // Note that multiple nested And's are handled by the fact that we will continue working through the list until it is finished UnaryExpressionFilter lhs = new UnaryExpressionFilter(f.Expression.Arguments.First()); UnaryExpressionFilter rhs = new UnaryExpressionFilter(f.Expression.Arguments.Last()); fs.RemoveAt(i); fs.Add(lhs); fs.Add(rhs); } } // Finally we need to ensure the Unplaced Filters list is appropriately updated gp.ResetFilters(fs); } foreach (ISparqlFilter f in gp.UnplacedFilters.ToList()) { TryPlaceFilter(gp, f); } } } } // Finally optimise the Child Graph Patterns foreach (GraphPattern cgp in gp.ChildGraphPatterns) { // At each point the variables that have occurred are those in the Triple Patterns and // those in previous Graph Patterns cgp.Optimise(this, ourVariables); ourVariables.AddRange(cgp.Variables); } // Note: Any remaining Unplaced Filters/Assignments are OK since the ToAlgebra() method of a GraphPattern // will take care of placing these appropriately }
/// <summary> /// Evaluates the filtered product /// </summary> /// <param name="context">Evaluation Context</param> /// <returns></returns> public BaseMultiset Evaluate(SparqlEvaluationContext context) { BaseMultiset initialInput = context.InputMultiset; BaseMultiset lhsResults = context.Evaluate(this._lhs); if (lhsResults is NullMultiset || lhsResults.IsEmpty) { //If LHS Results are Null/Empty then end result will always be null so short circuit context.OutputMultiset = new NullMultiset(); } else { context.InputMultiset = initialInput; BaseMultiset rhsResults = context.Evaluate(this._rhs); if (rhsResults is NullMultiset || rhsResults.IsEmpty) { //If RHS Results are Null/Empty then end results will always be null so short circuit context.OutputMultiset = new NullMultiset(); } else if (rhsResults is IdentityMultiset) { //Apply Filter over LHS Results only - defer evaluation to filter implementation context.InputMultiset = lhsResults; UnaryExpressionFilter filter = new UnaryExpressionFilter(this._expr); filter.Evaluate(context); context.OutputMultiset = lhsResults; } else { //Calculate the product applying the filter as we go #if NET40 && !SILVERLIGHT if (Options.UsePLinqEvaluation && this._expr.CanParallelise) { PartitionedMultiset partitionedSet; SparqlResultBinder binder = context.Binder; if (lhsResults.Count >= rhsResults.Count) { partitionedSet = new PartitionedMultiset(lhsResults.Count, rhsResults.Count); context.Binder = new LeviathanLeftJoinBinder(partitionedSet); lhsResults.Sets.AsParallel().ForAll(x => this.EvalFilteredProduct(context, x, rhsResults, partitionedSet)); } else { partitionedSet = new PartitionedMultiset(rhsResults.Count, lhsResults.Count); context.Binder = new LeviathanLeftJoinBinder(partitionedSet); rhsResults.Sets.AsParallel().ForAll(y => this.EvalFilteredProduct(context, y, lhsResults, partitionedSet)); } context.Binder = binder; context.OutputMultiset = partitionedSet; } else { #endif BaseMultiset productSet = new Multiset(); SparqlResultBinder binder = context.Binder; context.Binder = new LeviathanLeftJoinBinder(productSet); foreach (ISet x in lhsResults.Sets) { foreach (ISet y in rhsResults.Sets) { ISet z = x.Join(y); productSet.Add(z); try { if (!this._expr.Evaluate(context, z.ID).AsSafeBoolean()) { //Means the expression evaluates to false so we discard the solution productSet.Remove(z.ID); } } catch { //Means this solution does not meet the FILTER and can be discarded productSet.Remove(z.ID); } } //Remember to check for timeouts occassionaly context.CheckTimeout(); } context.Binder = binder; context.OutputMultiset = productSet; #if NET40 && !SILVERLIGHT } #endif } } return(context.OutputMultiset); }