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); }
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 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> /// 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> /// 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> /// 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> /// 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> /// 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> /// 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); }
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 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> /// 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; }
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 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 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 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 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> /// 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(); //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> /// 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> /// 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 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> /// 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> /// 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 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> /// 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 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()); } }
/// <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; }