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