/// <summary> /// Calculates the product of two mutlisets asynchronously with a timeout to restrict long running computations /// </summary> /// <param name="multiset">Multiset</param> /// <param name="other">Other Multiset</param> /// <param name="timeout">Timeout, if <=0 no timeout is used and product will be computed sychronously</param> /// <returns></returns> public static BaseMultiset ProductWithTimeout(this BaseMultiset multiset, BaseMultiset other, long timeout) { if (other is IdentityMultiset) return multiset; if (other is NullMultiset) return other; if (other.IsEmpty) return new NullMultiset(); if (timeout <= 0) { return multiset.Product(other); } //Invoke using an Async call Multiset productSet = new Multiset(); StopToken stop = new StopToken(); GenerateProductDelegate d = new GenerateProductDelegate(GenerateProduct); IAsyncResult r = d.BeginInvoke(multiset, other, productSet, stop, null, null); //Wait int t = (int)Math.Min(timeout, Int32.MaxValue); r.AsyncWaitHandle.WaitOne(t); if (!r.IsCompleted) { stop.ShouldStop = true; r.AsyncWaitHandle.WaitOne(); } return productSet; }
public void SparqlAlgebraJoinMultiVariable1() { ISet x = new Set(); x.Add("a", this._factory.CreateUriNode(UriFactory.Create("http://x"))); x.Add("b", this._factory.CreateUriNode(UriFactory.Create("http://y"))); ISet y1 = new Set(); y1.Add("a", this._factory.CreateUriNode(UriFactory.Create("http://x"))); y1.Add("b", this._factory.CreateUriNode(UriFactory.Create("http://y"))); ISet y2 = new Set(); y2.Add("a", this._factory.CreateUriNode(UriFactory.Create("http://x"))); y2.Add("b", this._factory.CreateUriNode(UriFactory.Create("http://y"))); BaseMultiset lhs = new Multiset(); lhs.Add(x); BaseMultiset rhs = new Multiset(); rhs.Add(y1); rhs.Add(y2); BaseMultiset joined = lhs.Join(rhs); Assert.AreEqual(2, joined.Count); }
/// <summary> /// Does a Product of this Multiset and another Multiset /// </summary> /// <param name="other">Other Multiset</param> /// <returns></returns> public override BaseMultiset Product(BaseMultiset other) { if (other is IdentityMultiset) { return(this); } if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } Multiset productSet = new Multiset(); foreach (ISet x in this.Sets) { foreach (ISet y in other.Sets) { productSet.Add(x.Join(y)); } } return(productSet); }
public void SparqlAlgebraJoinMultiVariable4() { ISet x1 = new Set(); x1.Add("a", this._factory.CreateUriNode(UriFactory.Create("http://x1"))); x1.Add("b", this._factory.CreateUriNode(UriFactory.Create("http://y1"))); ISet x2 = new Set(); x2.Add("a", this._factory.CreateUriNode(UriFactory.Create("http://x2"))); x2.Add("b", this._factory.CreateUriNode(UriFactory.Create("http://y2"))); ISet y1 = new Set(); y1.Add("a", this._factory.CreateUriNode(UriFactory.Create("http://x1"))); y1.Add("b", this._factory.CreateUriNode(UriFactory.Create("http://y2"))); ISet y2 = new Set(); y2.Add("a", this._factory.CreateUriNode(UriFactory.Create("http://x2"))); y2.Add("b", this._factory.CreateUriNode(UriFactory.Create("http://y1"))); BaseMultiset lhs = new Multiset(); lhs.Add(x1); lhs.Add(x2); BaseMultiset rhs = new Multiset(); rhs.Add(y1); rhs.Add(y2); BaseMultiset joined = lhs.Join(rhs); Assert.Equal(0, joined.Count); }
/// <summary> /// Does a Union of this Multiset and another Multiset. /// </summary> /// <param name="other">Other Multiset.</param> /// <returns></returns> public override BaseMultiset Union(BaseMultiset other) { if (other is IdentityMultiset) { return(this); } if (other is NullMultiset) { return(this); } if (other.IsEmpty) { return(this); } Multiset m = new Multiset(); foreach (ISet s in this.Sets) { m.Add(s.Copy()); } foreach (ISet s in other.Sets) { m.Add(s.Copy()); } return(m); }
/// <summary> /// Does a Product of this Multiset and another Multiset. /// </summary> /// <param name="other">Other Multiset.</param> /// <returns></returns> public virtual BaseMultiset Product(BaseMultiset other) { if (other is IdentityMultiset) { return(this); } if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } #if NET40 if (Options.UsePLinqEvaluation) { // Determine partition sizes so we can do a parallel product // Want to parallelize over whichever side is larger PartitionedMultiset partitionedSet; if (this.Count >= other.Count) { partitionedSet = new PartitionedMultiset(this.Count, other.Count); this.Sets.AsParallel().ForAll(x => this.EvalProduct(x, other, partitionedSet)); } else { partitionedSet = new PartitionedMultiset(other.Count, this.Count); other.Sets.AsParallel().ForAll(y => this.EvalProduct(y, this, partitionedSet)); } return(partitionedSet); } else { // Use serial calculation which is likely to really suck for big products Multiset productSet = new Multiset(); foreach (ISet x in this.Sets) { foreach (ISet y in other.Sets) { productSet.Add(x.Join(y)); } } return(productSet); } #else // Use serial calculation which is likely to really suck for big products var productSet = new Multiset(); foreach (var x in Sets) { foreach (var y in other.Sets) { productSet.Add(x.Join(y)); } } return(productSet); #endif }
/// <summary> /// Calculates the product of two mutlisets asynchronously with a timeout to restrict long running computations /// </summary> /// <param name="multiset">Multiset</param> /// <param name="other">Other Multiset</param> /// <param name="timeout">Timeout, if <=0 no timeout is used and product will be computed sychronously</param> /// <returns></returns> public static BaseMultiset ProductWithTimeout(this BaseMultiset multiset, BaseMultiset other, long timeout) { if (other is IdentityMultiset) { return(multiset); } if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } //If no timeout use default implementation if (timeout <= 0) { return(multiset.Product(other)); } //Otherwise Invoke using an Async call BaseMultiset productSet; #if NET40 && !SILVERLIGHT if (Options.UsePLinqEvaluation) { if (multiset.Count >= other.Count) { productSet = new PartitionedMultiset(multiset.Count, other.Count); } else { productSet = new PartitionedMultiset(other.Count, multiset.Count); } } else { #endif productSet = new Multiset(); #if NET40 && !SILVERLIGHT } #endif StopToken stop = new StopToken(); GenerateProductDelegate d = new GenerateProductDelegate(GenerateProduct); IAsyncResult r = d.BeginInvoke(multiset, other, productSet, stop, null, null); //Wait int t = (int)Math.Min(timeout, Int32.MaxValue); r.AsyncWaitHandle.WaitOne(t); if (!r.IsCompleted) { stop.ShouldStop = true; r.AsyncWaitHandle.WaitOne(); } return(productSet); }
/// <summary> /// Calculates the product of two multi-sets asynchronously with a timeout to restrict long running computations. /// </summary> /// <param name="multiset">Multiset.</param> /// <param name="other">Other Multiset.</param> /// <param name="timeout">Timeout, if <=0 no timeout is used and product will be computed synchronously.</param> /// <returns></returns> public static BaseMultiset ProductWithTimeout(this BaseMultiset multiset, BaseMultiset other, long timeout) { if (other is IdentityMultiset) { return(multiset); } if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } // If no timeout use default implementation if (timeout <= 0) { return(multiset.Product(other)); } // Otherwise Invoke using an Async call BaseMultiset productSet; if (Options.UsePLinqEvaluation) { if (multiset.Count >= other.Count) { productSet = new PartitionedMultiset(multiset.Count, other.Count); } else { productSet = new PartitionedMultiset(other.Count, multiset.Count); } } else { productSet = new Multiset(); } var stop = new StopToken(); var t = (int)Math.Min(timeout, int.MaxValue); var productTask = Task.Factory.StartNew(() => GenerateProduct(multiset, other, productSet, stop)); if (!productTask.Wait(t)) { stop.ShouldStop = true; productTask.Wait(); } return(productSet); }
/// <summary> /// Joins this Multiset to another Multiset /// </summary> /// <param name="other">Other Multiset</param> /// <returns></returns> public override BaseMultiset Join(BaseMultiset other) { //If the Other is the Identity Multiset the result is this Multiset if (other is IdentityMultiset) { return(this); } //If the Other is the Null Multiset the result is the Null Multiset if (other is NullMultiset) { return(other); } //If the Other is Empty then the result is the Null Multiset if (other.IsEmpty) { return(new NullMultiset()); } //Find the First Variable from this Multiset which is in both Multisets //If there is no Variable from this Multiset in the other Multiset then this //should be a Join operation instead of a LeftJoin List <String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { return(this.Product(other)); } //Start building the Joined Set Multiset joinedSet = new Multiset(); foreach (ISet x in this.Sets) { //For sets to be compatible for every joinable variable they must either have a null for the //variable in one of the sets or if they have values the values must be equal ////This first check is to try speed things up, it looks whether there are solutions that may match ////without needing to do a full table scan of the RHS results as the subsequent LINQ call will do ////if (!joinVars.All(v => x[v] == null || other.ContainsValue(v, x[v]) || other.ContainsValue(v, null))) continue; IEnumerable <ISet> ys = other.Sets.Where(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); //IEnumerable<ISet> ys = other.Sets.Where(s => s.IsCompatibleWith(x, joinVars)); foreach (ISet y in ys) { joinedSet.Add(x.Join(y)); } } return(joinedSet); }
public void SparqlAlgebraJoinSingleVariable1() { ISet x = new Set(); x.Add("a", this._factory.CreateUriNode(UriFactory.Create("http://x"))); BaseMultiset lhs = new Multiset(); lhs.Add(x); BaseMultiset rhs = new Multiset(); rhs.Add(x); BaseMultiset joined = lhs.Join(rhs); Assert.AreEqual(1, joined.Count); }
/// <summary> /// Does a Minus Join of this Multiset to another Multiset where any joinable results are subtracted from this Multiset to give the resulting Multiset /// </summary> /// <param name="other">Other Multiset</param> /// <returns></returns> public override BaseMultiset MinusJoin(BaseMultiset other) { //If the other Multiset is the Identity/Null Multiset then minus-ing it doesn't alter this set if (other is IdentityMultiset) { return(this); } if (other is NullMultiset) { return(this); } //If the other Multiset is disjoint then minus-ing it also doesn't alter this set if (this.IsDisjointWith(other)) { return(this); } //Find the Variables that are to be used for Joining List <String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { return(this.Product(other)); } //Start building the Joined Set Multiset joinedSet = new Multiset(); foreach (ISet x in this.Sets) { //New Minus logic based on the improved Join() logic bool minus = other.Sets.Any(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); //If no compatible sets then this set is preserved if (!minus) { joinedSet.Add(x); } } return(joinedSet); }
/// <summary> /// Calculates the product of two mutlisets asynchronously with a timeout to restrict long running computations /// </summary> /// <param name="multiset">Multiset</param> /// <param name="other">Other Multiset</param> /// <param name="timeout">Timeout, if <=0 no timeout is used and product will be computed sychronously</param> /// <returns></returns> public static BaseMultiset ProductWithTimeout(this BaseMultiset multiset, BaseMultiset other, long timeout) { if (other is IdentityMultiset) { return(multiset); } if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } if (timeout <= 0) { return(multiset.Product(other)); } //Invoke using an Async call Multiset productSet = new Multiset(); StopToken stop = new StopToken(); GenerateProductDelegate d = new GenerateProductDelegate(GenerateProduct); IAsyncResult r = d.BeginInvoke(multiset, other, productSet, stop, null, null); //Wait int t = (int)Math.Min(timeout, Int32.MaxValue); r.AsyncWaitHandle.WaitOne(t); if (!r.IsCompleted) { stop.ShouldStop = true; r.AsyncWaitHandle.WaitOne(); } return(productSet); }
/// <summary> /// Creates a new Multiset Handler /// </summary> /// <param name="mset">Multiset</param> public MultisetHandler(Multiset mset) { if (mset == null) throw new ArgumentNullException("mset", "Multiset to load into cannot be null"); this._mset = mset; }
/// <summary> /// Does a Left Join of this Multiset to another Multiset where the Join is predicated on the given Expression /// </summary> /// <param name="other">Other Multiset</param> /// <param name="expr">Expression</param> /// <returns></returns> public override BaseMultiset LeftJoin(BaseMultiset other, ISparqlExpression expr) { //If the Other is the Identity/Null Multiset the result is this Multiset if (other is IdentityMultiset) return this; if (other is NullMultiset) return this; if (other.IsEmpty) return this; Multiset joinedSet = new Multiset(); LeviathanLeftJoinBinder binder = new LeviathanLeftJoinBinder(joinedSet); SparqlEvaluationContext subcontext = new SparqlEvaluationContext(binder); //Find the First Variable from this Multiset which is in both Multisets //If there is no Variable from this Multiset in the other Multiset then this //should be a Join operation instead of a LeftJoin List<String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { //Calculate a Product filtering as we go foreach (ISet x in this.Sets) { bool standalone = false; foreach (ISet y in other.Sets) { ISet z = x.Join(y); try { joinedSet.Add(z); if (!expr.Evaluate(subcontext, z.ID).AsSafeBoolean()) { joinedSet.Remove(z.ID); standalone = true; } } catch { joinedSet.Remove(z.ID); standalone = true; } } if (standalone) joinedSet.Add(x.Copy()); } } else { //This is the old algorithm which is correct but has complexity O(n^2) so it scales terribly //foreach (ISet x in this.Sets) //{ // IEnumerable<ISet> ys = other.Sets.Where(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); // //IEnumerable<ISet> ys = other.Sets.Where(s => s.IsCompatibleWith(x, joinVars)); // bool standalone = false; // int i = 0; // foreach (ISet y in ys) // { // i++; // ISet z = x.Join(y); // try // { // joinedSet.Add(z); // if (!expr.Evaluate(subcontext, z.ID).AsSafeBoolean()) // { // joinedSet.Remove(z.ID); // standalone = true; // } // } // catch // { // joinedSet.Remove(z.ID); // standalone = true; // } // } // if (standalone || i == 0) joinedSet.Add(x); //} //This is the new Join algorithm which is also correct but is O(2n) so much faster and scalable //Downside is that it does require more memory than the old algorithm List<HashTable<INode, int>> values = new List<HashTable<INode, int>>(); List<List<int>> nulls = new List<List<int>>(); foreach (String var in joinVars) { values.Add(new HashTable<INode, int>(HashTableBias.Enumeration)); nulls.Add(new List<int>()); } //First do a pass over the LHS Result to find all possible values for joined variables HashSet<int> matched = new HashSet<int>(); HashSet<int> standalone = new HashSet<int>(); foreach (ISet x in this.Sets) { int i = 0; foreach (String var in joinVars) { INode value = x[var]; if (value != null) { values[i].Add(value, x.ID); } else { nulls[i].Add(x.ID); } i++; } } //Then do a pass over the RHS and work out the intersections foreach (ISet y in other.Sets) { IEnumerable<int> possMatches = null; int i = 0; foreach (String var in joinVars) { INode value = y[var]; if (value != null) { if (values[i].ContainsKey(value)) { possMatches = (possMatches == null ? values[i].GetValues(value).Concat(nulls[i]) : possMatches.Intersect(values[i].GetValues(value).Concat(nulls[i]))); } else { possMatches = Enumerable.Empty<int>(); break; } } else { //Don't forget that a null will be potentially compatible with everything possMatches = (possMatches == null ? this.SetIDs : possMatches.Intersect(this.SetIDs)); } i++; } if (possMatches == null) continue; //Now do the actual joins for the current set //Note - We access the dictionary directly here because going through the this[int id] method //incurs a Contains() call each time and we know the IDs must exist because they came from //our dictionary originally! foreach (int poss in possMatches) { if (this._sets[poss].IsCompatibleWith(y, joinVars)) { ISet z = this._sets[poss].Join(y); joinedSet.Add(z); try { if (!expr.Evaluate(subcontext, z.ID).AsSafeBoolean()) { joinedSet.Remove(z.ID); standalone.Add(poss); } else { matched.Add(poss); } } catch { joinedSet.Remove(z.ID); standalone.Add(poss); } } } } //Finally add in unmatched sets from LHS foreach (int id in this.SetIDs) { if (!matched.Contains(id) || standalone.Contains(id)) joinedSet.Add(this._sets[id].Copy()); } } return joinedSet; }
/// <summary> /// Does a Product of this Multiset and another Multiset /// </summary> /// <param name="other">Other Multiset</param> /// <returns></returns> public override BaseMultiset Product(BaseMultiset other) { if (other is IdentityMultiset) return this; if (other is NullMultiset) return other; if (other.IsEmpty) return new NullMultiset(); Multiset productSet = new Multiset(); foreach (ISet x in this.Sets) { foreach (ISet y in other.Sets) { productSet.Add(x.Join(y)); } } return productSet; }
/// <summary> /// Evaluates the Graph Clause by setting up the dataset, applying the pattern and then generating additional bindings if necessary /// </summary> /// <param name="context">Evaluation Context</param> /// <returns></returns> public BaseMultiset Evaluate(SparqlEvaluationContext context) { BaseMultiset result; //Q: Can we optimise GRAPH when the input is the Null Multiset to just return the Null Multiset? if (this._pattern is Bgp && ((Bgp)this._pattern).IsEmpty) { //Optimise the case where we have GRAPH ?g {} by not setting the Graph and just returning //a Null Multiset result = new NullMultiset(); } else { bool datasetOk = false; try { List<String> activeGraphs = new List<string>(); //Get the URIs of Graphs that should be evaluated over if (this._graphSpecifier.TokenType != Token.VARIABLE) { switch (this._graphSpecifier.TokenType) { case Token.URI: case Token.QNAME: Uri activeGraphUri = UriFactory.Create(Tools.ResolveUriOrQName(this._graphSpecifier, context.Query.NamespaceMap, context.Query.BaseUri)); if (context.Data.HasGraph(activeGraphUri)) { //If the Graph is explicitly specified and there are FROM NAMED present then the Graph //URI must be in the graphs specified by a FROM NAMED or the result is null if (context.Query == null || !context.Query.NamedGraphs.Any() || context.Query.NamedGraphs.Any(u => EqualityHelper.AreUrisEqual(activeGraphUri, u))) { //Either there was no Query //OR there were no Named Graphs (hence any Graph URI is permitted) //OR the specified URI was a Named Graph URI //In any case we can go ahead and set the active Graph activeGraphs.Add(activeGraphUri.ToString()); } else { //The specified URI was not present in the Default/Named Graphs so return null context.OutputMultiset = new NullMultiset(); return context.OutputMultiset; } } else { //If specifies a specific Graph and not in the Dataset result is a null multiset context.OutputMultiset = new NullMultiset(); return context.OutputMultiset; } break; default: throw new RdfQueryException("Cannot use a '" + this._graphSpecifier.GetType().ToString() + "' Token to specify the Graph for a GRAPH clause"); } } else { String gvar = this._graphSpecifier.Value.Substring(1); //Watch out for the case in which the Graph Variable is not bound for all Sets in which case //we still need to operate over all Graphs if (context.InputMultiset.ContainsVariable(gvar) && context.InputMultiset.Sets.All(s => s[gvar] != null)) { //If there are already values bound to the Graph variable for all Input Solutions then we limit the Query to those Graphs List<Uri> graphUris = new List<Uri>(); foreach (ISet s in context.InputMultiset.Sets) { INode temp = s[gvar]; if (temp != null) { if (temp.NodeType == NodeType.Uri) { activeGraphs.Add(temp.ToString()); graphUris.Add(((IUriNode)temp).Uri); } } } } else { //Nothing yet bound to the Graph Variable so the Query is over all the named Graphs if (context.Query != null && context.Query.NamedGraphs.Any()) { //Query specifies one/more named Graphs activeGraphs.AddRange(context.Query.NamedGraphs.Select(u => u.ToString())); } else { //Query is over entire dataset/default Graph since no named Graphs are explicitly specified activeGraphs.AddRange(context.Data.GraphUris.Select(u => u.ToSafeString())); } } } //Remove all duplicates from Active Graphs to avoid duplicate results activeGraphs = activeGraphs.Distinct().ToList(); //Evaluate the inner pattern BaseMultiset initialInput = context.InputMultiset; BaseMultiset finalResult = new Multiset(); //Evalute for each Graph URI and union the results foreach (String uri in activeGraphs) { //Always use the same Input for each Graph URI and set that Graph to be the Active Graph //Be sure to translate String.Empty back to the null URI to select the default graph //correctly context.InputMultiset = initialInput; Uri currGraphUri = (uri.Equals(String.Empty)) ? null : UriFactory.Create(uri); //Set Active Graph if (currGraphUri == null) { //GRAPH operates over named graphs only so default graph gets skipped continue; } else { //The result of the HasGraph() call is ignored we just make it so datasets with any kind of //load on demand behaviour work properly context.Data.HasGraph(currGraphUri); //All we actually care about is setting the active graph context.Data.SetActiveGraph(currGraphUri); } datasetOk = true; //Evaluate for the current Active Graph result = context.Evaluate(this._pattern); //Merge the Results into our overall Results if (result is NullMultiset || result is IdentityMultiset) { //Don't do anything } else { //If the Graph Specifier is a Variable then we must either bind the //variable or eliminate solutions which have an incorrect value for it if (this._graphSpecifier.TokenType == Token.VARIABLE) { String gvar = this._graphSpecifier.Value.Substring(1); INode currGraph = (currGraphUri == null) ? null : new UriNode(null, currGraphUri); foreach (int id in result.SetIDs.ToList()) { ISet s = result[id]; if (s[gvar] == null) { //If Graph Variable is not yet bound for solution bind it s.Add(gvar, currGraph); } else if (!s[gvar].Equals(currGraph)) { //If Graph Variable is bound for solution and doesn't match //current Graph then we have to remove the solution result.Remove(id); } } } //Union solutions into the Results finalResult.Union(result); } //Reset the Active Graph after each pass context.Data.ResetActiveGraph(); datasetOk = false; } //Return the final result if (finalResult.IsEmpty) finalResult = new NullMultiset(); context.OutputMultiset = finalResult; } finally { if (datasetOk) context.Data.ResetActiveGraph(); } } return context.OutputMultiset; }
/// <summary> /// Does an Exists Join of this Multiset to another Multiset where the Join is predicated on the existence/non-existence of a joinable solution on the RHS /// </summary> /// <param name="other">Other Multiset</param> /// <param name="mustExist">Whether a solution must exist in the Other Multiset for the join to be made</param> /// <returns></returns> public override BaseMultiset ExistsJoin(BaseMultiset other, bool mustExist) { //For EXISTS and NOT EXISTS if the other is the Identity then it has no effect if (other is IdentityMultiset) return this; if (mustExist) { //If an EXISTS then Null/Empty Other results in Null if (other is NullMultiset) return other; if (other.IsEmpty) return new NullMultiset(); } else { //If a NOT EXISTS then Null/Empty results in this if (other is NullMultiset) return this; if (other.IsEmpty) return this; } //Find the Variables that are to be used for Joining List<String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { //All Disjoint Solutions are compatible if (mustExist) { //If an EXISTS and disjoint then result is this return this; } else { //If a NOT EXISTS and disjoint then result is null return new NullMultiset(); } } //Start building the Joined Set Multiset joinedSet = new Multiset(); //This is the old algorithm which is correct but naive with worse case O(n^2) //foreach (ISet x in this.Sets) //{ // //New ExistsJoin() logic based on the improved Join() logic // bool exists = other.Sets.Any(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); // //bool exists = other.Sets.Any(s => s.IsCompatibleWith(x, joinVars)); // if (exists) // { // //If there are compatible sets and this is an EXIST then preserve the solution // if (mustExist) joinedSet.Add(x); // } // else // { // //If there are no compatible sets and this is a NOT EXISTS then preserve the solution // if (!mustExist) joinedSet.Add(x); // } //} //This is the new algorithm which is also correct but is O(3n) so much faster and scalable //Downside is that it does require more memory than the old algorithm List<HashTable<INode, int>> values = new List<HashTable<INode, int>>(); List<List<int>> nulls = new List<List<int>>(); foreach (String var in joinVars) { values.Add(new HashTable<INode, int>(HashTableBias.Enumeration)); nulls.Add(new List<int>()); } //First do a pass over the LHS Result to find all possible values for joined variables foreach (ISet x in this.Sets) { int i = 0; foreach (String var in joinVars) { INode value = x[var]; if (value != null) { values[i].Add(value, x.ID); } else { nulls[i].Add(x.ID); } i++; } } //Then do a pass over the RHS and work out the intersections HashSet<int> exists = new HashSet<int>(); foreach (ISet y in other.Sets) { IEnumerable<int> possMatches = null; int i = 0; foreach (String var in joinVars) { INode value = y[var]; if (value != null) { if (values[i].ContainsKey(value)) { possMatches = (possMatches == null ? values[i].GetValues(value).Concat(nulls[i]) : possMatches.Intersect(values[i].GetValues(value).Concat(nulls[i]))); } else { possMatches = Enumerable.Empty<int>(); break; } } else { //Don't forget that a null will be potentially compatible with everything possMatches = (possMatches == null ? this.SetIDs : possMatches.Intersect(this.SetIDs)); } i++; } if (possMatches == null) continue; //Look at possible matches, if is a valid match then mark the set as having an existing match //Don't reconsider sets which have already been marked as having an existing match foreach (int poss in possMatches) { if (exists.Contains(poss)) continue; if (this._sets[poss].IsCompatibleWith(y, joinVars)) { exists.Add(poss); } } } //Apply the actual exists if (exists.Count == this.Count) { //If number of sets that have a match is equal to number of sets then we're either returning everything or nothing if (mustExist) { return this; } else { return new NullMultiset(); } } else { //Otherwise iterate foreach (ISet x in this.Sets) { if (mustExist) { if (exists.Contains(x.ID)) { joinedSet.Add(x.Copy()); } } else { if (!exists.Contains(x.ID)) { joinedSet.Add(x.Copy()); } } } } return joinedSet; }
/// <summary> /// Evaluates the Graph Clause by setting up the dataset, applying the pattern and then generating additional bindings if necessary /// </summary> /// <param name="context">Evaluation Context</param> /// <returns></returns> public BaseMultiset Evaluate(SparqlEvaluationContext context) { BaseMultiset result; //Q: Can we optimise GRAPH when the input is the Null Multiset to just return the Null Multiset? if (this._pattern is Bgp && ((Bgp)this._pattern).IsEmpty) { //Optimise the case where we have GRAPH ?g {} by not setting the Graph and just returning //a Null Multiset result = new NullMultiset(); } else { bool datasetOk = false; try { List <String> activeGraphs = new List <string>(); //Get the URIs of Graphs that should be evaluated over if (this._graphSpecifier.TokenType != Token.VARIABLE) { switch (this._graphSpecifier.TokenType) { case Token.URI: case Token.QNAME: Uri activeGraphUri = new Uri(Tools.ResolveUriOrQName(this._graphSpecifier, context.Query.NamespaceMap, context.Query.BaseUri)); if (context.Data.HasGraph(activeGraphUri)) { //If the Graph is explicitly specified and there are FROM NAMED present then the Graph //URI must be in the graphs specified by a FROM NAMED or the result is null if (context.Query != null && ((!context.Query.DefaultGraphs.Any() && !context.Query.NamedGraphs.Any()) || context.Query.DefaultGraphs.Any(u => EqualityHelper.AreUrisEqual(activeGraphUri, u)) || context.Query.NamedGraphs.Any(u => EqualityHelper.AreUrisEqual(activeGraphUri, u))) ) { //Either there was no Query OR there were no Default/Named Graphs OR //the specified URI was either a Default/Named Graph URI //In any case we can go ahead and set the active Graph activeGraphs.Add(activeGraphUri.ToString()); } else { //The specified URI was not present in the Default/Named Graphs so return null context.OutputMultiset = new NullMultiset(); return(context.OutputMultiset); } } else { //If specifies a specific Graph and not in the Dataset result is a null multiset context.OutputMultiset = new NullMultiset(); return(context.OutputMultiset); } break; default: throw new RdfQueryException("Cannot use a '" + this._graphSpecifier.GetType().ToString() + "' Token to specify the Graph for a GRAPH clause"); } } else { String gvar = this._graphSpecifier.Value.Substring(1); //Watch out for the case in which the Graph Variable is not bound for all Sets in which case //we still need to operate over all Graphs if (context.InputMultiset.ContainsVariable(gvar) && context.InputMultiset.Sets.All(s => s[gvar] != null)) { //If there are already values bound to the Graph variable for all Input Solutions then we limit the Query to those Graphs List <Uri> graphUris = new List <Uri>(); foreach (ISet s in context.InputMultiset.Sets) { INode temp = s[gvar]; if (temp != null) { if (temp.NodeType == NodeType.Uri) { activeGraphs.Add(temp.ToString()); graphUris.Add(((IUriNode)temp).Uri); } } } } else { //Nothing yet bound to the Graph Variable so the Query is over all the named Graphs if (context.Query != null && context.Query.NamedGraphs.Any()) { //Query specifies one/more named Graphs activeGraphs.AddRange(context.Query.NamedGraphs.Select(u => u.ToString())); } else { //Query is over entire dataset/default Graph since no named Graphs are explicitly specified activeGraphs.AddRange(context.Data.GraphUris.Select(u => u.ToSafeString())); } } } //Remove all duplicates from Active Graphs to avoid duplicate results activeGraphs = activeGraphs.Distinct().ToList(); //Evaluate the inner pattern BaseMultiset initialInput = context.InputMultiset; BaseMultiset finalResult = new Multiset(); //Evalute for each Graph URI and union the results foreach (String uri in activeGraphs) { //Always use the same Input for each Graph URI and set that Graph to be the Active Graph //Be sure to translate String.Empty back to the null URI to select the default graph //correctly context.InputMultiset = initialInput; Uri currGraphUri = (uri.Equals(String.Empty)) ? null : new Uri(uri); //This bit of logic takes care of the fact that calling SetActiveGraph((Uri)null) resets the //Active Graph to be the default graph which if the default graph is null is usually the Union of //all Graphs in the Store if (currGraphUri == null && context.Data.DefaultGraph == null && context.Data.UsesUnionDefaultGraph) { if (context.Data.HasGraph(null)) { context.Data.SetActiveGraph(context.Data[null]); } else { continue; } } else { context.Data.SetActiveGraph(currGraphUri); } datasetOk = true; //Evaluate for the current Active Graph result = context.Evaluate(this._pattern); //Merge the Results into our overall Results if (result is NullMultiset || result is IdentityMultiset) { //Don't do anything } else { //If the Graph Specifier is a Variable then we must either bind the //variable or eliminate solutions which have an incorrect value for it if (this._graphSpecifier.TokenType == Token.VARIABLE) { String gvar = this._graphSpecifier.Value.Substring(1); INode currGraph = (currGraphUri == null) ? null : new UriNode(null, currGraphUri); foreach (int id in result.SetIDs.ToList()) { ISet s = result[id]; if (s[gvar] == null) { //If Graph Variable is not yet bound for solution bind it s.Add(gvar, currGraph); } else if (!s[gvar].Equals(currGraph)) { //If Graph Variable is bound for solution and doesn't match //current Graph then we have to remove the solution result.Remove(id); } } } //Union solutions into the Results finalResult.Union(result); } //Reset the Active Graph after each pass context.Data.ResetActiveGraph(); datasetOk = false; } //Return the final result if (finalResult.IsEmpty) { finalResult = new NullMultiset(); } context.OutputMultiset = finalResult; } finally { if (datasetOk) { context.Data.ResetActiveGraph(); } } } return(context.OutputMultiset); }
public virtual BaseMultiset GetMultiset(IStore store) { var ms = new Multiset(Columns); foreach (var row in Rows) { var set = new Set(); for (int i = 0; i < Columns.Count; i++) { if (row[i] > 0) { set.Add(Columns[i], MakeNode(store, row[i])); } } ms.Add(set); } return ms; }
/// <summary> /// Does a Minus Join of this Multiset to another Multiset where any joinable results are subtracted from this Multiset to give the resulting Multiset /// </summary> /// <param name="other">Other Multiset</param> /// <returns></returns> public override BaseMultiset MinusJoin(BaseMultiset other) { //If the other Multiset is the Identity/Null Multiset then minus-ing it doesn't alter this set if (other is IdentityMultiset) return this; if (other is NullMultiset) return this; //If the other Multiset is disjoint then minus-ing it also doesn't alter this set if (this.IsDisjointWith(other)) return this; //Find the Variables that are to be used for Joining List<String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) return this.Product(other); //Start building the Joined Set Multiset joinedSet = new Multiset(); foreach (ISet x in this.Sets) { //New Minus logic based on the improved Join() logic bool minus = other.Sets.Any(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); //If no compatible sets then this set is preserved if (!minus) { joinedSet.Add(x); } } return joinedSet; }
/// <summary> /// Does a Left Join of this Multiset to another Multiset where the Join is predicated on the given Expression. /// </summary> /// <param name="other">Other Multiset.</param> /// <param name="expr">Expression.</param> /// <returns></returns> public virtual BaseMultiset LeftJoin(BaseMultiset other, ISparqlExpression expr) { // If the Other is the Identity/Null Multiset the result is this Multiset if (other is IdentityMultiset) { return(this); } if (other is NullMultiset) { return(this); } if (other.IsEmpty) { return(this); } Multiset joinedSet = new Multiset(); LeviathanLeftJoinBinder binder = new LeviathanLeftJoinBinder(joinedSet); SparqlEvaluationContext subcontext = new SparqlEvaluationContext(binder); // Find the First Variable from this Multiset which is in both Multisets // If there is no Variable from this Multiset in the other Multiset then this // should be a Join operation instead of a LeftJoin List <String> joinVars = Variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { #if NET40 if (Options.UsePLinqEvaluation && expr.CanParallelise) { PartitionedMultiset partitionedSet = new PartitionedMultiset(this.Count, other.Count + 1); this.Sets.AsParallel().ForAll(x => EvalLeftJoinProduct(x, other, partitionedSet, expr)); return(partitionedSet); } #endif // Do a serial Left Join Product // Calculate a Product filtering as we go foreach (ISet x in Sets) { bool standalone = false; bool matched = false; foreach (ISet y in other.Sets) { ISet z = x.Join(y); try { joinedSet.Add(z); if (!expr.Evaluate(subcontext, z.ID).AsSafeBoolean()) { joinedSet.Remove(z.ID); standalone = true; } else { matched = true; } } catch { joinedSet.Remove(z.ID); standalone = true; } } if (standalone && !matched) { joinedSet.Add(x.Copy()); } } #if NET40 #endif } else { // This is the new Join algorithm which is also correct but is O(2n) so much faster and scalable // Downside is that it does require more memory than the old algorithm List <MultiDictionary <INode, List <int> > > values = new List <MultiDictionary <INode, List <int> > >(); List <List <int> > nulls = new List <List <int> >(); foreach (String var in joinVars) { joinedSet.AddVariable(var); values.Add(new MultiDictionary <INode, List <int> >(new FastVirtualNodeComparer())); nulls.Add(new List <int>()); } // First do a pass over the RHS Result to find all possible values for joined variables foreach (ISet y in other.Sets) { int i = 0; foreach (String var in joinVars) { INode value = y[var]; if (value != null) { if (values[i].TryGetValue(value, out List <int> ids)) { ids.Add(y.ID); } else { values[i].Add(value, new List <int> { y.ID }); } } else { nulls[i].Add(y.ID); } i++; } } // Then do a pass over the LHS and work out the intersections #if NET40 if (Options.UsePLinqEvaluation && expr.CanParallelise) { this.Sets.AsParallel().ForAll(x => EvalLeftJoin(x, other, joinVars, values, nulls, joinedSet, subcontext, expr)); } else { // Use a Serial Left Join foreach (ISet x in this.Sets) { this.EvalLeftJoin(x, other, joinVars, values, nulls, joinedSet, subcontext, expr); } } #else // Use a Serial Left Join foreach (var x in Sets) { EvalLeftJoin(x, other, joinVars, values, nulls, joinedSet, subcontext, expr); } #endif } return(joinedSet); }
/// <summary> /// Does a Left Join of this Multiset to another Multiset where the Join is predicated on the given Expression /// </summary> /// <param name="other">Other Multiset</param> /// <param name="expr">Expression</param> /// <returns></returns> public override BaseMultiset LeftJoin(BaseMultiset other, ISparqlExpression expr) { //If the Other is the Identity/Null Multiset the result is this Multiset if (other is IdentityMultiset) return this; if (other is NullMultiset) return this; if (other.IsEmpty) return this; Multiset joinedSet = new Multiset(); LeviathanLeftJoinBinder binder = new LeviathanLeftJoinBinder(joinedSet); SparqlEvaluationContext subcontext = new SparqlEvaluationContext(binder); //Find the First Variable from this Multiset which is in both Multisets //If there is no Variable from this Multiset in the other Multiset then this //should be a Join operation instead of a LeftJoin List<String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { //Calculate a Product filtering as we go foreach (ISet x in this.Sets) { bool standalone = false; foreach (ISet y in other.Sets) { ISet z = x.Join(y); try { joinedSet.Add(z); if (!expr.EffectiveBooleanValue(subcontext, z.ID)) { joinedSet.Remove(z.ID); standalone = true; } } catch { joinedSet.Remove(z.ID); standalone = true; } } if (standalone) joinedSet.Add(x); } } else { foreach (ISet x in this.Sets) { IEnumerable<ISet> ys = other.Sets.Where(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); //IEnumerable<ISet> ys = other.Sets.Where(s => s.IsCompatibleWith(x, joinVars)); bool standalone = false; int i = 0; foreach (ISet y in ys) { i++; ISet z = x.Join(y); try { joinedSet.Add(z); if (!expr.EffectiveBooleanValue(subcontext, z.ID)) { joinedSet.Remove(z.ID); standalone = true; } } catch { joinedSet.Remove(z.ID); standalone = true; } } if (standalone || i == 0) joinedSet.Add(x); } } return joinedSet; }
/// <summary> /// Does an Exists Join of this Multiset to another Multiset where the Join is predicated on the existence/non-existence of a joinable solution on the RHS /// </summary> /// <param name="other">Other Multiset</param> /// <param name="mustExist">Whether a solution must exist in the Other Multiset for the join to be made</param> /// <returns></returns> public override BaseMultiset ExistsJoin(BaseMultiset other, bool mustExist) { //For EXISTS and NOT EXISTS if the other is the Identity then it has no effect if (other is IdentityMultiset) return this; if (mustExist) { //If an EXISTS then Null/Empty Other results in Null if (other is NullMultiset) return other; if (other.IsEmpty) return new NullMultiset(); } else { //If a NOT EXISTS then Null/Empty results in this if (other is NullMultiset) return this; if (other.IsEmpty) return this; } //Find the Variables that are to be used for Joining List<String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { //All Disjoint Solutions are compatible if (mustExist) { //If an EXISTS and disjoint then result is this return this; } else { //If a NOT EXISTS and disjoint then result is null return new NullMultiset(); } } //Start building the Joined Set Multiset joinedSet = new Multiset(); foreach (ISet x in this.Sets) { //New ExistsJoin() logic based on the improved Join() logic bool exists = other.Sets.Any(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); //bool exists = other.Sets.Any(s => s.IsCompatibleWith(x, joinVars)); if (exists) { //If there are compatible sets and this is an EXIST then preserve the solution if (mustExist) joinedSet.Add(x); } else { //If there are no compatible sets and this is a NOT EXISTS then preserve the solution if (!mustExist) joinedSet.Add(x); } } return joinedSet; }
/// <summary> /// Joins this Multiset to another Multiset /// </summary> /// <param name="other">Other Multiset</param> /// <returns></returns> public override BaseMultiset Join(BaseMultiset other) { //If the Other is the Identity Multiset the result is this Multiset if (other is IdentityMultiset) return this; //If the Other is the Null Multiset the result is the Null Multiset if (other is NullMultiset) return other; //If the Other is Empty then the result is the Null Multiset if (other.IsEmpty) return new NullMultiset(); //Find the First Variable from this Multiset which is in both Multisets //If there is no Variable from this Multiset in the other Multiset then this //should be a Join operation instead of a LeftJoin List<String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) return this.Product(other); //Start building the Joined Set Multiset joinedSet = new Multiset(); foreach (ISet x in this.Sets) { //For sets to be compatible for every joinable variable they must either have a null for the //variable in one of the sets or if they have values the values must be equal ////This first check is to try speed things up, it looks whether there are solutions that may match ////without needing to do a full table scan of the RHS results as the subsequent LINQ call will do ////if (!joinVars.All(v => x[v] == null || other.ContainsValue(v, x[v]) || other.ContainsValue(v, null))) continue; IEnumerable<ISet> ys = other.Sets.Where(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); //IEnumerable<ISet> ys = other.Sets.Where(s => s.IsCompatibleWith(x, joinVars)); foreach (ISet y in ys) { joinedSet.Add(x.Join(y)); } } return joinedSet; }
/// <summary> /// Does an Exists Join of this Multiset to another Multiset where the Join is predicated on the existence/non-existence of a joinable solution on the RHS /// </summary> /// <param name="other">Other Multiset</param> /// <param name="mustExist">Whether a solution must exist in the Other Multiset for the join to be made</param> /// <returns></returns> public override BaseMultiset ExistsJoin(BaseMultiset other, bool mustExist) { //For EXISTS and NOT EXISTS if the other is the Identity then it has no effect if (other is IdentityMultiset) { return(this); } if (mustExist) { //If an EXISTS then Null/Empty Other results in Null if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } } else { //If a NOT EXISTS then Null/Empty results in this if (other is NullMultiset) { return(this); } if (other.IsEmpty) { return(this); } } //Find the Variables that are to be used for Joining List <String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { //All Disjoint Solutions are compatible if (mustExist) { //If an EXISTS and disjoint then result is this return(this); } else { //If a NOT EXISTS and disjoint then result is null return(new NullMultiset()); } } //Start building the Joined Set Multiset joinedSet = new Multiset(); //This is the old algorithm which is correct but naive with worse case O(n^2) //foreach (ISet x in this.Sets) //{ // //New ExistsJoin() logic based on the improved Join() logic // bool exists = other.Sets.Any(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); // //bool exists = other.Sets.Any(s => s.IsCompatibleWith(x, joinVars)); // if (exists) // { // //If there are compatible sets and this is an EXIST then preserve the solution // if (mustExist) joinedSet.Add(x); // } // else // { // //If there are no compatible sets and this is a NOT EXISTS then preserve the solution // if (!mustExist) joinedSet.Add(x); // } //} //This is the new algorithm which is also correct but is O(3n) so much faster and scalable //Downside is that it does require more memory than the old algorithm List <HashTable <INode, int> > values = new List <HashTable <INode, int> >(); List <List <int> > nulls = new List <List <int> >(); foreach (String var in joinVars) { values.Add(new HashTable <INode, int>(HashTableBias.Enumeration)); nulls.Add(new List <int>()); } //First do a pass over the LHS Result to find all possible values for joined variables foreach (ISet x in this.Sets) { int i = 0; foreach (String var in joinVars) { INode value = x[var]; if (value != null) { values[i].Add(value, x.ID); } else { nulls[i].Add(x.ID); } i++; } } //Then do a pass over the RHS and work out the intersections HashSet <int> exists = new HashSet <int>(); foreach (ISet y in other.Sets) { IEnumerable <int> possMatches = null; int i = 0; foreach (String var in joinVars) { INode value = y[var]; if (value != null) { if (values[i].ContainsKey(value)) { possMatches = (possMatches == null ? values[i].GetValues(value).Concat(nulls[i]) : possMatches.Intersect(values[i].GetValues(value).Concat(nulls[i]))); } else { possMatches = Enumerable.Empty <int>(); break; } } else { //Don't forget that a null will be potentially compatible with everything possMatches = (possMatches == null ? this.SetIDs : possMatches.Intersect(this.SetIDs)); } i++; } if (possMatches == null) { continue; } //Look at possible matches, if is a valid match then mark the set as having an existing match //Don't reconsider sets which have already been marked as having an existing match foreach (int poss in possMatches) { if (exists.Contains(poss)) { continue; } if (this._sets[poss].IsCompatibleWith(y, joinVars)) { exists.Add(poss); } } } //Apply the actual exists if (exists.Count == this.Count) { //If number of sets that have a match is equal to number of sets then we're either returning everything or nothing if (mustExist) { return(this); } else { return(new NullMultiset()); } } else { //Otherwise iterate foreach (ISet x in this.Sets) { if (mustExist) { if (exists.Contains(x.ID)) { joinedSet.Add(x.Copy()); } } else { if (!exists.Contains(x.ID)) { joinedSet.Add(x.Copy()); } } } } return(joinedSet); }
/// <summary> /// Processes an INSERT/DELETE command /// </summary> /// <param name="cmd">Insert/Delete Command</param> public void ProcessModifyCommand(ModifyCommand cmd) { if (this._manager is IUpdateableGenericIOManager) { ((IUpdateableGenericIOManager)this._manager).Update(cmd.ToString()); } else { if (this._manager is IQueryableGenericIOManager) { //First build and make the query to get a Result Set String queryText = "SELECT * WHERE " + cmd.WherePattern.ToString(); SparqlQueryParser parser = new SparqlQueryParser(); SparqlQuery query = parser.ParseFromString(queryText); if (cmd.GraphUri != null && !cmd.UsingUris.Any()) query.AddDefaultGraph(cmd.GraphUri); foreach (Uri u in cmd.UsingUris) { query.AddDefaultGraph(u); } foreach (Uri u in cmd.UsingNamedUris) { query.AddNamedGraph(u); } Object results = ((IQueryableGenericIOManager)this._manager).Query(query.ToString()); if (results is SparqlResultSet) { //Now need to transform the Result Set back to a Multiset Multiset mset = new Multiset((SparqlResultSet)results); //Generate the Triples for each Solution List<Triple> deletedTriples = new List<Triple>(); Dictionary<String, List<Triple>> deletedGraphTriples = new Dictionary<string, List<Triple>>(); foreach (Set s in mset.Sets) { List<Triple> tempDeletedTriples = new List<Triple>(); try { ConstructContext context = new ConstructContext(null, s, true); foreach (IConstructTriplePattern p in cmd.DeletePattern.TriplePatterns.OfType<IConstructTriplePattern>()) { try { tempDeletedTriples.Add(p.Construct(context)); } catch (RdfQueryException) { //If we get an error here then it means we could not construct a specific //triple so we continue anyway } } deletedTriples.AddRange(tempDeletedTriples); } catch (RdfQueryException) { //If we get an error here this means we couldn't construct for this solution so the //solution is ignored for this graph } //Triples from GRAPH clauses foreach (GraphPattern gp in cmd.DeletePattern.ChildGraphPatterns) { tempDeletedTriples.Clear(); try { String graphUri; switch (gp.GraphSpecifier.TokenType) { case Token.URI: graphUri = gp.GraphSpecifier.Value; break; case Token.VARIABLE: if (s.ContainsVariable(gp.GraphSpecifier.Value)) { INode temp = s[gp.GraphSpecifier.Value.Substring(1)]; if (temp == null) { //If the Variable is not bound then skip continue; } else if (temp.NodeType == NodeType.Uri) { graphUri = temp.ToSafeString(); } else { //If the Variable is not bound to a URI then skip continue; } } else { //If the Variable is not bound for this solution then skip continue; } break; default: //Any other Graph Specifier we have to ignore this solution continue; } if (!deletedGraphTriples.ContainsKey(graphUri)) deletedGraphTriples.Add(graphUri, new List<Triple>()); ConstructContext context = new ConstructContext(null, s, true); foreach (IConstructTriplePattern p in gp.TriplePatterns.OfType<IConstructTriplePattern>()) { try { tempDeletedTriples.Add(p.Construct(context)); } catch (RdfQueryException) { //If we throw an error this means we couldn't construct a specific //triple so we continue anyway } } deletedGraphTriples[graphUri].AddRange(tempDeletedTriples); } catch (RdfQueryException) { //If we get an error here this means we couldn't construct for this solution so the //solution is ignored for this graph } } } //Generate the Triples for each Solution List<Triple> insertedTriples = new List<Triple>(); Dictionary<String, List<Triple>> insertedGraphTriples = new Dictionary<string, List<Triple>>(); foreach (Set s in mset.Sets) { List<Triple> tempInsertedTriples = new List<Triple>(); try { ConstructContext context = new ConstructContext(null, s, true); foreach (IConstructTriplePattern p in cmd.InsertPattern.TriplePatterns.OfType<IConstructTriplePattern>()) { try { tempInsertedTriples.Add(p.Construct(context)); } catch (RdfQueryException) { //If we get an error here then it means we couldn't construct a specific //triple so we continue anyway } } insertedTriples.AddRange(tempInsertedTriples); } catch (RdfQueryException) { //If we get an error here this means we couldn't construct for this solution so the //solution is ignored for this graph } //Triples from GRAPH clauses foreach (GraphPattern gp in cmd.InsertPattern.ChildGraphPatterns) { tempInsertedTriples.Clear(); try { String graphUri; switch (gp.GraphSpecifier.TokenType) { case Token.URI: graphUri = gp.GraphSpecifier.Value; break; case Token.VARIABLE: if (s.ContainsVariable(gp.GraphSpecifier.Value)) { INode temp = s[gp.GraphSpecifier.Value.Substring(1)]; if (temp == null) { //If the Variable is not bound then skip continue; } else if (temp.NodeType == NodeType.Uri) { graphUri = temp.ToSafeString(); } else { //If the Variable is not bound to a URI then skip continue; } } else { //If the Variable is not bound for this solution then skip continue; } break; default: //Any other Graph Specifier we have to ignore this solution continue; } if (!insertedGraphTriples.ContainsKey(graphUri)) insertedGraphTriples.Add(graphUri, new List<Triple>()); ConstructContext context = new ConstructContext(null, s, true); foreach (IConstructTriplePattern p in gp.TriplePatterns.OfType<IConstructTriplePattern>()) { try { tempInsertedTriples.Add(p.Construct(context)); } catch (RdfQueryException) { //If we get an error here it means we couldn't construct a specific //triple so we continue anyway } } insertedGraphTriples[graphUri].AddRange(tempInsertedTriples); } catch (RdfQueryException) { //If we get an error here this means we couldn't construct for this solution so the //solution is ignored for this graph } } } //Now decide how to apply the update if (this._manager.UpdateSupported) { this._manager.UpdateGraph(cmd.GraphUri, insertedTriples, deletedTriples); //We do these two operations sequentially even if in some cases they could be combined to ensure that the underlying //Manager doesn't do any optimisations which would have the result of our updates not being properly applied //e.g. ignoring Triples which are both asserted and retracted in one update foreach (KeyValuePair<String, List<Triple>> graphDeletion in deletedGraphTriples) { this._manager.UpdateGraph(graphDeletion.Key, Enumerable.Empty<Triple>(), graphDeletion.Value); } foreach (KeyValuePair<String, List<Triple>> graphInsertion in insertedGraphTriples) { this._manager.UpdateGraph(graphInsertion.Key, graphInsertion.Value, Enumerable.Empty<Triple>()); } } else { Graph g = new Graph(); this._manager.LoadGraph(g, cmd.GraphUri); g.Retract(deletedTriples); this._manager.SaveGraph(g); foreach (String graphUri in deletedGraphTriples.Keys.Concat(insertedGraphTriples.Keys).Distinct()) { g = new Graph(); this._manager.LoadGraph(g, graphUri); if (deletedGraphTriples.ContainsKey(graphUri)) g.Retract(deletedGraphTriples[graphUri]); if (insertedGraphTriples.ContainsKey(graphUri)) g.Assert(insertedGraphTriples[graphUri]); this._manager.SaveGraph(g); } } } else { throw new SparqlUpdateException("Cannot evaluate an INSERT/DELETE Command as the underlying Store failed to answer the query for the WHERE portion of the command as expected"); } } else { throw new NotSupportedException("INSERT/DELETE commands are not supported by this Update Processor as the manager for the underlying Store does not provide Query capabilities which are necessary to process this command"); } } }
/// <summary> /// Does a Minus Join of this Multiset to another Multiset where any joinable results are subtracted from this Multiset to give the resulting Multiset /// </summary> /// <param name="other">Other Multiset</param> /// <returns></returns> public override BaseMultiset MinusJoin(BaseMultiset other) { //If the other Multiset is the Identity/Null Multiset then minus-ing it doesn't alter this set if (other is IdentityMultiset) { return(this); } if (other is NullMultiset) { return(this); } //If the other Multiset is disjoint then minus-ing it also doesn't alter this set if (this.IsDisjointWith(other)) { return(this); } //Find the Variables that are to be used for Joining List <String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { return(this.Product(other)); } //Start building the Joined Set Multiset joinedSet = new Multiset(); //This is the old algorithm which is correct but naive and has O(n^2) complexity //foreach (ISet x in this.Sets) //{ // //New Minus logic based on the improved Join() logic // bool minus = other.Sets.Any(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); // //If no compatible sets then this set is preserved // if (!minus) // { // joinedSet.Add(x); // } //} //This is the new algorithm which is also correct but is O(3n) so much faster and scalable //Downside is that it does require more memory than the old algorithm List <HashTable <INode, int> > values = new List <HashTable <INode, int> >(); List <List <int> > nulls = new List <List <int> >(); foreach (String var in joinVars) { values.Add(new HashTable <INode, int>(HashTableBias.Enumeration)); nulls.Add(new List <int>()); } //First do a pass over the LHS Result to find all possible values for joined variables foreach (ISet x in this.Sets) { int i = 0; foreach (String var in joinVars) { INode value = x[var]; if (value != null) { values[i].Add(value, x.ID); } else { nulls[i].Add(x.ID); } i++; } } //Then do a pass over the RHS and work out the intersections HashSet <int> toMinus = new HashSet <int>(); foreach (ISet y in other.Sets) { IEnumerable <int> possMatches = null; int i = 0; foreach (String var in joinVars) { INode value = y[var]; if (value != null) { if (values[i].ContainsKey(value)) { possMatches = (possMatches == null ? values[i].GetValues(value).Concat(nulls[i]) : possMatches.Intersect(values[i].GetValues(value).Concat(nulls[i]))); } else { possMatches = Enumerable.Empty <int>(); break; } } else { //Don't forget that a null will be potentially compatible with everything possMatches = (possMatches == null ? this.SetIDs : possMatches.Intersect(this.SetIDs)); } i++; } if (possMatches == null) { continue; } //Look at possible matches, if is a valid match then mark the matched set for minus'ing //Don't reconsider sets which have already been marked for minusing foreach (int poss in possMatches) { if (toMinus.Contains(poss)) { continue; } if (this._sets[poss].IsCompatibleWith(y, joinVars)) { toMinus.Add(poss); } } } //Apply the actual minus if (toMinus.Count == this.Count) { //If number of sets to minus is equal to number of sets then we're minusing everything return(new NullMultiset()); } else { //Otherwise iterate foreach (ISet x in this.Sets) { if (!toMinus.Contains(x.ID)) { joinedSet.Add(x.Copy()); } } } return(joinedSet); }
/// <summary> /// Does an Exists Join of this Multiset to another Multiset where the Join is predicated on the existence/non-existence of a joinable solution on the RHS. /// </summary> /// <param name="other">Other Multiset.</param> /// <param name="mustExist">Whether a solution must exist in the Other Multiset for the join to be made.</param> /// <returns></returns> public virtual BaseMultiset ExistsJoin(BaseMultiset other, bool mustExist) { // For EXISTS and NOT EXISTS if the other is the Identity then it has no effect if (other is IdentityMultiset) { return(this); } if (mustExist) { // If an EXISTS then Null/Empty Other results in Null if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } } else { // If a NOT EXISTS then Null/Empty results in this if (other is NullMultiset) { return(this); } if (other.IsEmpty) { return(this); } } // Find the Variables that are to be used for Joining List <String> joinVars = Variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { // All Disjoint Solutions are compatible if (mustExist) { // If an EXISTS and disjoint then result is this return(this); } else { // If a NOT EXISTS and disjoint then result is null return(new NullMultiset()); } } // Start building the Joined Set Multiset joinedSet = new Multiset(); // This is the new algorithm which is also correct but is O(3n) so much faster and scalable // Downside is that it does require more memory than the old algorithm List <MultiDictionary <INode, List <int> > > values = new List <MultiDictionary <INode, List <int> > >(); List <List <int> > nulls = new List <List <int> >(); foreach (String var in joinVars) { joinedSet.AddVariable(var); values.Add(new MultiDictionary <INode, List <int> >(new FastVirtualNodeComparer())); nulls.Add(new List <int>()); } // First do a pass over the LHS Result to find all possible values for joined variables foreach (ISet x in Sets) { int i = 0; foreach (String var in joinVars) { INode value = x[var]; if (value != null) { if (values[i].TryGetValue(value, out List <int> ids)) { ids.Add(x.ID); } else { values[i].Add(value, new List <int> { x.ID }); } } else { nulls[i].Add(x.ID); } i++; } } // Then do a pass over the RHS and work out the intersections HashSet <int> exists = new HashSet <int>(); foreach (ISet y in other.Sets) { IEnumerable <int> possMatches = null; int i = 0; foreach (String var in joinVars) { INode value = y[var]; if (value != null) { if (values[i].ContainsKey(value)) { possMatches = (possMatches == null ? values[i][value].Concat(nulls[i]) : possMatches.Intersect(values[i][value].Concat(nulls[i]))); } else { possMatches = Enumerable.Empty <int>(); break; } } else { // Don't forget that a null will be potentially compatible with everything possMatches = (possMatches == null ? SetIDs : possMatches.Intersect(SetIDs)); } i++; } if (possMatches == null) { continue; } // Look at possible matches, if is a valid match then mark the set as having an existing match // Don't reconsider sets which have already been marked as having an existing match foreach (int poss in possMatches) { if (exists.Contains(poss)) { continue; } if (this[poss].IsCompatibleWith(y, joinVars)) { exists.Add(poss); } } } // Apply the actual exists if (exists.Count == Count) { // If number of sets that have a match is equal to number of sets then we're either returning everything or nothing if (mustExist) { return(this); } else { return(new NullMultiset()); } } else { // Otherwise iterate foreach (ISet x in Sets) { if (mustExist) { if (exists.Contains(x.ID)) { joinedSet.Add(x.Copy()); } } else { if (!exists.Contains(x.ID)) { joinedSet.Add(x.Copy()); } } } } return(joinedSet); }
/// <summary> /// Joins this Multiset to another Multiset /// </summary> /// <param name="other">Other Multiset</param> /// <returns></returns> public override BaseMultiset Join(BaseMultiset other) { //If the Other is the Identity Multiset the result is this Multiset if (other is IdentityMultiset) { return(this); } //If the Other is the Null Multiset the result is the Null Multiset if (other is NullMultiset) { return(other); } //If the Other is Empty then the result is the Null Multiset if (other.IsEmpty) { return(new NullMultiset()); } //Find the First Variable from this Multiset which is in both Multisets //If there is no Variable from this Multiset in the other Multiset then this //should be a Join operation instead of a LeftJoin List <String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { return(this.Product(other)); } //Start building the Joined Set Multiset joinedSet = new Multiset(); //This is the old Join algorithm which while correct is O(n^2) so scales terribly //foreach (ISet x in this.Sets) //{ // //For sets to be compatible for every joinable variable they must either have a null for the // //variable in one of the sets or if they have values the values must be equal // ////This first check is to try speed things up, it looks whether there are solutions that may match // ////without needing to do a full table scan of the RHS results as the subsequent LINQ call will do // ////if (!joinVars.All(v => x[v] == null || other.ContainsValue(v, x[v]) || other.ContainsValue(v, null))) continue; // IEnumerable<ISet> ys = other.Sets.Where(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); // //IEnumerable<ISet> ys = other.Sets.Where(s => s.IsCompatibleWith(x, joinVars)); // foreach (ISet y in ys) // { // joinedSet.Add(x.Join(y)); // } //} //This is the new Join algorithm which is also correct but is O(2n) so much faster and scalable //Downside is that it does require more memory than the old algorithm List <HashTable <INode, int> > values = new List <HashTable <INode, int> >(); List <List <int> > nulls = new List <List <int> >(); foreach (String var in joinVars) { values.Add(new HashTable <INode, int>(HashTableBias.Enumeration)); nulls.Add(new List <int>()); } //First do a pass over the LHS Result to find all possible values for joined variables foreach (ISet x in this.Sets) { int i = 0; foreach (String var in joinVars) { INode value = x[var]; if (value != null) { values[i].Add(value, x.ID); } else { nulls[i].Add(x.ID); } i++; } } #if NET40 && !SILVERLIGHT if (Options.UsePLinqEvaluation) { //Use a paralllel join other.Sets.AsParallel().ForAll(y => EvalJoin(y, joinVars, values, nulls, joinedSet)); } else { #endif //Use a serial join //Then do a pass over the RHS and work out the intersections foreach (ISet y in other.Sets) { this.EvalJoin(y, joinVars, values, nulls, joinedSet); } #if NET40 && !SILVERLIGHT } #endif return(joinedSet); }
/// <summary> /// Does a Minus Join of this Multiset to another Multiset where any joinable results are subtracted from this Multiset to give the resulting Multiset. /// </summary> /// <param name="other">Other Multiset.</param> /// <returns></returns> public virtual BaseMultiset MinusJoin(BaseMultiset other) { // If the other Multiset is the Identity/Null Multiset then minus-ing it doesn't alter this set if (other is IdentityMultiset) { return(this); } if (other is NullMultiset) { return(this); } // If the other Multiset is disjoint then minus-ing it also doesn't alter this set if (IsDisjointWith(other)) { return(this); } // Find the Variables that are to be used for Joining List <String> joinVars = Variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { return(Product(other)); } // Start building the Joined Set Multiset joinedSet = new Multiset(); // This is the new algorithm which is also correct but is O(3n) so much faster and scalable // Downside is that it does require more memory than the old algorithm List <MultiDictionary <INode, List <int> > > values = new List <MultiDictionary <INode, List <int> > >(); List <List <int> > nulls = new List <List <int> >(); foreach (String var in joinVars) { values.Add(new MultiDictionary <INode, List <int> >(new FastVirtualNodeComparer())); nulls.Add(new List <int>()); } // First do a pass over the LHS Result to find all possible values for joined variables foreach (ISet x in Sets) { int i = 0; foreach (String var in joinVars) { INode value = x[var]; if (value != null) { if (values[i].TryGetValue(value, out List <int> ids)) { ids.Add(x.ID); } else { values[i].Add(value, new List <int> { x.ID }); } } else { nulls[i].Add(x.ID); } i++; } } // Then do a pass over the RHS and work out the intersections HashSet <int> toMinus = new HashSet <int>(); foreach (ISet y in other.Sets) { IEnumerable <int> possMatches = null; int i = 0; foreach (String var in joinVars) { INode value = y[var]; if (value != null) { if (values[i].ContainsKey(value)) { possMatches = (possMatches == null ? values[i][value].Concat(nulls[i]) : possMatches.Intersect(values[i][value].Concat(nulls[i]))); } else { possMatches = Enumerable.Empty <int>(); break; } } else { // Don't forget that a null will be potentially compatible with everything possMatches = (possMatches == null ? SetIDs : possMatches.Intersect(SetIDs)); } i++; } if (possMatches == null) { continue; } // Look at possible matches, if is a valid match then mark the matched set for minus'ing // Don't reconsider sets which have already been marked for minusing foreach (int poss in possMatches) { if (toMinus.Contains(poss)) { continue; } if (this[poss].IsMinusCompatibleWith(y, joinVars)) { toMinus.Add(poss); } } } // Apply the actual minus if (toMinus.Count == Count) { // If number of sets to minus is equal to number of sets then we're minusing everything return(new NullMultiset()); } else { // Otherwise iterate foreach (ISet x in Sets) { if (!toMinus.Contains(x.ID)) { joinedSet.Add(x.Copy()); } } } return(joinedSet); }
/// <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); }
/// <summary> /// Joins this Multiset to another Multiset. /// </summary> /// <param name="other">Other Multiset.</param> /// <returns></returns> public virtual BaseMultiset Join(BaseMultiset other) { // If the Other is the Identity Multiset the result is this Multiset if (other is IdentityMultiset) { return(this); } // If the Other is the Null Multiset the result is the Null Multiset if (other is NullMultiset) { return(other); } // If the Other is Empty then the result is the Null Multiset if (other.IsEmpty) { return(new NullMultiset()); } // Find the First Variable from this Multiset which is in both Multisets // If there is no Variable from this Multiset in the other Multiset then this // should be a Product operation instead of a Join List <String> joinVars = Variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { return(Product(other)); } // Start building the Joined Set Multiset joinedSet = new Multiset(); // This is the new Join algorithm which is O(2n) so much faster and scalable // Downside is that it does require more memory than the old algorithm List <MultiDictionary <INode, List <int> > > values = new List <MultiDictionary <INode, List <int> > >(); List <List <int> > nulls = new List <List <int> >(); foreach (String var in joinVars) { joinedSet.AddVariable(var); values.Add(new MultiDictionary <INode, List <int> >(new FastVirtualNodeComparer())); nulls.Add(new List <int>()); } // First do a pass over the LHS Result to find all possible values for joined variables foreach (ISet x in Sets) { var i = 0; foreach (var var in joinVars) { var value = x[var]; if (value != null) { if (values[i].TryGetValue(value, out List <int> ids)) { ids.Add(x.ID); } else { values[i].Add(value, new List <int> { x.ID }); } } else { nulls[i].Add(x.ID); } i++; } } #if NET40 if (Options.UsePLinqEvaluation) { // Use a paralllel join other.Sets.AsParallel().ForAll(y => EvalJoin(y, joinVars, values, nulls, joinedSet)); } else { // Use a serial join // Then do a pass over the RHS and work out the intersections foreach (ISet y in other.Sets) { this.EvalJoin(y, joinVars, values, nulls, joinedSet); } } #else // Use a serial join // Then do a pass over the RHS and work out the intersections foreach (ISet y in other.Sets) { EvalJoin(y, joinVars, values, nulls, joinedSet); } #endif return(joinedSet); }
/// <summary> /// Evaluates a Group By by generating a <see cref="GroupMultiset">GroupMultiset</see> from the Input Multiset /// </summary> /// <param name="context">SPARQL Evaluation Context</param> /// <returns></returns> public BaseMultiset Evaluate(SparqlEvaluationContext context) { BaseMultiset results = context.Evaluate(this._pattern); context.InputMultiset = results; //Identity/Null yields an empty multiset if (context.InputMultiset is IdentityMultiset || context.InputMultiset is NullMultiset) { results = new Multiset(); } GroupMultiset groupSet = new GroupMultiset(results); List <BindingGroup> groups; //Calculate Groups if (context.Query.GroupBy != null) { groups = context.Query.GroupBy.Apply(context); } else if (this._grouping != null) { groups = this._grouping.Apply(context); } else { groups = new List <BindingGroup>() { new BindingGroup(results.SetIDs) }; } //Add Groups to the GroupMultiset HashSet <String> vars = new HashSet <String>(); foreach (BindingGroup group in groups) { foreach (KeyValuePair <String, INode> assignment in group.Assignments) { if (vars.Contains(assignment.Key)) { continue; } groupSet.AddVariable(assignment.Key); vars.Add(assignment.Key); } groupSet.AddGroup(group); } //If grouping produced no groups and there are aggregates present //then an implicit group is created if (groups.Count == 0 && this._aggregates.Count > 0) { groupSet.AddGroup(new BindingGroup()); } //Apply the aggregates context.InputMultiset = groupSet; context.Binder.SetGroupContext(true); foreach (SparqlVariable var in this._aggregates) { if (!vars.Contains(var.Name)) { groupSet.AddVariable(var.Name); vars.Add(var.Name); } foreach (ISet s in groupSet.Sets) { try { INode value = var.Aggregate.Apply(context, groupSet.GroupSetIDs(s.ID)); s.Add(var.Name, value); } catch (RdfQueryException) { s.Add(var.Name, null); } } } context.Binder.SetGroupContext(false); context.OutputMultiset = groupSet; return(context.OutputMultiset); }
/// <summary> /// Evaluates the Graph Clause by setting up the dataset, applying the pattern and then generating additional bindings if necessary /// </summary> /// <param name="context">Evaluation Context</param> /// <returns></returns> public BaseMultiset Evaluate(SparqlEvaluationContext context) { // Q: Can we optimise GRAPH when the input is the Null Multiset to just return the Null Multiset? bool datasetOk = false; try { List <String> activeGraphs = new List <string>(); // Get the URIs of Graphs that should be evaluated over if (_graphSpecifier.TokenType != Token.VARIABLE) { switch (_graphSpecifier.TokenType) { case Token.URI: case Token.QNAME: Uri activeGraphUri = UriFactory.Create(Tools.ResolveUriOrQName(_graphSpecifier, context.Query.NamespaceMap, context.Query.BaseUri)); if (context.Data.HasGraph(activeGraphUri)) { // If the Graph is explicitly specified and there are FROM/FROM NAMED present then the Graph // URI must be in the graphs specified by a FROM/FROM NAMED or the result is null if (context.Query == null || ((!context.Query.DefaultGraphs.Any() && !context.Query.NamedGraphs.Any()) || context.Query.NamedGraphs.Any(u => EqualityHelper.AreUrisEqual(activeGraphUri, u))) ) { // Either there was no Query // OR there were no Default/Named Graphs (hence any Graph URI is permitted) // OR the specified URI was a Named Graph URI // In any case we can go ahead and set the active Graph activeGraphs.Add(activeGraphUri.AbsoluteUri); } else { // The specified URI was not present in the Named Graphs so return null context.OutputMultiset = new NullMultiset(); return(context.OutputMultiset); } } else { // If specifies a specific Graph and not in the Dataset result is a null multiset context.OutputMultiset = new NullMultiset(); return(context.OutputMultiset); } break; default: throw new RdfQueryException("Cannot use a '" + _graphSpecifier.GetType().ToString() + "' Token to specify the Graph for a GRAPH clause"); } } else { String gvar = _graphSpecifier.Value.Substring(1); // Watch out for the case in which the Graph Variable is not bound for all Sets in which case // we still need to operate over all Graphs if (context.InputMultiset.ContainsVariable(gvar) && context.InputMultiset.Sets.All(s => s[gvar] != null)) { // If there are already values bound to the Graph variable for all Input Solutions then we limit the Query to those Graphs List <Uri> graphUris = new List <Uri>(); foreach (ISet s in context.InputMultiset.Sets) { INode temp = s[gvar]; if (temp == null) { continue; } if (temp.NodeType != NodeType.Uri) { continue; } activeGraphs.Add(temp.ToString()); graphUris.Add(((IUriNode)temp).Uri); } } else { // Nothing yet bound to the Graph Variable so the Query is over all the named Graphs if (context.Query != null && context.Query.NamedGraphs.Any()) { // Query specifies one/more named Graphs activeGraphs.AddRange(context.Query.NamedGraphs.Select(u => u.AbsoluteUri)); } else if (context.Query != null && context.Query.DefaultGraphs.Any() && !context.Query.NamedGraphs.Any()) { // Gives null since the query dataset does not include any named graphs context.OutputMultiset = new NullMultiset(); return(context.OutputMultiset); } else { // Query is over entire dataset/default Graph since no named Graphs are explicitly specified activeGraphs.AddRange(context.Data.GraphUris.Select(u => u.ToSafeString())); } } } // Remove all duplicates from Active Graphs to avoid duplicate results activeGraphs = activeGraphs.Distinct().ToList(); // Evaluate the inner pattern BaseMultiset initialInput = context.InputMultiset; BaseMultiset finalResult = new Multiset(); // Evalute for each Graph URI and union the results foreach (String uri in activeGraphs) { // Always use the same Input for each Graph URI and set that Graph to be the Active Graph // Be sure to translate String.Empty back to the null URI to select the default graph // correctly context.InputMultiset = initialInput; Uri currGraphUri = (uri.Equals(String.Empty)) ? null : UriFactory.Create(uri); // Set Active Graph if (currGraphUri == null) { // GRAPH operates over named graphs only so default graph gets skipped continue; } // The result of the HasGraph() call is ignored we just make it so datasets with any kind of // load on demand behaviour work properly context.Data.HasGraph(currGraphUri); // All we actually care about is setting the active graph context.Data.SetActiveGraph(currGraphUri); datasetOk = true; // Evaluate for the current Active Graph BaseMultiset result = context.Evaluate(_pattern); // Merge the Results into our overall Results if (result is NullMultiset) { // Don't do anything, adds nothing to the results } else if (result is IdentityMultiset) { // Adds a single row to the results if (_graphSpecifier.TokenType == Token.VARIABLE) { // Include graph variable if not yet bound INode currGraph = new UriNode(null, currGraphUri); Set s = new Set(); s.Add(_graphSpecifier.Value.Substring(1), currGraph); finalResult.Add(s); } else { finalResult.Add(new Set()); } } else { // If the Graph Specifier is a Variable then we must either bind the // variable or eliminate solutions which have an incorrect value for it if (_graphSpecifier.TokenType == Token.VARIABLE) { String gvar = _graphSpecifier.Value.Substring(1); INode currGraph = new UriNode(null, currGraphUri); foreach (int id in result.SetIDs.ToList()) { ISet s = result[id]; if (s[gvar] == null) { // If Graph Variable is not yet bound for solution bind it s.Add(gvar, currGraph); } else if (!s[gvar].Equals(currGraph)) { // If Graph Variable is bound for solution and doesn't match // current Graph then we have to remove the solution result.Remove(id); } } } // Union solutions into the Results finalResult.Union(result); } // Reset the Active Graph after each pass context.Data.ResetActiveGraph(); datasetOk = false; } // Return the final result if (finalResult.IsEmpty) { finalResult = new NullMultiset(); } context.OutputMultiset = finalResult; } finally { if (datasetOk) { context.Data.ResetActiveGraph(); } } return(context.OutputMultiset); }
private BaseMultiset StreamingEvaluate(SparqlEvaluationContext context, int pattern, out bool halt) { halt = false; //Handle Empty BGPs if (pattern == 0 && this._triplePatterns.Count == 0) { context.OutputMultiset = new IdentityMultiset(); return context.OutputMultiset; } BaseMultiset initialInput, localOutput, results; //Set up the Input and Output Multiset appropriately switch (pattern) { case 0: //Input is as given and Output is new empty multiset initialInput = context.InputMultiset; localOutput = new Multiset(); break; case 1: //Input becomes current Output and Output is new empty multiset initialInput = context.OutputMultiset; localOutput = new Multiset(); break; default: //Input is join of previous input and ouput and Output is new empty multiset if (context.InputMultiset.IsDisjointWith(context.OutputMultiset)) { //Disjoint so do a Product initialInput = context.InputMultiset.Product(context.OutputMultiset); } else { //Normal Join initialInput = context.InputMultiset.Join(context.OutputMultiset); } localOutput = new Multiset(); break; } context.InputMultiset = initialInput; context.OutputMultiset = localOutput; //Get the Triple Pattern we're evaluating ITriplePattern temp = this._triplePatterns[pattern]; int resultsFound = 0; if (temp is TriplePattern) { //Find the first Triple which matches the Pattern TriplePattern tp = (TriplePattern)temp; foreach (Triple t in tp.GetTriples(context)) { //Remember to check for Timeout during lazy evaluation context.CheckTimeout(); if (tp.Accepts(context, t)) { resultsFound++; context.OutputMultiset.Add(tp.CreateResult(t)); //Recurse unless we're the last pattern if (pattern < this._triplePatterns.Count - 1) { results = this.StreamingEvaluate(context, pattern + 1, out halt); //If recursion leads to a halt then we halt and return immediately if (halt) return results; //Otherwise we need to keep going here //So must reset our input and outputs before continuing context.InputMultiset = initialInput; context.OutputMultiset = new Multiset(); resultsFound--; } else { //If we're at the last pattern and we've found a match then we can halt halt = true; //Generate the final output and return it if (context.InputMultiset.IsDisjointWith(context.OutputMultiset)) { //Disjoint so do a Product context.OutputMultiset = context.InputMultiset.ProductWithTimeout(context.OutputMultiset, context.QueryTimeout - context.QueryTime); } else { //Normal Join context.OutputMultiset = context.InputMultiset.Join(context.OutputMultiset); } return context.OutputMultiset; } } } } else if (temp is FilterPattern) { FilterPattern fp = (FilterPattern)temp; ISparqlFilter filter = fp.Filter; ISparqlExpression expr = filter.Expression; //Find the first result of those we've got so far that matches if (context.InputMultiset is IdentityMultiset || context.InputMultiset.IsEmpty) { try { //If the Input is the Identity Multiset then the Output is either //the Identity/Null Multiset depending on whether the Expression evaluates to true if (expr.EffectiveBooleanValue(context, 0)) { context.OutputMultiset = new IdentityMultiset(); } else { context.OutputMultiset = new NullMultiset(); } } catch { //If Expression fails to evaluate then result is NullMultiset context.OutputMultiset = new NullMultiset(); } } else { foreach (int id in context.InputMultiset.SetIDs) { //Remember to check for Timeout during lazy evaluation context.CheckTimeout(); try { if (expr.EffectiveBooleanValue(context, id)) { resultsFound++; context.OutputMultiset.Add(context.InputMultiset[id]); //Recurse unless we're the last pattern if (pattern < this._triplePatterns.Count - 1) { results = this.StreamingEvaluate(context, pattern + 1, out halt); //If recursion leads to a halt then we halt and return immediately if (halt) return results; //Otherwise we need to keep going here //So must reset our input and outputs before continuing context.InputMultiset = initialInput; context.OutputMultiset = new Multiset(); resultsFound--; } else { //If we're at the last pattern and we've found a match then we can halt halt = true; //Generate the final output and return it if (context.InputMultiset.IsDisjointWith(context.OutputMultiset)) { //Disjoint so do a Product context.OutputMultiset = context.InputMultiset.ProductWithTimeout(context.OutputMultiset, context.RemainingTimeout); } else { //Normal Join context.OutputMultiset = context.InputMultiset.Join(context.OutputMultiset); } return context.OutputMultiset; } } } catch { //Ignore expression evaluation errors } } } } //If we found no possibles we return the null multiset if (resultsFound == 0) return new NullMultiset(); //We should never reach here so throw an error to that effect //The reason we'll never reach here is that this method should always return earlier throw new RdfQueryException("Unexpected control flow in evaluating a Streamed BGP for an ASK query"); }
private BaseMultiset StreamingEvaluate(SparqlEvaluationContext context, int pattern, out bool halt) { halt = false; //Handle Empty BGPs if (pattern == 0 && this._triplePatterns.Count == 0) { context.OutputMultiset = new IdentityMultiset(); return(context.OutputMultiset); } BaseMultiset initialInput, localOutput, results = null; //Determine whether the Pattern modifies the existing Input rather than joining to it bool modifies = (this._triplePatterns[pattern] is FilterPattern); bool extended = (pattern > 0 && this._triplePatterns[pattern - 1] is BindPattern); bool modified = (pattern > 0 && this._triplePatterns[pattern - 1] is FilterPattern); //Set up the Input and Output Multiset appropriately switch (pattern) { case 0: //Input is as given and Output is new empty multiset if (!modifies) { initialInput = context.InputMultiset; } else { //If the Pattern will modify the Input and is the first thing in the BGP then it actually modifies a new empty input //This takes care of FILTERs being out of scope initialInput = new Multiset(); } localOutput = new Multiset(); break; case 1: //Input becomes current Output and Output is new empty multiset initialInput = context.OutputMultiset; localOutput = new Multiset(); break; default: if (!extended && !modified) { //Input is join of previous input and output and Output is new empty multiset if (context.InputMultiset.IsDisjointWith(context.OutputMultiset)) { //Disjoint so do a Product initialInput = context.InputMultiset.ProductWithTimeout(context.OutputMultiset, context.RemainingTimeout); } else { //Normal Join initialInput = context.InputMultiset.Join(context.OutputMultiset); } } else { initialInput = context.OutputMultiset; } localOutput = new Multiset(); break; } context.InputMultiset = initialInput; context.OutputMultiset = localOutput; //Get the Triple Pattern we're evaluating ITriplePattern temp = this._triplePatterns[pattern]; int resultsFound = 0; int prevResults = -1; if (temp is TriplePattern) { //Find the first Triple which matches the Pattern TriplePattern tp = (TriplePattern)temp; IEnumerable <Triple> ts = tp.GetTriples(context); //In the case that we're lazily evaluating an optimisable ORDER BY then //we need to apply OrderBy()'s to our enumeration //This only applies to the 1st pattern if (pattern == 0) { if (context.Query != null) { if (context.Query.OrderBy != null && context.Query.IsOptimisableOrderBy) { IComparer <Triple> comparer = context.Query.OrderBy.GetComparer(tp); if (comparer != null) { ts = ts.OrderBy(t => t, comparer); } else { //Can't get a comparer so can't optimise this._requiredResults = -1; } } } } foreach (Triple t in ts) { //Remember to check for Timeouts during Lazy Evaluation context.CheckTimeout(); if (tp.Accepts(context, t)) { resultsFound++; if (tp.IndexType == TripleIndexType.NoVariables) { localOutput = new IdentityMultiset(); context.OutputMultiset = localOutput; } else { context.OutputMultiset.Add(tp.CreateResult(t)); } //Recurse unless we're the last pattern if (pattern < this._triplePatterns.Count - 1) { results = this.StreamingEvaluate(context, pattern + 1, out halt); //If recursion leads to a halt then we halt and return immediately if (halt && results.Count >= this._requiredResults && this._requiredResults != -1) { return(results); } else if (halt) { if (results.Count == 0) { //If recursing leads to no results then eliminate all outputs //Also reset to prevResults to -1 resultsFound = 0; localOutput = new Multiset(); prevResults = -1; } else if (prevResults > -1) { if (results.Count == prevResults) { //If the amount of results found hasn't increased then this match does not //generate any further solutions further down the recursion so we can eliminate //this from the results localOutput.Remove(localOutput.SetIDs.Max()); } } prevResults = results.Count; //If we're supposed to halt but not reached the number of required results then continue context.InputMultiset = initialInput; context.OutputMultiset = localOutput; } else { //Otherwise we need to keep going here //So must reset our input and outputs before continuing context.InputMultiset = initialInput; context.OutputMultiset = new Multiset(); resultsFound--; } } else { //If we're at the last pattern and we've found a match then we can halt halt = true; //Generate the final output and return it if (context.InputMultiset.IsDisjointWith(context.OutputMultiset)) { //Disjoint so do a Product results = context.InputMultiset.ProductWithTimeout(context.OutputMultiset, context.RemainingTimeout); } else { //Normal Join results = context.InputMultiset.Join(context.OutputMultiset); } //If not reached required number of results continue if (results.Count >= this._requiredResults && this._requiredResults != -1) { context.OutputMultiset = results; return(context.OutputMultiset); } } } } } else if (temp is FilterPattern) { FilterPattern filter = (FilterPattern)temp; ISparqlExpression filterExpr = filter.Filter.Expression; if (filter.Variables.IsDisjoint(context.InputMultiset.Variables)) { //Remember to check for Timeouts during Lazy Evaluation context.CheckTimeout(); //Filter is Disjoint so determine whether it has any affect or not if (filter.Variables.Any()) { //Has Variables but disjoint from input => not in scope so gets ignored //Do we recurse or not? if (pattern < this._triplePatterns.Count - 1) { //Recurse and return results = this.StreamingEvaluate(context, pattern + 1, out halt); return(results); } else { //We don't affect the input in any way so just return it return(context.InputMultiset); } } else { //No Variables so have to evaluate it to see if it gives true otherwise try { if (filterExpr.EffectiveBooleanValue(context, 0)) { if (pattern < this._triplePatterns.Count - 1) { //Recurse and return results = this.StreamingEvaluate(context, pattern + 1, out halt); return(results); } else { //Last Pattern and we evaluate to true so can return the input as-is halt = true; return(context.InputMultiset); } } } catch (RdfQueryException) { //Evaluates to false so eliminates all solutions (use an empty Multiset) return(new Multiset()); } } } else { //Remember to check for Timeouts during Lazy Evaluation context.CheckTimeout(); //Test each solution found so far against the Filter and eliminate those that evalute to false/error foreach (int id in context.InputMultiset.SetIDs.ToList()) { try { if (filterExpr.EffectiveBooleanValue(context, id)) { //If evaluates to true then add to output context.OutputMultiset.Add(context.InputMultiset[id]); } } catch (RdfQueryException) { //Error means we ignore the solution } } //Remember to check for Timeouts during Lazy Evaluation context.CheckTimeout(); //Decide whether to recurse or not resultsFound = context.OutputMultiset.Count; if (pattern < this._triplePatterns.Count - 1) { //Recurse then return //We can never decide whether to recurse again at this point as we are not capable of deciding //which solutions should be dumped (that is the job of an earlier pattern in the BGP) results = this.StreamingEvaluate(context, pattern + 1, out halt); return(results); } else { halt = true; //However many results we need we'll halt - previous patterns can call us again if they find more potential solutions //for us to filter return(context.OutputMultiset); } } } else if (temp is BindPattern) { BindPattern bind = (BindPattern)temp; ISparqlExpression bindExpr = bind.AssignExpression; String bindVar = bind.VariableName; if (context.InputMultiset.ContainsVariable(bindVar)) { throw new RdfQueryException("Cannot use a BIND assigment to BIND to a variable that has previously been used in the Query"); } else { //Remember to check for Timeouts during Lazy Evaluation context.CheckTimeout(); //Compute the Binding for every value context.OutputMultiset.AddVariable(bindVar); foreach (ISet s in context.InputMultiset.Sets) { ISet x = s.Copy(); try { INode val = bindExpr.Value(context, s.ID); x.Add(bindVar, val); } catch (RdfQueryException) { //Equivalent to no assignment but the solution is preserved } context.OutputMultiset.Add(x); } //Remember to check for Timeouts during Lazy Evaluation context.CheckTimeout(); //Decide whether to recurse or not resultsFound = context.OutputMultiset.Count; if (pattern < this._triplePatterns.Count - 1) { //Recurse then return results = this.StreamingEvaluate(context, pattern + 1, out halt); return(results); } else { halt = true; //However many results we need we'll halt - previous patterns can call us again if they find more potential solutions //for us to extend return(context.OutputMultiset); } } } else { throw new RdfQueryException("Encountered a " + temp.GetType().FullName + " which is not a lazily evaluable Pattern"); } //If we found no possibles we return the null multiset if (resultsFound == 0) { return(new NullMultiset()); } else { //Generate the final output and return it if (!modifies) { if (context.InputMultiset.IsDisjointWith(context.OutputMultiset)) { //Disjoint so do a Product results = context.InputMultiset.ProductWithTimeout(context.OutputMultiset, context.RemainingTimeout); } else { //Normal Join results = context.InputMultiset.Join(context.OutputMultiset); } context.OutputMultiset = results; } return(context.OutputMultiset); } }
/// <summary> /// Joins this Multiset to another Multiset /// </summary> /// <param name="other">Other Multiset</param> /// <returns></returns> public override BaseMultiset Join(BaseMultiset other) { //If the Other is the Identity Multiset the result is this Multiset if (other is IdentityMultiset) return this; //If the Other is the Null Multiset the result is the Null Multiset if (other is NullMultiset) return other; //If the Other is Empty then the result is the Null Multiset if (other.IsEmpty) return new NullMultiset(); //Find the First Variable from this Multiset which is in both Multisets //If there is no Variable from this Multiset in the other Multiset then this //should be a Join operation instead of a LeftJoin List<String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) return this.Product(other); //Start building the Joined Set Multiset joinedSet = new Multiset(); //This is the old Join algorithm which while correct is O(n^2) so scales terribly //foreach (ISet x in this.Sets) //{ // //For sets to be compatible for every joinable variable they must either have a null for the // //variable in one of the sets or if they have values the values must be equal // ////This first check is to try speed things up, it looks whether there are solutions that may match // ////without needing to do a full table scan of the RHS results as the subsequent LINQ call will do // ////if (!joinVars.All(v => x[v] == null || other.ContainsValue(v, x[v]) || other.ContainsValue(v, null))) continue; // IEnumerable<ISet> ys = other.Sets.Where(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); // //IEnumerable<ISet> ys = other.Sets.Where(s => s.IsCompatibleWith(x, joinVars)); // foreach (ISet y in ys) // { // joinedSet.Add(x.Join(y)); // } //} //This is the new Join algorithm which is also correct but is O(2n) so much faster and scalable //Downside is that it does require more memory than the old algorithm List<HashTable<INode, int>> values = new List<HashTable<INode, int>>(); List<List<int>> nulls = new List<List<int>>(); foreach (String var in joinVars) { values.Add(new HashTable<INode, int>(HashTableBias.Enumeration)); nulls.Add(new List<int>()); } //First do a pass over the LHS Result to find all possible values for joined variables foreach (ISet x in this.Sets) { int i = 0; foreach (String var in joinVars) { INode value = x[var]; if (value != null) { values[i].Add(value, x.ID); } else { nulls[i].Add(x.ID); } i++; } } #if NET40 && !SILVERLIGHT if (Options.UsePLinqEvaluation) { //Use a paralllel join other.Sets.AsParallel().ForAll(y => EvalJoin(y, joinVars, values, nulls, joinedSet)); } else { #endif //Use a serial join //Then do a pass over the RHS and work out the intersections foreach (ISet y in other.Sets) { this.EvalJoin(y, joinVars, values, nulls, joinedSet); } #if NET40 && !SILVERLIGHT } #endif return joinedSet; }
public void SparqlMultisetLeftJoin() { //Create a load of Nodes to use in the tests Graph g = new Graph(); g.NamespaceMap.AddNamespace(String.Empty, new Uri("http://example.org")); IUriNode s1 = g.CreateUriNode(":s1"); IUriNode s2 = g.CreateUriNode(":s2"); IUriNode p1 = g.CreateUriNode(":p1"); IUriNode p2 = g.CreateUriNode(":p2"); IUriNode rdfsLabel = g.CreateUriNode("rdfs:label"); ILiteralNode o1 = g.CreateLiteralNode("Some Text"); ILiteralNode o2 = g.CreateLiteralNode("1", new Uri(XmlSpecsHelper.XmlSchemaDataTypeInteger)); //Create an ID and Null Multiset IdentityMultiset id = new IdentityMultiset(); NullMultiset nullset = new NullMultiset(); //Create and Populate a Multiset Multiset m = new Multiset(); Set s = new Set(); s.Add("s", s1); s.Add("p", p1); s.Add("o", o1); m.Add(s); s = new Set(); s.Add("s", s2); s.Add("p", p2); s.Add("o", o2); m.Add(s); //Create and Populate another Multiset Multiset n = new Multiset(); s = new Set(); s.Add("s", s1); s.Add("label", o1); n.Add(s); //Create and Populate another Multiset Multiset d = new Multiset(); s = new Set(); s.Add("s1", s1); s.Add("p1", p1); s.Add("o1", o1); d.Add(s); s = new Set(); s.Add("s1", s2); s.Add("p1", p2); s.Add("o1", o2); d.Add(s); //Show the Sets Console.WriteLine("LHS"); foreach (Set set in m.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); Console.WriteLine("RHS"); foreach (Set set in n.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); Console.WriteLine("D"); foreach (Set set in d.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); //Try a Join to Identity Console.WriteLine("Join ID-LHS"); BaseMultiset join = id.Join(m); foreach (Set set in join.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); //Try a Join to Identity Console.WriteLine("Join LHS-ID"); join = m.Join(id); foreach (Set set in join.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); //Try a Join to Null Console.WriteLine("Join NULL-LHS"); join = nullset.Join(m); foreach (Set set in join.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); //Try a Join to Null Console.WriteLine("Join LHS-NULL"); join = m.Join(nullset); foreach (Set set in join.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); //Try a LeftJoin Console.WriteLine("LeftJoin NULL-LHS"); BaseMultiset leftjoin = nullset.LeftJoin(m, new BooleanExpressionTerm(true)); foreach (Set set in leftjoin.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); //Try a LeftJoin Console.WriteLine("LeftJoin LHS-NULL"); leftjoin = m.LeftJoin(nullset, new BooleanExpressionTerm(true)); foreach (Set set in leftjoin.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); //Try a Join Console.WriteLine("Join LHS-RHS"); join = m.Join(n); foreach (Set set in join.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); //Try a LeftOuterJoin Console.WriteLine("LeftJoin LHS-RHS"); leftjoin = m.LeftJoin(n, new BooleanExpressionTerm(true)); foreach (Set set in leftjoin.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); //Try a Produce Console.WriteLine("Product LHS-RHS"); BaseMultiset product = m.Product(n); foreach (Set set in product.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); //Try a Join to Self Console.WriteLine("Product LHS-D"); product = m.Product(d); foreach (Set set in product.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); //Try a Union Console.WriteLine("Union LHS-RHS"); BaseMultiset union = m.Union(n); foreach (Set set in union.Sets) { Console.WriteLine(set.ToString()); } Console.WriteLine(); }
/// <summary> /// Does a Minus Join of this Multiset to another Multiset where any joinable results are subtracted from this Multiset to give the resulting Multiset /// </summary> /// <param name="other">Other Multiset</param> /// <returns></returns> public override BaseMultiset MinusJoin(BaseMultiset other) { //If the other Multiset is the Identity/Null Multiset then minus-ing it doesn't alter this set if (other is IdentityMultiset) return this; if (other is NullMultiset) return this; //If the other Multiset is disjoint then minus-ing it also doesn't alter this set if (this.IsDisjointWith(other)) return this; //Find the Variables that are to be used for Joining List<String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) return this.Product(other); //Start building the Joined Set Multiset joinedSet = new Multiset(); //This is the old algorithm which is correct but naive and has O(n^2) complexity //foreach (ISet x in this.Sets) //{ // //New Minus logic based on the improved Join() logic // bool minus = other.Sets.Any(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); // //If no compatible sets then this set is preserved // if (!minus) // { // joinedSet.Add(x); // } //} //This is the new algorithm which is also correct but is O(3n) so much faster and scalable //Downside is that it does require more memory than the old algorithm List<HashTable<INode, int>> values = new List<HashTable<INode, int>>(); List<List<int>> nulls = new List<List<int>>(); foreach (String var in joinVars) { values.Add(new HashTable<INode, int>(HashTableBias.Enumeration)); nulls.Add(new List<int>()); } //First do a pass over the LHS Result to find all possible values for joined variables foreach (ISet x in this.Sets) { int i = 0; foreach (String var in joinVars) { INode value = x[var]; if (value != null) { values[i].Add(value, x.ID); } else { nulls[i].Add(x.ID); } i++; } } //Then do a pass over the RHS and work out the intersections HashSet<int> toMinus = new HashSet<int>(); foreach (ISet y in other.Sets) { IEnumerable<int> possMatches = null; int i = 0; foreach (String var in joinVars) { INode value = y[var]; if (value != null) { if (values[i].ContainsKey(value)) { possMatches = (possMatches == null ? values[i].GetValues(value).Concat(nulls[i]) : possMatches.Intersect(values[i].GetValues(value).Concat(nulls[i]))); } else { possMatches = Enumerable.Empty<int>(); break; } } else { //Don't forget that a null will be potentially compatible with everything possMatches = (possMatches == null ? this.SetIDs : possMatches.Intersect(this.SetIDs)); } i++; } if (possMatches == null) continue; //Look at possible matches, if is a valid match then mark the matched set for minus'ing //Don't reconsider sets which have already been marked for minusing foreach (int poss in possMatches) { if (toMinus.Contains(poss)) continue; if (this._sets[poss].IsCompatibleWith(y, joinVars)) { toMinus.Add(poss); } } } //Apply the actual minus if (toMinus.Count == this.Count) { //If number of sets to minus is equal to number of sets then we're minusing everything return new NullMultiset(); } else { //Otherwise iterate foreach (ISet x in this.Sets) { if (!toMinus.Contains(x.ID)) { joinedSet.Add(x.Copy()); } } } return joinedSet; }
/// <summary> /// Does a Left Join of this Multiset to another Multiset where the Join is predicated on the given Expression /// </summary> /// <param name="other">Other Multiset</param> /// <param name="expr">Expression</param> /// <returns></returns> public override BaseMultiset LeftJoin(BaseMultiset other, ISparqlExpression expr) { //If the Other is the Identity/Null Multiset the result is this Multiset if (other is IdentityMultiset) { return(this); } if (other is NullMultiset) { return(this); } if (other.IsEmpty) { return(this); } Multiset joinedSet = new Multiset(); LeviathanLeftJoinBinder binder = new LeviathanLeftJoinBinder(joinedSet); SparqlEvaluationContext subcontext = new SparqlEvaluationContext(binder); //Find the First Variable from this Multiset which is in both Multisets //If there is no Variable from this Multiset in the other Multiset then this //should be a Join operation instead of a LeftJoin List <String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { //Calculate a Product filtering as we go foreach (ISet x in this.Sets) { bool standalone = false; foreach (ISet y in other.Sets) { ISet z = x.Join(y); try { joinedSet.Add(z); if (!expr.Evaluate(subcontext, z.ID).AsSafeBoolean()) { joinedSet.Remove(z.ID); standalone = true; } } catch { joinedSet.Remove(z.ID); standalone = true; } } if (standalone) { joinedSet.Add(x.Copy()); } } } else { //This is the old algorithm which is correct but has complexity O(n^2) so it scales terribly //foreach (ISet x in this.Sets) //{ // IEnumerable<ISet> ys = other.Sets.Where(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); // //IEnumerable<ISet> ys = other.Sets.Where(s => s.IsCompatibleWith(x, joinVars)); // bool standalone = false; // int i = 0; // foreach (ISet y in ys) // { // i++; // ISet z = x.Join(y); // try // { // joinedSet.Add(z); // if (!expr.Evaluate(subcontext, z.ID).AsSafeBoolean()) // { // joinedSet.Remove(z.ID); // standalone = true; // } // } // catch // { // joinedSet.Remove(z.ID); // standalone = true; // } // } // if (standalone || i == 0) joinedSet.Add(x); //} //This is the new Join algorithm which is also correct but is O(2n) so much faster and scalable //Downside is that it does require more memory than the old algorithm List <HashTable <INode, int> > values = new List <HashTable <INode, int> >(); List <List <int> > nulls = new List <List <int> >(); foreach (String var in joinVars) { values.Add(new HashTable <INode, int>(HashTableBias.Enumeration)); nulls.Add(new List <int>()); } //First do a pass over the LHS Result to find all possible values for joined variables HashSet <int> matched = new HashSet <int>(); HashSet <int> standalone = new HashSet <int>(); foreach (ISet x in this.Sets) { int i = 0; foreach (String var in joinVars) { INode value = x[var]; if (value != null) { values[i].Add(value, x.ID); } else { nulls[i].Add(x.ID); } i++; } } //Then do a pass over the RHS and work out the intersections foreach (ISet y in other.Sets) { IEnumerable <int> possMatches = null; int i = 0; foreach (String var in joinVars) { INode value = y[var]; if (value != null) { if (values[i].ContainsKey(value)) { possMatches = (possMatches == null ? values[i].GetValues(value).Concat(nulls[i]) : possMatches.Intersect(values[i].GetValues(value).Concat(nulls[i]))); } else { possMatches = Enumerable.Empty <int>(); break; } } else { //Don't forget that a null will be potentially compatible with everything possMatches = (possMatches == null ? this.SetIDs : possMatches.Intersect(this.SetIDs)); } i++; } if (possMatches == null) { continue; } //Now do the actual joins for the current set //Note - We access the dictionary directly here because going through the this[int id] method //incurs a Contains() call each time and we know the IDs must exist because they came from //our dictionary originally! foreach (int poss in possMatches) { if (this._sets[poss].IsCompatibleWith(y, joinVars)) { ISet z = this._sets[poss].Join(y); joinedSet.Add(z); try { if (!expr.Evaluate(subcontext, z.ID).AsSafeBoolean()) { joinedSet.Remove(z.ID); standalone.Add(poss); } else { matched.Add(poss); } } catch { joinedSet.Remove(z.ID); standalone.Add(poss); } } } } //Finally add in unmatched sets from LHS foreach (int id in this.SetIDs) { if (!matched.Contains(id) || standalone.Contains(id)) { joinedSet.Add(this._sets[id].Copy()); } } } return(joinedSet); }
/// <summary> /// Converts a Bindings Clause to a Multiset /// </summary> /// <returns></returns> public BaseMultiset ToMultiset() { if (this._vars.Any()) { Multiset m = new Multiset(); foreach (String var in this._vars) { m.AddVariable(var); } foreach (BindingTuple tuple in this._tuples) { m.Add(new Set(tuple)); } return m; } else { return new IdentityMultiset(); } }
/// <summary> /// Calculates the product of two mutlisets asynchronously with a timeout to restrict long running computations. /// </summary> /// <param name="multiset">Multiset.</param> /// <param name="other">Other Multiset.</param> /// <param name="timeout">Timeout, if <=0 no timeout is used and product will be computed sychronously.</param> /// <returns></returns> public static BaseMultiset ProductWithTimeout(this BaseMultiset multiset, BaseMultiset other, long timeout) { if (other is IdentityMultiset) { return(multiset); } if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } // If no timeout use default implementation if (timeout <= 0) { return(multiset.Product(other)); } // Otherwise Invoke using an Async call #if NET40 BaseMultiset productSet; if (Options.UsePLinqEvaluation) { if (multiset.Count >= other.Count) { productSet = new PartitionedMultiset(multiset.Count, other.Count); } else { productSet = new PartitionedMultiset(other.Count, multiset.Count); } } else { productSet = new Multiset(); } #else var productSet = new Multiset(); #endif var stop = new StopToken(); var t = (int)Math.Min(timeout, int.MaxValue); #if NET40 || NETSTANDARD1_4 || NETSTANDARD2_0 var productTask = Task.Factory.StartNew(() => GenerateProduct(multiset, other, productSet, stop)); if (!productTask.Wait(t)) { stop.ShouldStop = true; productTask.Wait(); } return(productSet); #else GenerateProductDelegate d = new GenerateProductDelegate(GenerateProduct); IAsyncResult r = d.BeginInvoke(multiset, other, productSet, stop, null, null); // Wait r.AsyncWaitHandle.WaitOne(t); if (!r.IsCompleted) { stop.ShouldStop = true; r.AsyncWaitHandle.WaitOne(); } return(productSet); #endif }
/// <summary> /// Joins this Multiset to another Multiset /// </summary> /// <param name="other">Other Multiset</param> /// <returns></returns> public override BaseMultiset Join(BaseMultiset other) { //If the Other is the Identity Multiset the result is this Multiset if (other is IdentityMultiset) return this; //If the Other is the Null Multiset the result is the Null Multiset if (other is NullMultiset) return other; //If the Other is Empty then the result is the Null Multiset if (other.IsEmpty) return new NullMultiset(); //Find the First Variable from this Multiset which is in both Multisets //If there is no Variable from this Multiset in the other Multiset then this //should be a Join operation instead of a LeftJoin List<String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) return this.Product(other); //Start building the Joined Set Multiset joinedSet = new Multiset(); foreach (Set x in this.Sets) { //For sets to be compatible for every joinable variable they must either have a null for the //variable in one of the sets or if they have values the values must be equal IEnumerable<Set> ys = other.Sets.Where(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); foreach (Set y in ys) { joinedSet.Add(new Set(x, y)); } } return joinedSet; }
/// <summary> /// Does a Left Join of this Multiset to another Multiset where the Join is predicated on the given Expression /// </summary> /// <param name="other">Other Multiset</param> /// <param name="expr">Expression</param> /// <returns></returns> public override BaseMultiset LeftJoin(BaseMultiset other, ISparqlExpression expr) { //If the Other is the Identity/Null Multiset the result is this Multiset if (other is IdentityMultiset) { return(this); } if (other is NullMultiset) { return(this); } if (other.IsEmpty) { return(this); } Multiset joinedSet = new Multiset(); LeviathanLeftJoinBinder binder = new LeviathanLeftJoinBinder(joinedSet); SparqlEvaluationContext subcontext = new SparqlEvaluationContext(binder); //Find the First Variable from this Multiset which is in both Multisets //If there is no Variable from this Multiset in the other Multiset then this //should be a Join operation instead of a LeftJoin List <String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { //Calculate a Product filtering as we go foreach (ISet x in this.Sets) { bool standalone = false; foreach (ISet y in other.Sets) { ISet z = x.Join(y); try { joinedSet.Add(z); if (!expr.EffectiveBooleanValue(subcontext, z.ID)) { joinedSet.Remove(z.ID); standalone = true; } } catch { joinedSet.Remove(z.ID); standalone = true; } } if (standalone) { joinedSet.Add(x); } } } else { foreach (ISet x in this.Sets) { IEnumerable <ISet> ys = other.Sets.Where(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); //IEnumerable<ISet> ys = other.Sets.Where(s => s.IsCompatibleWith(x, joinVars)); bool standalone = false; int i = 0; foreach (ISet y in ys) { i++; ISet z = x.Join(y); try { joinedSet.Add(z); if (!expr.EffectiveBooleanValue(subcontext, z.ID)) { joinedSet.Remove(z.ID); standalone = true; } } catch { joinedSet.Remove(z.ID); standalone = true; } } if (standalone || i == 0) { joinedSet.Add(x); } } } return(joinedSet); }
private BaseMultiset StreamingEvaluate(SparqlEvaluationContext context, int pattern, out bool halt) { halt = false; //Handle Empty BGPs if (pattern == 0 && this._triplePatterns.Count == 0) { context.OutputMultiset = new IdentityMultiset(); return(context.OutputMultiset); } BaseMultiset initialInput, localOutput, results; //Set up the Input and Output Multiset appropriately switch (pattern) { case 0: //Input is as given and Output is new empty multiset initialInput = context.InputMultiset; localOutput = new Multiset(); break; case 1: //Input becomes current Output and Output is new empty multiset initialInput = context.OutputMultiset; localOutput = new Multiset(); break; default: //Input is join of previous input and ouput and Output is new empty multiset if (context.InputMultiset.IsDisjointWith(context.OutputMultiset)) { //Disjoint so do a Product initialInput = context.InputMultiset.Product(context.OutputMultiset); } else { //Normal Join initialInput = context.InputMultiset.Join(context.OutputMultiset); } localOutput = new Multiset(); break; } context.InputMultiset = initialInput; context.OutputMultiset = localOutput; //Get the Triple Pattern we're evaluating ITriplePattern temp = this._triplePatterns[pattern]; int resultsFound = 0; if (temp.PatternType == TriplePatternType.Match) { //Find the first Triple which matches the Pattern IMatchTriplePattern tp = (IMatchTriplePattern)temp; foreach (Triple t in tp.GetTriples(context)) { //Remember to check for Timeout during lazy evaluation context.CheckTimeout(); if (tp.Accepts(context, t)) { resultsFound++; context.OutputMultiset.Add(tp.CreateResult(t)); //Recurse unless we're the last pattern if (pattern < this._triplePatterns.Count - 1) { results = this.StreamingEvaluate(context, pattern + 1, out halt); //If recursion leads to a halt then we halt and return immediately if (halt) { return(results); } //Otherwise we need to keep going here //So must reset our input and outputs before continuing context.InputMultiset = initialInput; context.OutputMultiset = new Multiset(); resultsFound--; } else { //If we're at the last pattern and we've found a match then we can halt halt = true; //Generate the final output and return it if (context.InputMultiset.IsDisjointWith(context.OutputMultiset)) { //Disjoint so do a Product context.OutputMultiset = context.InputMultiset.ProductWithTimeout(context.OutputMultiset, context.QueryTimeout - context.QueryTime); } else { //Normal Join context.OutputMultiset = context.InputMultiset.Join(context.OutputMultiset); } return(context.OutputMultiset); } } } } else if (temp.PatternType == TriplePatternType.Filter) { IFilterPattern fp = (IFilterPattern)temp; ISparqlFilter filter = fp.Filter; ISparqlExpression expr = filter.Expression; //Find the first result of those we've got so far that matches if (context.InputMultiset is IdentityMultiset || context.InputMultiset.IsEmpty) { try { //If the Input is the Identity Multiset then the Output is either //the Identity/Null Multiset depending on whether the Expression evaluates to true if (expr.Evaluate(context, 0).AsSafeBoolean()) { context.OutputMultiset = new IdentityMultiset(); } else { context.OutputMultiset = new NullMultiset(); } } catch { //If Expression fails to evaluate then result is NullMultiset context.OutputMultiset = new NullMultiset(); } } else { foreach (int id in context.InputMultiset.SetIDs) { //Remember to check for Timeout during lazy evaluation context.CheckTimeout(); try { if (expr.Evaluate(context, id).AsSafeBoolean()) { resultsFound++; context.OutputMultiset.Add(context.InputMultiset[id].Copy()); //Recurse unless we're the last pattern if (pattern < this._triplePatterns.Count - 1) { results = this.StreamingEvaluate(context, pattern + 1, out halt); //If recursion leads to a halt then we halt and return immediately if (halt) { return(results); } //Otherwise we need to keep going here //So must reset our input and outputs before continuing context.InputMultiset = initialInput; context.OutputMultiset = new Multiset(); resultsFound--; } else { //If we're at the last pattern and we've found a match then we can halt halt = true; //Generate the final output and return it if (context.InputMultiset.IsDisjointWith(context.OutputMultiset)) { //Disjoint so do a Product context.OutputMultiset = context.InputMultiset.ProductWithTimeout(context.OutputMultiset, context.RemainingTimeout); } else { //Normal Join context.OutputMultiset = context.InputMultiset.Join(context.OutputMultiset); } return(context.OutputMultiset); } } } catch { //Ignore expression evaluation errors } } } } //If we found no possibles we return the null multiset if (resultsFound == 0) { return(new NullMultiset()); } //We should never reach here so throw an error to that effect //The reason we'll never reach here is that this method should always return earlier throw new RdfQueryException("Unexpected control flow in evaluating a Streamed BGP for an ASK query"); }
/// <summary> /// Evaluates a setp of the Path /// </summary> /// <param name="context">Context</param> /// <param name="path">Paths</param> /// <param name="reverse">Whether to evaluate Paths in reverse</param> /// <returns></returns> protected List<INode> EvaluateStep(SparqlEvaluationContext context, List<INode> path, bool reverse) { if (this.Path is Property) { HashSet<INode> nodes = new HashSet<INode>(); INode predicate = ((Property)this.Path).Predicate; IEnumerable<Triple> ts = (reverse ? context.Data.GetTriplesWithPredicateObject(predicate, path[path.Count - 1]) : context.Data.GetTriplesWithSubjectPredicate(path[path.Count - 1], predicate)); foreach (Triple t in ts) { if (reverse) { if (!path.Contains(t.Subject)) { nodes.Add(t.Subject); } } else { if (!path.Contains(t.Object)) { nodes.Add(t.Object); } } } return nodes.ToList(); } else { List<INode> nodes = new List<INode>(); BaseMultiset initialInput = context.InputMultiset; Multiset currInput = new Multiset(); VariablePattern x = new VariablePattern("?x"); VariablePattern y = new VariablePattern("?y"); Set temp = new Set(); if (reverse) { temp.Add("y", path[path.Count - 1]); } else { temp.Add("x", path[path.Count - 1]); } currInput.Add(temp); context.InputMultiset = currInput; Bgp bgp = new Bgp(new PropertyPathPattern(x, this.Path, y)); BaseMultiset results = context.Evaluate(bgp);//bgp.Evaluate(context); context.InputMultiset = initialInput; if (!results.IsEmpty) { foreach (Set s in results.Sets) { if (reverse) { if (s["x"] != null) { if (!path.Contains(s["x"])) { nodes.Add(s["x"]); } } } else { if (s["y"] != null) { if (!path.Contains(s["y"])) { nodes.Add(s["y"]); } } } } } return nodes; } }
/// <summary> /// Processes an INSERT command /// </summary> /// <param name="cmd">Insert Command</param> /// <remarks> /// <para> /// <strong>Note:</strong> The underlying manager must implement the <see cref="IQueryableGenericIOManager">IQueryableGenericIOManager</see> interface in order for INSERT commands to be processed /// </para> /// </remarks> public void ProcessInsertCommand(InsertCommand cmd) { if (this._manager is IUpdateableGenericIOManager) { ((IUpdateableGenericIOManager)this._manager).Update(cmd.ToString()); } else { if (this._manager is IQueryableGenericIOManager) { //Check IO Behaviour //For a insert we either need the ability to Update Add Triples or to Overwrite Graphs //Firstly check behaviour persuant to default graph if applicable if (cmd.InsertPattern.TriplePatterns.OfType<IConstructTriplePattern>().Any()) { //Must support notion of default graph if ((this._manager.IOBehaviour & IOBehaviour.HasDefaultGraph) == 0) throw new SparqlUpdateException("The underlying store does not support the notion of an explicit unnamed Default Graph required to process this command"); //Must allow either OverwriteDefault or CanUpdateAddTriples if ((this._manager.IOBehaviour & IOBehaviour.CanUpdateAddTriples) == 0 && (this._manager.IOBehaviour & IOBehaviour.OverwriteDefault) == 0) throw new SparqlUpdateException("The underlying store does not support the required IO Behaviour to implement this command"); } //Then check behaviour persuant to named graphs if applicable if (cmd.InsertPattern.HasChildGraphPatterns) { //Must support named graphs if ((this._manager.IOBehaviour & IOBehaviour.HasNamedGraphs) == 0) throw new SparqlUpdateException("The underlying store does not support the notion of named graphs required to process this command"); //Must allow either CanUpdateAddTriples or OverwriteNamed if ((this._manager.IOBehaviour & IOBehaviour.CanUpdateAddTriples) == 0 && (this._manager.IOBehaviour & IOBehaviour.OverwriteNamed) == 0) throw new SparqlUpdateException("The underlying store does not support the required IO Behaviour to implement this command"); } //First build and make the query to get a Result Set String queryText = "SELECT * WHERE " + cmd.WherePattern.ToString(); SparqlQueryParser parser = new SparqlQueryParser(); SparqlQuery query = parser.ParseFromString(queryText); if (cmd.GraphUri != null && !cmd.UsingUris.Any()) query.AddDefaultGraph(cmd.GraphUri); foreach (Uri u in cmd.UsingUris) { query.AddDefaultGraph(u); } foreach (Uri u in cmd.UsingNamedUris) { query.AddNamedGraph(u); } Object results = ((IQueryableGenericIOManager)this._manager).Query(query.ToString()); if (results is SparqlResultSet) { //Now need to transform the Result Set back to a Multiset Multiset mset = new Multiset((SparqlResultSet)results); //Generate the Triples for each Solution List<Triple> insertedTriples = new List<Triple>(); Dictionary<String, List<Triple>> insertedGraphTriples = new Dictionary<string, List<Triple>>(); foreach (ISet s in mset.Sets) { List<Triple> tempInsertedTriples = new List<Triple>(); try { ConstructContext context = new ConstructContext(null, s, true); foreach (IConstructTriplePattern p in cmd.InsertPattern.TriplePatterns.OfType<IConstructTriplePattern>()) { try { tempInsertedTriples.Add(p.Construct(context)); } catch (RdfQueryException) { //If we get an error here then it means we couldn't construct a specific //triple so we continue anyway } } insertedTriples.AddRange(tempInsertedTriples); } catch (RdfQueryException) { //If we get an error here this means we couldn't construct for this solution so the //solution is ignore for this graph } //Triples from GRAPH clauses foreach (GraphPattern gp in cmd.InsertPattern.ChildGraphPatterns) { tempInsertedTriples.Clear(); try { String graphUri; switch (gp.GraphSpecifier.TokenType) { case Token.URI: graphUri = gp.GraphSpecifier.Value; break; case Token.VARIABLE: if (s.ContainsVariable(gp.GraphSpecifier.Value)) { INode temp = s[gp.GraphSpecifier.Value.Substring(1)]; if (temp == null) { //If the Variable is not bound then skip continue; } else if (temp.NodeType == NodeType.Uri) { graphUri = temp.ToSafeString(); } else { //If the Variable is not bound to a URI then skip continue; } } else { //If the Variable is not bound for this solution then skip continue; } break; default: //Any other Graph Specifier we have to ignore this solution continue; } if (!insertedGraphTriples.ContainsKey(graphUri)) insertedGraphTriples.Add(graphUri, new List<Triple>()); ConstructContext context = new ConstructContext(null, s, true); foreach (IConstructTriplePattern p in gp.TriplePatterns.OfType<IConstructTriplePattern>()) { try { tempInsertedTriples.Add(p.Construct(context)); } catch (RdfQueryException) { //If we get an error here then it means we couldn't construct a specific //triple so we continue anyway } } insertedGraphTriples[graphUri].AddRange(tempInsertedTriples); } catch (RdfQueryException) { //If we get an error here this means we couldn't construct for this solution so the //solution is ignore for this graph } } } //Now decide how to apply the update if (this._manager.UpdateSupported) { this._manager.UpdateGraph(cmd.GraphUri, insertedTriples, Enumerable.Empty<Triple>()); foreach (KeyValuePair<String, List<Triple>> graphInsertion in insertedGraphTriples) { this._manager.UpdateGraph(graphInsertion.Key, graphInsertion.Value, Enumerable.Empty<Triple>()); } } else { Graph g = new Graph(); this._manager.LoadGraph(g, cmd.GraphUri); g.Assert(insertedTriples); this._manager.SaveGraph(g); foreach (KeyValuePair<String, List<Triple>> graphInsertion in insertedGraphTriples) { g = new Graph(); this._manager.LoadGraph(g, graphInsertion.Key); g.Assert(graphInsertion.Value); this._manager.SaveGraph(g); } } } else { throw new SparqlUpdateException("Cannot evaluate an INSERT Command as the underlying Store failed to answer the query for the WHERE portion of the command as expected"); } } else { throw new NotSupportedException("INSERT commands are not supported by this Update Processor as the manager for the underlying Store does not provide Query capabilities which are necessary to process this command"); } } }
/// <summary> /// Does an Exists Join of this Multiset to another Multiset where the Join is predicated on the existence/non-existence of a joinable solution on the RHS /// </summary> /// <param name="other">Other Multiset</param> /// <param name="mustExist">Whether a solution must exist in the Other Multiset for the join to be made</param> /// <returns></returns> public override BaseMultiset ExistsJoin(BaseMultiset other, bool mustExist) { //For EXISTS and NOT EXISTS if the other is the Identity then it has no effect if (other is IdentityMultiset) { return(this); } if (mustExist) { //If an EXISTS then Null/Empty Other results in Null if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } } else { //If a NOT EXISTS then Null/Empty results in this if (other is NullMultiset) { return(this); } if (other.IsEmpty) { return(this); } } //Find the Variables that are to be used for Joining List <String> joinVars = this._variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { //All Disjoint Solutions are compatible if (mustExist) { //If an EXISTS and disjoint then result is this return(this); } else { //If a NOT EXISTS and disjoint then result is null return(new NullMultiset()); } } //Start building the Joined Set Multiset joinedSet = new Multiset(); foreach (ISet x in this.Sets) { //New ExistsJoin() logic based on the improved Join() logic bool exists = other.Sets.Any(s => joinVars.All(v => x[v] == null || s[v] == null || x[v].Equals(s[v]))); //bool exists = other.Sets.Any(s => s.IsCompatibleWith(x, joinVars)); if (exists) { //If there are compatible sets and this is an EXIST then preserve the solution if (mustExist) { joinedSet.Add(x); } } else { //If there are no compatible sets and this is a NOT EXISTS then preserve the solution if (!mustExist) { joinedSet.Add(x); } } } return(joinedSet); }
/// <summary> /// Evaluates a setp of the Path /// </summary> /// <param name="context">Context</param> /// <param name="path">Paths</param> /// <param name="reverse">Whether to evaluate Paths in reverse</param> /// <returns></returns> protected List <INode> EvaluateStep(SparqlEvaluationContext context, List <INode> path, bool reverse) { if (this.Path is Property) { HashSet <INode> nodes = new HashSet <INode>(); INode predicate = ((Property)this.Path).Predicate; IEnumerable <Triple> ts = (reverse ? context.Data.GetTriplesWithPredicateObject(predicate, path[path.Count - 1]) : context.Data.GetTriplesWithSubjectPredicate(path[path.Count - 1], predicate)); foreach (Triple t in ts) { if (reverse) { if (!path.Contains(t.Subject)) { nodes.Add(t.Subject); } } else { if (!path.Contains(t.Object)) { nodes.Add(t.Object); } } } return(nodes.ToList()); } else { HashSet <INode> nodes = new HashSet <INode>(); BaseMultiset initialInput = context.InputMultiset; Multiset currInput = new Multiset(); VariablePattern x = new VariablePattern("?x"); VariablePattern y = new VariablePattern("?y"); Set temp = new Set(); if (reverse) { temp.Add("y", path[path.Count - 1]); } else { temp.Add("x", path[path.Count - 1]); } currInput.Add(temp); context.InputMultiset = currInput; Bgp bgp = new Bgp(new PropertyPathPattern(x, this.Path, y)); BaseMultiset results = context.Evaluate(bgp); //bgp.Evaluate(context); context.InputMultiset = initialInput; if (!results.IsEmpty) { foreach (ISet s in results.Sets) { if (reverse) { if (s["x"] != null) { if (!path.Contains(s["x"])) { nodes.Add(s["x"]); } } } else { if (s["y"] != null) { if (!path.Contains(s["y"])) { nodes.Add(s["y"]); } } } } } return(nodes.ToList()); } }
private BaseMultiset StreamingEvaluate(SparqlEvaluationContext context, int pattern, out bool halt) { halt = false; //Handle Empty BGPs if (pattern == 0 && this._triplePatterns.Count == 0) { context.OutputMultiset = new IdentityMultiset(); return context.OutputMultiset; } BaseMultiset initialInput, localOutput, results = null; //Determine whether the Pattern modifies the existing Input rather than joining to it bool modifies = (this._triplePatterns[pattern] is FilterPattern); bool extended = (pattern > 0 && this._triplePatterns[pattern-1] is BindPattern); bool modified = (pattern > 0 && this._triplePatterns[pattern-1] is FilterPattern); //Set up the Input and Output Multiset appropriately switch (pattern) { case 0: //Input is as given and Output is new empty multiset if (!modifies) { initialInput = context.InputMultiset; } else { //If the Pattern will modify the Input and is the first thing in the BGP then it actually modifies a new empty input //This takes care of FILTERs being out of scope initialInput = new Multiset(); } localOutput = new Multiset(); break; case 1: //Input becomes current Output and Output is new empty multiset initialInput = context.OutputMultiset; localOutput = new Multiset(); break; default: if (!extended && !modified) { //Input is join of previous input and output and Output is new empty multiset if (context.InputMultiset.IsDisjointWith(context.OutputMultiset)) { //Disjoint so do a Product initialInput = context.InputMultiset.ProductWithTimeout(context.OutputMultiset, context.RemainingTimeout); } else { //Normal Join initialInput = context.InputMultiset.Join(context.OutputMultiset); } } else { initialInput = context.OutputMultiset; } localOutput = new Multiset(); break; } context.InputMultiset = initialInput; context.OutputMultiset = localOutput; //Get the Triple Pattern we're evaluating ITriplePattern temp = this._triplePatterns[pattern]; int resultsFound = 0; int prevResults = -1; if (temp is TriplePattern) { //Find the first Triple which matches the Pattern TriplePattern tp = (TriplePattern)temp; IEnumerable<Triple> ts = tp.GetTriples(context); //In the case that we're lazily evaluating an optimisable ORDER BY then //we need to apply OrderBy()'s to our enumeration //This only applies to the 1st pattern if (pattern == 0) { if (context.Query != null) { if (context.Query.OrderBy != null && context.Query.IsOptimisableOrderBy) { IComparer<Triple> comparer = context.Query.OrderBy.GetComparer(tp); if (comparer != null) { ts = ts.OrderBy(t => t, comparer); } else { //Can't get a comparer so can't optimise this._requiredResults = -1; } } } } foreach (Triple t in ts) { //Remember to check for Timeouts during Lazy Evaluation context.CheckTimeout(); if (tp.Accepts(context, t)) { resultsFound++; if (tp.IndexType == TripleIndexType.NoVariables) { localOutput = new IdentityMultiset(); context.OutputMultiset = localOutput; } else { context.OutputMultiset.Add(tp.CreateResult(t)); } //Recurse unless we're the last pattern if (pattern < this._triplePatterns.Count - 1) { results = this.StreamingEvaluate(context, pattern + 1, out halt); //If recursion leads to a halt then we halt and return immediately if (halt && results.Count >= this._requiredResults && this._requiredResults != -1) { return results; } else if (halt) { if (results.Count == 0) { //If recursing leads to no results then eliminate all outputs //Also reset to prevResults to -1 resultsFound = 0; localOutput = new Multiset(); prevResults = -1; } else if (prevResults > -1) { if (results.Count == prevResults) { //If the amount of results found hasn't increased then this match does not //generate any further solutions further down the recursion so we can eliminate //this from the results localOutput.Remove(localOutput.SetIDs.Max()); } } prevResults = results.Count; //If we're supposed to halt but not reached the number of required results then continue context.InputMultiset = initialInput; context.OutputMultiset = localOutput; } else { //Otherwise we need to keep going here //So must reset our input and outputs before continuing context.InputMultiset = initialInput; context.OutputMultiset = new Multiset(); resultsFound--; } } else { //If we're at the last pattern and we've found a match then we can halt halt = true; //Generate the final output and return it if (context.InputMultiset.IsDisjointWith(context.OutputMultiset)) { //Disjoint so do a Product results = context.InputMultiset.ProductWithTimeout(context.OutputMultiset, context.RemainingTimeout); } else { //Normal Join results = context.InputMultiset.Join(context.OutputMultiset); } //If not reached required number of results continue if (results.Count >= this._requiredResults && this._requiredResults != -1) { context.OutputMultiset = results; return context.OutputMultiset; } } } } } else if (temp is FilterPattern) { FilterPattern filter = (FilterPattern)temp; ISparqlExpression filterExpr = filter.Filter.Expression; if (filter.Variables.IsDisjoint(context.InputMultiset.Variables)) { //Remember to check for Timeouts during Lazy Evaluation context.CheckTimeout(); //Filter is Disjoint so determine whether it has any affect or not if (filter.Variables.Any()) { //Has Variables but disjoint from input => not in scope so gets ignored //Do we recurse or not? if (pattern < this._triplePatterns.Count - 1) { //Recurse and return results = this.StreamingEvaluate(context, pattern + 1, out halt); return results; } else { //We don't affect the input in any way so just return it return context.InputMultiset; } } else { //No Variables so have to evaluate it to see if it gives true otherwise try { if (filterExpr.Evaluate(context, 0).AsSafeBoolean()) { if (pattern < this._triplePatterns.Count - 1) { //Recurse and return results = this.StreamingEvaluate(context, pattern + 1, out halt); return results; } else { //Last Pattern and we evaluate to true so can return the input as-is halt = true; return context.InputMultiset; } } } catch (RdfQueryException) { //Evaluates to false so eliminates all solutions (use an empty Multiset) return new Multiset(); } } } else { //Remember to check for Timeouts during Lazy Evaluation context.CheckTimeout(); //Test each solution found so far against the Filter and eliminate those that evalute to false/error foreach (int id in context.InputMultiset.SetIDs.ToList()) { try { if (filterExpr.Evaluate(context, id).AsSafeBoolean()) { //If evaluates to true then add to output context.OutputMultiset.Add(context.InputMultiset[id].Copy()); } } catch (RdfQueryException) { //Error means we ignore the solution } } //Remember to check for Timeouts during Lazy Evaluation context.CheckTimeout(); //Decide whether to recurse or not resultsFound = context.OutputMultiset.Count; if (pattern < this._triplePatterns.Count - 1) { //Recurse then return //We can never decide whether to recurse again at this point as we are not capable of deciding //which solutions should be dumped (that is the job of an earlier pattern in the BGP) results = this.StreamingEvaluate(context, pattern + 1, out halt); return results; } else { halt = true; //However many results we need we'll halt - previous patterns can call us again if they find more potential solutions //for us to filter return context.OutputMultiset; } } } else if (temp is BindPattern) { BindPattern bind = (BindPattern)temp; ISparqlExpression bindExpr = bind.AssignExpression; String bindVar = bind.VariableName; if (context.InputMultiset.ContainsVariable(bindVar)) { throw new RdfQueryException("Cannot use a BIND assigment to BIND to a variable that has previously been used in the Query"); } else { //Remember to check for Timeouts during Lazy Evaluation context.CheckTimeout(); //Compute the Binding for every value context.OutputMultiset.AddVariable(bindVar); foreach (ISet s in context.InputMultiset.Sets) { ISet x = s.Copy(); try { INode val = bindExpr.Evaluate(context, s.ID); x.Add(bindVar, val); } catch (RdfQueryException) { //Equivalent to no assignment but the solution is preserved } context.OutputMultiset.Add(x.Copy()); } //Remember to check for Timeouts during Lazy Evaluation context.CheckTimeout(); //Decide whether to recurse or not resultsFound = context.OutputMultiset.Count; if (pattern < this._triplePatterns.Count - 1) { //Recurse then return results = this.StreamingEvaluate(context, pattern + 1, out halt); return results; } else { halt = true; //However many results we need we'll halt - previous patterns can call us again if they find more potential solutions //for us to extend return context.OutputMultiset; } } } else { throw new RdfQueryException("Encountered a " + temp.GetType().FullName + " which is not a lazily evaluable Pattern"); } //If we found no possibles we return the null multiset if (resultsFound == 0) { return new NullMultiset(); } else { //Generate the final output and return it if (!modifies) { if (context.InputMultiset.IsDisjointWith(context.OutputMultiset)) { //Disjoint so do a Product results = context.InputMultiset.ProductWithTimeout(context.OutputMultiset, context.RemainingTimeout); } else { //Normal Join results = context.InputMultiset.Join(context.OutputMultiset); } context.OutputMultiset = results; } return context.OutputMultiset; } }