/// <summary> /// Evaluates a Zero or More Path /// </summary> /// <param name="context">Evaluation Context</param> /// <returns></returns> public override BaseMultiset Evaluate(SparqlEvaluationContext context) { List <List <INode> > paths = new List <List <INode> >(); BaseMultiset initialInput = context.InputMultiset; int step = 0, prevCount = 0, skipCount = 0; String subjVar = PathStart.VariableName; String objVar = PathEnd.VariableName; bool bothTerms = (subjVar == null && objVar == null); bool reverse = false; if (subjVar == null || (context.InputMultiset.ContainsVariable(subjVar))) { // Work Forwards from the Starting Term or Bound Variable // OR if there is no Ending Term or Bound Variable work forwards regardless if (subjVar == null) { paths.Add(((NodeMatchPattern)PathStart).Node.AsEnumerable().ToList()); } else if (context.InputMultiset.ContainsVariable(subjVar)) { paths.AddRange((from s in context.InputMultiset.Sets where s[subjVar] != null select s[subjVar]).Distinct().Select(n => n.AsEnumerable().ToList())); } } else if (objVar == null || (context.InputMultiset.ContainsVariable(objVar))) { // Work Backwards from Ending Term or Bound Variable if (objVar == null) { paths.Add(((NodeMatchPattern)PathEnd).Node.AsEnumerable().ToList()); } else { paths.AddRange((from s in context.InputMultiset.Sets where s[objVar] != null select s[objVar]).Distinct().Select(n => n.AsEnumerable().ToList())); } reverse = true; } if (paths.Count == 0) { GetPathStarts(context, paths, reverse); } // Traverse the Paths do { prevCount = paths.Count; foreach (List <INode> path in paths.Skip(skipCount).ToList()) { foreach (INode nextStep in EvaluateStep(context, path, reverse)) { List <INode> newPath = new List <INode>(path); newPath.Add(nextStep); paths.Add(newPath); } } // Update Counts // skipCount is used to indicate the paths which we will ignore for the purposes of // trying to further extend since we've already done them once step++; if (paths.Count == 0) { break; } skipCount = prevCount; // Can short circuit evaluation here if both are terms and any path is acceptable if (bothTerms) { bool exit = false; foreach (List <INode> path in paths) { if (reverse) { if (PathEnd.Accepts(context, path[0]) && PathStart.Accepts(context, path[path.Count - 1])) { exit = true; break; } } else { if (PathStart.Accepts(context, path[0]) && PathEnd.Accepts(context, path[path.Count - 1])) { exit = true; break; } } } if (exit) { break; } } } while (paths.Count > prevCount || (step == 1 && paths.Count == prevCount)); if (paths.Count == 0) { // If all path starts lead nowhere then we get the Null Multiset as a result context.OutputMultiset = new NullMultiset(); } else { context.OutputMultiset = new Multiset(); // Evaluate the Paths to check that are acceptable HashSet <ISet> returnedPaths = new HashSet <ISet>(); foreach (List <INode> path in paths) { if (reverse) { if (PathEnd.Accepts(context, path[0]) && PathStart.Accepts(context, path[path.Count - 1])) { Set s = new Set(); if (!bothTerms) { if (subjVar != null) { s.Add(subjVar, path[path.Count - 1]); } if (objVar != null) { s.Add(objVar, path[0]); } } // Make sure to check for uniqueness if (returnedPaths.Contains(s)) { continue; } context.OutputMultiset.Add(s); returnedPaths.Add(s); // If both are terms can short circuit evaluation here // It is sufficient just to determine that there is one path possible if (bothTerms) { break; } } } else { if (PathStart.Accepts(context, path[0]) && PathEnd.Accepts(context, path[path.Count - 1])) { Set s = new Set(); if (!bothTerms) { if (subjVar != null) { s.Add(subjVar, path[0]); } if (objVar != null) { s.Add(objVar, path[path.Count - 1]); } } // Make sure to check for uniqueness if (returnedPaths.Contains(s)) { continue; } context.OutputMultiset.Add(s); returnedPaths.Add(s); // If both are terms can short circuit evaluation here // It is sufficient just to determine that there is one path possible if (bothTerms) { break; } } } } // Now add the zero length paths into IEnumerable <INode> nodes; if (subjVar != null) { if (objVar != null) { nodes = (from s in context.OutputMultiset.Sets where s[subjVar] != null select s[subjVar]).Concat(from s in context.OutputMultiset.Sets where s[objVar] != null select s[objVar]).Distinct(); } else { nodes = (from s in context.OutputMultiset.Sets where s[subjVar] != null select s[subjVar]).Distinct(); } } else if (objVar != null) { nodes = (from s in context.OutputMultiset.Sets where s[objVar] != null select s[objVar]).Distinct(); } else { nodes = Enumerable.Empty <INode>(); } if (bothTerms) { // If both were terms transform to an Identity/Null Multiset as appropriate if (context.OutputMultiset.IsEmpty) { context.OutputMultiset = new NullMultiset(); } else { context.OutputMultiset = new IdentityMultiset(); } } // Then union in the zero length paths context.InputMultiset = initialInput; ZeroLengthPath zeroPath = new ZeroLengthPath(PathStart, PathEnd, Path); BaseMultiset currResults = context.OutputMultiset; context.OutputMultiset = new Multiset(); BaseMultiset results = context.Evaluate(zeroPath);//zeroPath.Evaluate(context); context.OutputMultiset = currResults; foreach (ISet s in results.Sets) { if (!context.OutputMultiset.Sets.Contains(s)) { context.OutputMultiset.Add(s.Copy()); } } } context.InputMultiset = initialInput; return(context.OutputMultiset); }
/// <summary> /// Applies the Filter over the results of evaluating the inner pattern /// </summary> /// <param name="context">Evaluation Context</param> /// <returns></returns> public sealed override BaseMultiset Evaluate(SparqlEvaluationContext context) { INode term = this._term.Value(null, 0); //First take appropriate pre-filtering actions if (context.InputMultiset is IdentityMultiset) { //If the Input is Identity switch the input to be a Multiset containing a single Set //where the variable is bound to the term context.InputMultiset = new Multiset(); Set s = new Set(); s.Add(this.RestrictionVariable, term); context.InputMultiset.Add(s); } else if (context.InputMultiset is NullMultiset) { //If Input is Null then output is Null context.OutputMultiset = context.InputMultiset; return(context.OutputMultiset); } else { if (context.InputMultiset.ContainsVariable(this.RestrictionVariable)) { //If the Input Multiset contains the variable then pre-filter foreach (int id in context.InputMultiset.SetIDs.ToList()) { ISet x = context.InputMultiset[id]; try { if (x.ContainsVariable(this.RestrictionVariable)) { //If does exist check it has appropriate value and if not remove it if (!term.Equals(x[this.RestrictionVariable])) { context.InputMultiset.Remove(id); } } else { //If doesn't exist for this set then bind it to the term x.Add(this.RestrictionVariable, term); } } catch (RdfQueryException) { context.InputMultiset.Remove(id); } } } else { //If it doesn't contain the variable then bind for each existing set foreach (ISet x in context.InputMultiset.Sets) { x.Add(this.RestrictionVariable, term); } } } //Then evaluate the inner algebra BaseMultiset results = context.Evaluate(this.InnerAlgebra); if (results is NullMultiset || results is IdentityMultiset) { return(results); } //Filter the results to ensure that the variable is indeed bound to the term foreach (int id in results.SetIDs.ToList()) { ISet x = results[id]; try { if (!term.Equals(x[this.RestrictionVariable])) { results.Remove(id); } } catch (RdfQueryException) { results.Remove(id); } } if (results.Count > 0) { context.OutputMultiset = results; } else { context.OutputMultiset = new NullMultiset(); } 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 (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/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 '" + 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) { 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(this._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 (this._graphSpecifier.TokenType == Token.VARIABLE) { // Include graph variable if not yet bound INode currGraph = new UriNode(null, currGraphUri); Set s = new Set(); s.Add(this._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 (this._graphSpecifier.TokenType == Token.VARIABLE) { String gvar = this._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 Service Clause by generating instance(s) of <see cref="SparqlRemoteEndpoint">SparqlRemoteEndpoint</see> as required and issuing the query to the remote endpoint(s). /// </summary> /// <param name="context">Evaluation Context.</param> /// <returns></returns> public BaseMultiset Evaluate(SparqlEvaluationContext context) { bool bypassSilent = false; try { SparqlRemoteEndpoint endpoint; Uri endpointUri; String baseUri = (context.Query.BaseUri == null) ? String.Empty : context.Query.BaseUri.AbsoluteUri; SparqlParameterizedString sparqlQuery = new SparqlParameterizedString("SELECT * WHERE "); String pattern = _pattern.ToString(); pattern = pattern.Substring(pattern.IndexOf('{')); sparqlQuery.CommandText += pattern; // Pass through LIMIT and OFFSET to the remote service if (context.Query.Limit >= 0) { // Calculate a LIMIT which is the LIMIT plus the OFFSET // We'll apply OFFSET locally so don't pass that through explicitly int limit = context.Query.Limit; if (context.Query.Offset > 0) { limit += context.Query.Offset; } sparqlQuery.CommandText += " LIMIT " + limit; } // Select which service to use if (_endpointSpecifier.TokenType == Token.URI) { endpointUri = UriFactory.Create(Tools.ResolveUri(_endpointSpecifier.Value, baseUri)); endpoint = new SparqlRemoteEndpoint(endpointUri); } else if (_endpointSpecifier.TokenType == Token.VARIABLE) { // Get all the URIs that are bound to this Variable in the Input String var = _endpointSpecifier.Value.Substring(1); if (!context.InputMultiset.ContainsVariable(var)) { throw new RdfQueryException("Cannot evaluate a SERVICE clause which uses a Variable as the Service specifier when the Variable is unbound"); } List <IUriNode> services = new List <IUriNode>(); foreach (ISet s in context.InputMultiset.Sets) { if (s.ContainsVariable(var)) { if (s[var].NodeType == NodeType.Uri) { services.Add((IUriNode)s[var]); } } } services = services.Distinct().ToList(); // Now generate a Federated Remote Endpoint List <SparqlRemoteEndpoint> serviceEndpoints = new List <SparqlRemoteEndpoint>(); services.ForEach(u => serviceEndpoints.Add(new SparqlRemoteEndpoint(u.Uri))); endpoint = new FederatedSparqlRemoteEndpoint(serviceEndpoints); } else { // Note that we must bypass the SILENT operator in this case as this is not an evaluation failure // but a query syntax error bypassSilent = true; throw new RdfQueryException("SERVICE Specifier must be a URI/Variable Token but a " + _endpointSpecifier.GetType().ToString() + " Token was provided"); } // Where possible do substitution and execution to get accurate and correct SERVICE results context.OutputMultiset = new Multiset(); List <String> existingVars = (from v in _pattern.Variables where context.InputMultiset.ContainsVariable(v) select v).ToList(); if (existingVars.Any() || context.Query.Bindings != null) { // Pre-bound variables/BINDINGS clause so do substitution and execution // Build the set of possible bindings HashSet <ISet> bindings = new HashSet <ISet>(); if (context.Query.Bindings != null && !_pattern.Variables.IsDisjoint(context.Query.Bindings.Variables)) { // Possible Bindings comes from BINDINGS clause // In this case each possibility is a distinct binding tuple defined in the BINDINGS clause foreach (BindingTuple tuple in context.Query.Bindings.Tuples) { bindings.Add(new Set(tuple)); } } else { // Possible Bindings get built from current input (if there was a BINDINGS clause the variables it defines are not in this SERVICE clause) // In this case each possibility only contains Variables bound so far foreach (ISet s in context.InputMultiset.Sets) { Set t = new Set(); foreach (String var in existingVars) { t.Add(var, s[var]); } bindings.Add(t); } } // Execute the Query for every possible Binding and build up our Output Multiset from all the results foreach (ISet s in bindings) { // Q: Should we continue processing here if and when we hit an error? foreach (String var in s.Variables) { sparqlQuery.SetVariable(var, s[var]); } SparqlResultSet results = endpoint.QueryWithResultSet(sparqlQuery.ToString()); context.CheckTimeout(); foreach (SparqlResult r in results) { Set t = new Set(r); foreach (String var in s.Variables) { t.Add(var, s[var]); } context.OutputMultiset.Add(t); } } return(context.OutputMultiset); } else { // No pre-bound variables/BINDINGS clause so just execute the query // Try and get a Result Set from the Service SparqlResultSet results = endpoint.QueryWithResultSet(sparqlQuery.ToString()); // Transform this Result Set back into a Multiset foreach (SparqlResult r in results.Results) { context.OutputMultiset.Add(new Set(r)); } return(context.OutputMultiset); } } catch (Exception ex) { if (_silent && !bypassSilent) { // If Evaluation Errors are SILENT is specified then a Multiset containing a single set with all values unbound is returned // Unless some of the SPARQL queries did return results in which we just return the results we did obtain if (context.OutputMultiset.IsEmpty) { Set s = new Set(); foreach (String var in _pattern.Variables.Distinct()) { s.Add(var, null); } context.OutputMultiset.Add(s); } return(context.OutputMultiset); } else { throw new RdfQueryException("Query execution failed because evaluating a SERVICE clause failed - this may be due to an error with the remote service", ex); } } }
/// <summary> /// Evaluates the Negated Property Set. /// </summary> /// <param name="context">SPARQL Evaluation Context.</param> /// <returns></returns> public BaseMultiset Evaluate(SparqlEvaluationContext context) { IEnumerable <Triple> ts; String subjVar = _start.VariableName; String objVar = _end.VariableName; if (subjVar != null && context.InputMultiset.ContainsVariable(subjVar)) { if (objVar != null && context.InputMultiset.ContainsVariable(objVar)) { ts = (from s in context.InputMultiset.Sets where s[subjVar] != null && s[objVar] != null from t in context.Data.GetTriplesWithSubjectObject(s[subjVar], s[objVar]) select t); } else { ts = (from s in context.InputMultiset.Sets where s[subjVar] != null from t in context.Data.GetTriplesWithSubject(s[subjVar]) select t); } } else if (objVar != null && context.InputMultiset.ContainsVariable(objVar)) { ts = (from s in context.InputMultiset.Sets where s[objVar] != null from t in context.Data.GetTriplesWithObject(s[objVar]) select t); } else { ts = context.Data.Triples; } context.OutputMultiset = new Multiset(); // Q: Should this not go at the start of evaluation? if (_inverse) { String temp = objVar; objVar = subjVar; subjVar = temp; } foreach (Triple t in ts) { if (!_properties.Contains(t.Predicate)) { Set s = new Set(); if (subjVar != null) { s.Add(subjVar, t.Subject); } if (objVar != null) { s.Add(objVar, t.Object); } context.OutputMultiset.Add(s); } } if (subjVar == null && objVar == null) { if (context.OutputMultiset.Count == 0) { context.OutputMultiset = new NullMultiset(); } else { context.OutputMultiset = new IdentityMultiset(); } } return(context.OutputMultiset); }
/// <summary> /// Evaluates a Zero Length Path. /// </summary> /// <param name="context">Evaluation Context.</param> /// <returns></returns> public override BaseMultiset Evaluate(SparqlEvaluationContext context) { if (AreBothTerms()) { if (AreSameTerms()) { return(new IdentityMultiset()); } else { return(new NullMultiset()); } } String subjVar = PathStart.VariableName; String objVar = PathEnd.VariableName; context.OutputMultiset = new Multiset(); // Determine the Triples to which this applies if (subjVar != null) { // Subject is a Variable if (context.InputMultiset.ContainsVariable(subjVar)) { // Subject is Bound if (objVar != null) { // Object is a Variable if (context.InputMultiset.ContainsVariable(objVar)) { // Both Subject and Object are Bound foreach (ISet s in context.InputMultiset.Sets.Where(x => x[subjVar] != null && x[objVar] != null && PathStart.Accepts(context, x[subjVar]) && PathEnd.Accepts(context, x[objVar]))) { ISet x = new Set(); x.Add(subjVar, x[subjVar]); context.OutputMultiset.Add(x); x = new Set(); x.Add(objVar, x[objVar]); context.OutputMultiset.Add(x); } } else { // Subject is bound but Object is Unbound foreach (ISet s in context.InputMultiset.Sets.Where(x => x[subjVar] != null && PathStart.Accepts(context, x[subjVar]))) { ISet x = s.Copy(); x.Add(objVar, x[subjVar]); context.OutputMultiset.Add(x); } } } else { // Object is a Term // Preseve sets where the Object Term is equal to the currently bound Subject INode objTerm = ((NodeMatchPattern)PathEnd).Node; foreach (ISet s in context.InputMultiset.Sets) { INode temp = s[subjVar]; if (temp != null && temp.Equals(objTerm)) { context.OutputMultiset.Add(s.Copy()); } } } } else { // Subject is Unbound if (objVar != null) { // Object is a Variable if (context.InputMultiset.ContainsVariable(objVar)) { // Object is Bound but Subject is unbound foreach (ISet s in context.InputMultiset.Sets.Where(x => x[objVar] != null && PathEnd.Accepts(context, x[objVar]))) { ISet x = s.Copy(); x.Add(subjVar, x[objVar]); context.OutputMultiset.Add(x); } } else { // Subject and Object are Unbound HashSet <INode> nodes = new HashSet <INode>(); foreach (Triple t in context.Data.Triples) { nodes.Add(t.Subject); nodes.Add(t.Object); } foreach (INode n in nodes) { Set s = new Set(); s.Add(subjVar, n); s.Add(objVar, n); context.OutputMultiset.Add(s); } } } else { // Object is a Term // Create a single set with the Variable bound to the Object Term Set s = new Set(); s.Add(subjVar, ((NodeMatchPattern)PathEnd).Node); context.OutputMultiset.Add(s); } } } else if (objVar != null) { // Subject is a Term but Object is a Variable if (context.InputMultiset.ContainsVariable(objVar)) { // Object is Bound // Preseve sets where the Subject Term is equal to the currently bound Object INode subjTerm = ((NodeMatchPattern)PathStart).Node; foreach (ISet s in context.InputMultiset.Sets) { INode temp = s[objVar]; if (temp != null && temp.Equals(subjTerm)) { context.OutputMultiset.Add(s.Copy()); } } } else { // Object is Unbound // Create a single set with the Variable bound to the Suject Term Set s = new Set(); s.Add(objVar, ((NodeMatchPattern)PathStart).Node); context.OutputMultiset.Add(s); } } else { // Should already have dealt with this earlier (the AreBothTerms() and AreSameTerms() branch) throw new RdfQueryException("Reached unexpected point of ZeroLengthPath evaluation"); } return(context.OutputMultiset); }
/// <summary> /// Evaluates a Zero Length Path /// </summary> /// <param name="context">Evaluation Context</param> /// <returns></returns> public override BaseMultiset Evaluate(SparqlEvaluationContext context) { if (this.AreBothTerms()) { if (this.AreSameTerms()) { return(new IdentityMultiset()); } else { return(new NullMultiset()); } } String subjVar = this.PathStart.VariableName; String objVar = this.PathEnd.VariableName; context.OutputMultiset = new Multiset(); //Determine the Triples to which this applies IEnumerable <Triple> ts = null; if (subjVar != null) { //Subject is a Variable if (context.InputMultiset.ContainsVariable(subjVar)) { //Subject is Bound if (objVar != null) { //Object is a Variable if (context.InputMultiset.ContainsVariable(objVar)) { //Object is Bound ts = (from s in context.InputMultiset.Sets where s[subjVar] != null && s[objVar] != null from t in context.Data.GetTriplesWithSubjectObject(s[subjVar], s[objVar]) select t); } else { //Object is Unbound ts = (from s in context.InputMultiset.Sets where s[subjVar] != null from t in context.Data.GetTriplesWithSubject(s[subjVar]) select t); } } else { //Object is a Term //Preseve sets where the Object Term is equal to the currently bound Subject INode objTerm = ((NodeMatchPattern)this.PathEnd).Node; foreach (ISet s in context.InputMultiset.Sets) { INode temp = s[subjVar]; if (temp != null && temp.Equals(objTerm)) { context.OutputMultiset.Add(s.Copy()); } } } } else { //Subject is Unbound if (objVar != null) { //Object is a Variable if (context.InputMultiset.ContainsVariable(objVar)) { //Object is Bound ts = (from s in context.InputMultiset.Sets where s[objVar] != null from t in context.Data.GetTriplesWithObject(s[objVar]) select t); } else { //Object is Unbound HashSet <INode> nodes = new HashSet <INode>(); foreach (Triple t in context.Data.Triples) { nodes.Add(t.Subject); nodes.Add(t.Object); } foreach (INode n in nodes) { Set s = new Set(); s.Add(subjVar, n); s.Add(objVar, n); context.OutputMultiset.Add(s); } } } else { //Object is a Term //Create a single set with the Variable bound to the Object Term Set s = new Set(); s.Add(subjVar, ((NodeMatchPattern)this.PathEnd).Node); context.OutputMultiset.Add(s); } } } else if (objVar != null) { //Subject is a Term but Object is a Variable if (context.InputMultiset.ContainsVariable(objVar)) { //Object is Bound //Preseve sets where the Subject Term is equal to the currently bound Object INode subjTerm = ((NodeMatchPattern)this.PathStart).Node; foreach (ISet s in context.InputMultiset.Sets) { INode temp = s[objVar]; if (temp != null && temp.Equals(subjTerm)) { context.OutputMultiset.Add(s.Copy()); } } } else { //Object is Unbound //Create a single set with the Variable bound to the Suject Term Set s = new Set(); s.Add(objVar, ((NodeMatchPattern)this.PathStart).Node); context.OutputMultiset.Add(s); } } else { //Should already have dealt with this earlier (the AreBothTerms() and AreSameTerms() branch) throw new RdfQueryException("Reached unexpected point of ZeroLengthPath evaluation"); } //Get the Matches only if we haven't already generated the output if (ts != null) { HashSet <KeyValuePair <INode, INode> > matches = new HashSet <KeyValuePair <INode, INode> >(); foreach (Triple t in ts) { if (this.PathStart.Accepts(context, t.Subject) && this.PathEnd.Accepts(context, t.Object)) { matches.Add(new KeyValuePair <INode, INode>(t.Subject, t.Object)); } } //Generate the Output based on the mathces if (matches.Count == 0) { context.OutputMultiset = new NullMultiset(); } else { if (this.PathStart.VariableName == null && this.PathEnd.VariableName == null) { context.OutputMultiset = new IdentityMultiset(); } else { context.OutputMultiset = new Multiset(); foreach (KeyValuePair <INode, INode> m in matches) { Set s = new Set(); if (subjVar != null) { s.Add(subjVar, m.Key); } if (objVar != null) { s.Add(objVar, m.Value); } context.OutputMultiset.Add(s); } } } } return(context.OutputMultiset); }
/// <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 (Path is Property) { HashSet <INode> nodes = new HashSet <INode>(); INode predicate = ((Property)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, 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> /// Evaluates the One or More Path /// </summary> /// <param name="context">SPARQL Evaluation Context</param> /// <returns></returns> public override BaseMultiset Evaluate(SparqlEvaluationContext context) { List <List <INode> > paths = new List <List <INode> >(); BaseMultiset initialInput = context.InputMultiset; int step = 0, prevCount = 0, skipCount = 0; String subjVar = this.PathStart.VariableName; String objVar = this.PathEnd.VariableName; bool bothTerms = (subjVar == null && objVar == null); bool reverse = false; if (subjVar == null || (subjVar != null && context.InputMultiset.ContainsVariable(subjVar)) || (objVar != null && !context.InputMultiset.ContainsVariable(objVar))) { //Work Forwards from the Starting Term or Bound Variable //OR if there is no Ending Term or Bound Variable work forwards regardless if (subjVar == null) { paths.Add(((NodeMatchPattern)this.PathStart).Node.AsEnumerable().ToList()); } else if (context.InputMultiset.ContainsVariable(subjVar)) { paths.AddRange((from s in context.InputMultiset.Sets where s[subjVar] != null select s[subjVar]).Distinct().Select(n => n.AsEnumerable().ToList())); } } else if (objVar == null || (objVar != null && context.InputMultiset.ContainsVariable(objVar))) { //Work Backwards from Ending Term or Bound Variable if (objVar == null) { paths.Add(((NodeMatchPattern)this.PathEnd).Node.AsEnumerable().ToList()); } else { paths.AddRange((from s in context.InputMultiset.Sets where s[objVar] != null select s[objVar]).Distinct().Select(n => n.AsEnumerable().ToList())); } reverse = true; } if (paths.Count == 0) { this.GetPathStarts(context, paths, reverse); } //Traverse the Paths do { prevCount = paths.Count; foreach (List <INode> path in paths.Skip(skipCount).ToList()) { foreach (INode nextStep in this.EvaluateStep(context, path, reverse)) { List <INode> newPath = new List <INode>(path); newPath.Add(nextStep); paths.Add(newPath); } } if (step == 0) { //Remove any 1 length paths as these denote path starts that couldn't be traversed paths.RemoveAll(p => p.Count == 1); prevCount = paths.Count; } //Update Counts //skipCount is used to indicate the paths which we will ignore for the purposes of //trying to further extend since we've already done them once step++; if (paths.Count == 0) { break; } if (step > 1) { skipCount = prevCount; } //Can short circuit evaluation here if both are terms and any path is acceptable if (bothTerms) { bool exit = false; foreach (List <INode> path in paths) { if (reverse) { if (this.PathEnd.Accepts(context, path[0]) && this.PathStart.Accepts(context, path[path.Count - 1])) { exit = true; break; } } else { if (this.PathStart.Accepts(context, path[0]) && this.PathEnd.Accepts(context, path[path.Count - 1])) { exit = true; break; } } } if (exit) { break; } } } while (paths.Count > prevCount || (step == 1 && paths.Count == prevCount)); if (paths.Count == 0) { //If all path starts lead nowhere then we get the Null Multiset as a result context.OutputMultiset = new NullMultiset(); } else { context.OutputMultiset = new Multiset(); //Evaluate the Paths to check that are acceptable foreach (List <INode> path in paths) { if (reverse) { if (this.PathEnd.Accepts(context, path[0]) && this.PathStart.Accepts(context, path[path.Count - 1])) { Set s = new Set(); if (!bothTerms) { if (subjVar != null) { s.Add(subjVar, path[path.Count - 1]); } if (objVar != null) { s.Add(objVar, path[0]); } } context.OutputMultiset.Add(s); //If both are terms can short circuit evaluation here //It is sufficient just to determine that there is one path possible if (bothTerms) { break; } } } else { if (this.PathStart.Accepts(context, path[0]) && this.PathEnd.Accepts(context, path[path.Count - 1])) { Set s = new Set(); if (!bothTerms) { if (subjVar != null) { s.Add(subjVar, path[0]); } if (objVar != null) { s.Add(objVar, path[path.Count - 1]); } } context.OutputMultiset.Add(s); //If both are terms can short circuit evaluation here //It is sufficient just to determine that there is one path possible if (bothTerms) { break; } } } } if (bothTerms) { //If both were terms transform to an Identity/Null Multiset as appropriate if (context.OutputMultiset.IsEmpty) { context.OutputMultiset = new NullMultiset(); } else { context.OutputMultiset = new IdentityMultiset(); } } } context.InputMultiset = initialInput; return(context.OutputMultiset); }
/// <summary> /// Applies the Projection to the results of Evaluating the Inner Pattern /// </summary> /// <param name="context">Evaluation Context</param> /// <returns></returns> public BaseMultiset Evaluate(SparqlEvaluationContext context) { try { context.InputMultiset = context.Evaluate(this._pattern);//this._pattern.Evaluate(context); } catch (RdfQueryTimeoutException) { //If not partial results throw the error if (!context.Query.PartialResultsOnTimeout) { throw; } } IEnumerable <SparqlVariable> vars; if (context.Query != null) { vars = context.Query.Variables; } else { vars = this._variables; } //For Null and Identity Multisets this is just a simple selection if (context.InputMultiset is NullMultiset) { context.InputMultiset = new Multiset(vars.Select(v => v.Name)); context.OutputMultiset = context.InputMultiset; } else if (context.InputMultiset is IdentityMultiset) { context.InputMultiset = new Multiset(vars.Select(v => v.Name)); Set s = new Set(); context.InputMultiset.Add(s); context.OutputMultiset = context.InputMultiset; } //If we have a Group Multiset then Projection is more complex GroupMultiset groupSet = null; if (context.InputMultiset is GroupMultiset) { groupSet = (GroupMultiset)context.InputMultiset; //Project all simple variables for the Groups here foreach (SparqlVariable v in vars.Where(v => v.IsResultVariable && !v.IsProjection && !v.IsAggregate)) { //Can only project a variable if it's used in the GROUP OR if it was assigned by a GROUP BY expression if (context.Query != null) { if (!groupSet.ContainsVariable(v.Name) && !context.Query.GroupBy.Variables.Contains(v.Name)) { throw new RdfQueryException("Cannot project the variable ?" + v.Name + " since this Query contains Grouping(s) but the given Variable is not in the GROUP BY - use the SAMPLE aggregate if you need to sample this Variable"); } } //Project the value for each variable if (!groupSet.ContainsVariable(v.Name)) { //Simple Variable Projection used in GROUP BY so grab first value as all should be same //for the group context.OutputMultiset.AddVariable(v.Name); foreach (int id in groupSet.SetIDs) { INode value = groupSet.Contents[groupSet.GroupSetIDs(id).First()][v.Name]; context.OutputMultiset[id].Add(v.Name, value); } } } } else if (context.Query.IsAggregate) { context.OutputMultiset = new Multiset(); } //Project the rest of the Variables Set aggSet = new Set(); foreach (SparqlVariable v in vars.Where(v => v.IsResultVariable)) { if (groupSet == null) { context.InputMultiset.AddVariable(v.Name); } else { context.OutputMultiset.AddVariable(v.Name); } if (v.IsAggregate) { //Compute the Aggregate if (groupSet != null) { context.InputMultiset = groupSet.Contents; foreach (int id in groupSet.SetIDs) { INode aggValue = v.Aggregate.Apply(context, groupSet.GroupSetIDs(id)); context.OutputMultiset[id].Add(v.Name, aggValue); } context.InputMultiset = groupSet; } else { INode aggValue = v.Aggregate.Apply(context, context.InputMultiset.SetIDs); aggSet.Add(v.Name, aggValue); } } else if (v.IsProjection) { if (context.Query != null && context.Query.IsAggregate && context.Query.GroupBy == null) { throw new RdfQueryException("Cannot project an expression since this Query contains Aggregates and no GROUP BY"); } else { //Compute the Value of the Projection Expression for each Set foreach (int id in context.InputMultiset.SetIDs) { ISet s = context.InputMultiset[id]; try { INode temp = v.Projection.Value(context, id); s.Add(v.Name, temp); } catch (RdfQueryException) { s.Add(v.Name, null); } } } } else { if (context.Query != null && context.Query.IsAggregate && context.Query.GroupBy == null) { //If this is an Aggregate without a GROUP BY projected variables are invalid throw new RdfQueryException("Cannot project the variable ?" + v.Name + " since this Query contains Aggregates and no GROUP BY"); } else if (context.Query != null && context.Query.IsAggregate && !context.Query.GroupBy.ProjectableVariables.Contains(v.Name)) { //If this is an Aggregate with a GROUP BY projected variables are only valid if they occur in the GROUP BY throw new RdfQueryException("Cannot project the variable ?" + v.Name + " since this Query contains Aggregates but the given Variable is not in the GROUP BY - use the SAMPLE aggregate if you need to access this variable"); } //Otherwise we don't need to do anything with the Variable } } if (context.Query != null && context.Query.IsAggregate && context.Query.GroupBy == null) { context.OutputMultiset.Add(aggSet); } else { context.OutputMultiset = context.InputMultiset; } return(context.OutputMultiset); }