From c3e291dcca261f1ec7efb41fc6a0bd15aa5bd917 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Wed, 4 May 2022 16:33:51 -0700 Subject: [PATCH 1/4] `Query#execute` and `Pattern#execute` can take a Solution for bindings, in addition to a hash. This supports passing pre-bound variables from SPARQL. --- lib/rdf/query.rb | 5 +++-- lib/rdf/query/pattern.rb | 3 ++- spec/query_pattern_spec.rb | 10 ++++++++++ spec/query_spec.rb | 21 +++++++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/rdf/query.rb b/lib/rdf/query.rb index 809a4381..a91c0f40 100644 --- a/lib/rdf/query.rb +++ b/lib/rdf/query.rb @@ -294,7 +294,7 @@ def optimize!(**options) # Alias for `:graph_name`. # @param [Hash{Symbol => Object}] options # any additional keyword options - # @option options [Hash{Symbol => RDF::Term}] bindings + # @option options [Hash{Symbol => RDF::Term}, RDF::Query::Solution] bindings # optional variable bindings to use # @option options [Boolean] :optimize # Optimize query before execution. @@ -313,6 +313,7 @@ def execute(queryable, bindings: {}, solutions: Solution.new, graph_name: nil, n # Otherwise, a quick empty solution simplifies the logic below; no special case for # the first pattern @solutions = Query::Solutions(solutions) + bindings = bindings.to_h if bindings.is_a?(Solution) # If there are no patterns, just return the empty solution if empty? @@ -341,7 +342,7 @@ def execute(queryable, bindings: {}, solutions: Solution.new, graph_name: nil, n bindings.each_key do |variable| if pattern.variables.include?(variable) unbound_solutions, old_solutions = old_solutions, Query::Solutions() - bindings[variable].each do |binding| + Array(bindings[variable]).each do |binding| unbound_solutions.each do |solution| old_solutions << solution.merge(variable => binding) end diff --git a/lib/rdf/query/pattern.rb b/lib/rdf/query/pattern.rb index f6214800..5a9ef841 100644 --- a/lib/rdf/query/pattern.rb +++ b/lib/rdf/query/pattern.rb @@ -160,7 +160,7 @@ def eql?(other) # # @param [RDF::Queryable] queryable # the graph or repository to query - # @param [Hash{Symbol => RDF::Term}] bindings + # @param [Hash{Symbol => RDF::Term}, RDF::Query::Solution] bindings # optional variable bindings to use # @yield [statement] # each matching statement @@ -171,6 +171,7 @@ def eql?(other) # @see RDF::Queryable#query # @since 0.3.0 def execute(queryable, bindings = {}, &block) + bindings = bindings.to_h if bindings.is_a?(Solution) query = { subject: subject.is_a?(Variable) && bindings[subject.to_sym] ? bindings[subject.to_sym] : subject, predicate: predicate.is_a?(Variable) && bindings[predicate.to_sym] ? bindings[predicate.to_sym] : predicate, diff --git a/spec/query_pattern_spec.rb b/spec/query_pattern_spec.rb index 6b1f5dae..86564663 100644 --- a/spec/query_pattern_spec.rb +++ b/spec/query_pattern_spec.rb @@ -599,10 +599,20 @@ let!(:statement) {repo.detect {|s| s.to_a.none?(&:node?)}} let(:pattern) {described_class.new(:s, :p, :o)} subject {pattern} + describe "#execute" do it "executes query against repo" do expect(subject.execute(repo).to_a.size).to eql repo.count end + + it "executes query with hash bindings" do + expect(subject.execute(repo, {subject: statement.subject}).to_a.size).to be > 0 + end + + it "executes query with solution bindings" do + soln = RDF::Query::Solution.new(subject: statement.subject) + expect(subject.execute(repo, soln).to_a.size).to be > 0 + end end describe "#solution" do diff --git a/spec/query_spec.rb b/spec/query_spec.rb index 4e63284b..bdb31fb3 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -702,6 +702,14 @@ {o: EX.o1}, {o: EX.o4}] end + it "limits a variable to the initial bindings (solution)" do + query = RDF::Query.new do |query| + query << [EX.x1, EX.p, :o] + end + expect(query.execute(graph, bindings: RDF::Query::Solution.new(o: [EX.o1, EX.o4]))).to have_result_set [ + {o: EX.o1}, {o: EX.o4}] + end + it "uses bindings for multiple variables" do graph << [EX.x1, EX.p1, EX.o1] graph << [EX.x1, EX.p1, EX.o2] @@ -714,6 +722,19 @@ ] end + it "uses bindings for multiple variables (solution)" do + graph << [EX.x1, EX.p1, EX.o1] + graph << [EX.x1, EX.p1, EX.o2] + graph << [EX.x2, EX.p1, EX.o1] + query = RDF::Query.new do |query| + query << [:s, EX.p1, :o] + end + solution = RDF::Query::Solution.new(o: EX.o1, s: EX.x1) + expect(query.execute(graph, bindings: solution)).to have_result_set [ + {s: EX.x1, o: EX.o1} + ] + end + end context "solution modifiers" do From 9acb19a3c47b6719dccdee424e706e42e7aad1bc Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Mon, 9 May 2022 13:13:48 -0700 Subject: [PATCH 2/4] When generating docs, add etc/doap files to yard output. --- .github/workflows/generate-docs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index b8d16ed9..40a1cfce 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -19,6 +19,8 @@ jobs: run: gem install yard --no-document - name: Build YARD Ruby Documentation run: yardoc + - name: Copy etc files + run: mkdir -p ./doc/yard/etc && cp ./etc/doap.* ./doc/yard/etc/ - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: From 567061c23ce095463ef01e2e6c5221813bf72c49 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Tue, 10 May 2022 22:16:09 -0700 Subject: [PATCH 3/4] Fix `Dataset#inspect` --- lib/rdf/model/dataset.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rdf/model/dataset.rb b/lib/rdf/model/dataset.rb index 183dd3ba..27ec4d2e 100644 --- a/lib/rdf/model/dataset.rb +++ b/lib/rdf/model/dataset.rb @@ -79,7 +79,7 @@ def each # # @return [String] def inspect - sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, uri.to_s) + sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, count.to_s) end ## From 06868532683d9ca162447b54d016bc0a57ac7e4b Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Sat, 28 May 2022 17:28:36 -0700 Subject: [PATCH 4/4] Version 3.2.8. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 406ebcbd..f092941a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.2.7 +3.2.8