From 656494d8a0fb53e0a117b26c379b61a75fa9b871 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Wed, 15 Nov 2023 11:37:18 -0800 Subject: [PATCH] Fix documentation on `RDF::Query#initialize` and `#execuite` to note that variable graph names do not match the default graph due to SPARQL semantics. Fixes #442. --- lib/rdf/mixin/queryable.rb | 2 ++ lib/rdf/query.rb | 15 ++++++--------- lib/rdf/query/pattern.rb | 4 ++-- lib/rdf/query/variable.rb | 2 ++ spec/query_spec.rb | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 11 deletions(-) diff --git a/lib/rdf/mixin/queryable.rb b/lib/rdf/mixin/queryable.rb index 82bee7b1..576f35d0 100644 --- a/lib/rdf/mixin/queryable.rb +++ b/lib/rdf/mixin/queryable.rb @@ -146,6 +146,8 @@ def query_execute(query, **options, &block) # # Patterns may also have embedded patterns as either a subject or object, recursively. # + # Patterns with a variable `graph_name` do not match the default graph. + # # When matching, match an embedded pattern against embedded statements, recursively. (see {RDF::Query::Pattern#eql?}) # # @param [RDF::Query::Pattern] pattern diff --git a/lib/rdf/query.rb b/lib/rdf/query.rb index a91c0f40..c5d68a75 100644 --- a/lib/rdf/query.rb +++ b/lib/rdf/query.rb @@ -151,10 +151,9 @@ def self.Solutions(*args) # @option options [RDF::Query::Solutions] :solutions (Solutions.new) # @option options [RDF::Resource, RDF::Query::Variable, false] :graph_name (nil) # Default graph name for matching against queryable. - # Named queries either match against a specifically named + # Queries with a graph name match against a specifically named # graphs if the name is an {RDF::Resource} or bound {RDF::Query::Variable}. - # Names that are against unbound variables match either default - # or named graphs. + # Queries using an unbound variable as a graph name only match against named graphs, and will not match the default graph. # The name of `false` will only match against the default graph. # @option options [RDF::Resource, RDF::Query::Variable, false] :name (nil) # Alias for `:graph_name`. @@ -168,10 +167,9 @@ def self.Solutions(*args) # @param [RDF::Query::Solutions] solutions (Solutions.new) # @param [RDF::Resource, RDF::Query::Variable, false] graph_name (false) # Default graph name for matching against queryable. - # Named queries either match against a specifically named + # Queries with a graph name match against a specifically named # graphs if the name is an {RDF::Resource} or bound {RDF::Query::Variable}. - # Names that are against unbound variables match either default - # or named graphs. + # Queries using an unbound variable as a graph name only match against named graphs, and will not match the default graph. # The name of `false` will only match against the default graph. # @param [RDF::Resource, RDF::Query::Variable, false] name (false) # Alias for `:graph_name`. @@ -285,10 +283,9 @@ def optimize!(**options) # @param [RDF::Query::Solutions] solutions (Solutions.new) # @param [RDF::Resource, RDF::Query::Variable, false] graph_name (nil) # Default graph name for matching against queryable. - # Named queries either match against a specifically named + # Queries with a graph name match against a specifically named # graphs if the name is an {RDF::Resource} or bound {RDF::Query::Variable}. - # Names that are against unbound variables match either default - # or named graphs. + # Queries using an unbound variable as a graph name only match against named graphs, and will not match the default graph. # The name of `false` will only match against the default graph. # @param [RDF::Resource, RDF::Query::Variable, false] name (nil) # Alias for `:graph_name`. diff --git a/lib/rdf/query/pattern.rb b/lib/rdf/query/pattern.rb index 5a9ef841..5e141086 100644 --- a/lib/rdf/query/pattern.rb +++ b/lib/rdf/query/pattern.rb @@ -23,7 +23,7 @@ def self.from(pattern, graph_name: nil, **options) # @option options [Variable, URI, Symbol, nil] :predicate (nil) # @option options [Variable, Term, Symbol, nil] :object (nil) # @option options [Variable, Resource, Symbol, nil, false] :graph_name (nil) - # A graph_name of nil matches any graph, a graph_name of false, matches only the default graph. + # A graph_name of nil matches any graph, a graph_name of false, matches only the default graph. (See {RDF::Query#initialize}) # @option options [Boolean] :optional (false) # # @overload initialize(subject, predicate, object, options = {}) @@ -32,7 +32,7 @@ def self.from(pattern, graph_name: nil, **options) # @param [Variable, Termm, Symbol, nil] object # @param [Hash{Symbol => Object}] options # @option options [Variable, Resource, Symbol, nil, false] :graph_name (nil) - # A graph_name of nil matches any graph, a graph_name of false, matches only the default graph. + # A graph_name of nil matches any graph, a graph_name of false, matches only the default graph. (See {RDF::Query#initialize}) # @option options [Boolean] :optional (false) # # @note {Statement} treats symbols as interned {Node} instances, in a {Pattern}, they are treated as {Variable}. diff --git a/lib/rdf/query/variable.rb b/lib/rdf/query/variable.rb index 7d28deeb..2c29c693 100644 --- a/lib/rdf/query/variable.rb +++ b/lib/rdf/query/variable.rb @@ -233,6 +233,8 @@ def hash # Returns `true` if this variable is equivalent to a given `other` # variable. Or, to another Term if bound, or to any other Term # + # @note when comparing against the default graph in an {RDF::Dataset}, `other` will be `false` and not be equal to an unbound variable. + # # @param [Object] other # @return [Boolean] `true` or `false` # @since 0.3.0 diff --git a/spec/query_spec.rb b/spec/query_spec.rb index 1aa98941..09f8be51 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -965,6 +965,43 @@ end end + context "Issues" do + it "issue #442" do + repository = RDF::Repository.new do |r| + # Adding a statement in the default graph + r << RDF::Statement.new( + RDF::URI('http://www.example.com#alice'), + RDF::URI('http://www.example.com#knows'), + RDF::URI('http://www.example.com#bob') + ) + + # Adding a statement in a named graph + r << RDF::Statement.new( + RDF::URI('http://www.example.com#alice'), + RDF::URI('http://www.example.com#knows'), + RDF::URI('http://www.example.com#charlie'), + graph_name: RDF::URI('http://www.example.com#named_graph') + ) + end + + query = RDF::Query.new( + { + RDF::URI('http://www.example.com#alice') => { + RDF::URI('http://www.example.com#knows') => :friend + } + }, + graph_name: RDF::Query::Variable.new(:graph) + ) + + solutions = query.execute(repository) + expect(solutions.count).to eql(1) + expect(solutions.first).to eql(RDF::Query::Solution.new( + friend: RDF::URI('http://www.example.com#charlie'), + graph: RDF::URI('http://www.example.com#named_graph') + )) + end + end + context "Examples" do let!(:graph) {RDF::Graph.new.insert(RDF::Spec.triples.extend(RDF::Enumerable))} subject {