Skip to content

Commit c3c5c87

Browse files
committed
Some optimization specs.
1 parent a95a421 commit c3c5c87

File tree

5 files changed

+177
-24
lines changed

5 files changed

+177
-24
lines changed

lib/sparql/algebra/operator/join.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,17 @@ def validate!
101101
#
102102
# @return [Join, RDF::Query] `self`
103103
# @return [self]
104-
# @see SPARQL::Algebra::Expression#optimize!
105-
def optimize!(**options)
104+
# @see SPARQL::Algebra::Expression#optimize
105+
def optimize(**options)
106106
ops = operands.map {|o| o.optimize(**options) }.reject {|o| o.respond_to?(:empty?) && o.empty?}
107-
@operands = ops
108-
self
107+
case ops.length
108+
when 0
109+
SPARQL::Algebra::Expression[:bgp]
110+
when 1
111+
ops.first
112+
else
113+
self.class.new(ops)
114+
end
109115
end
110116

111117
##

lib/sparql/algebra/operator/left_join.rb

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,21 +136,22 @@ def validate!
136136
# @return [Object] a copy of `self`
137137
# @see SPARQL::Algebra::Expression#optimize
138138
# FIXME
139-
def optimize!(**options)
140-
return self
141-
ops = operands.map {|o| o.optimize(**options) }.reject {|o| o.respond_to?(:empty?) && o.empty?}
142-
expr = ops.pop unless ops.last.executable?
139+
def optimize(**options)
140+
lhs, rhs, expr = operands.map {|o| o.optimize(**options) }
143141
expr = nil if expr.respond_to?(:true?) && expr.true?
144-
145-
# ops now is one or two executable operators
146-
# expr is a filter expression, which may have been optimized to 'true'
147-
case ops.length
148-
when 0
142+
143+
if lhs.empty? && rhs.empty?
149144
RDF::Query.new # Empty query, expr doesn't matter
150-
when 1
151-
expr ? Filter.new(expr, ops.first) : ops.first
145+
elsif rhs.empty?
146+
# Expression doesn't matter, just use the first operand
147+
lhs
148+
elsif lhs.empty?
149+
# Result is the filter of the second operand if there is an expression
150+
# FIXME: doesn't seem to work
151+
#expr ? Filter.new(expr, rhs) : rhs
152+
self.dup
152153
else
153-
expr ? LeftJoin.new(ops[0], ops[1], expr) : LeftJoin.new(ops[0], ops[1])
154+
expr ? LeftJoin.new(rhs, lhs, expr) : LeftJoin.new(lhs, rhs)
154155
end
155156
end
156157

spec/algebra/construct_spec.rb

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
describe SPARQL::Algebra::Query do
99
EX = RDF::EX = RDF::Vocabulary.new('http://example.org/') unless const_defined?(:EX)
1010

11+
let(:logger) {RDF::Spec.logger.tap {|l| l.level = Logger::INFO}}
12+
1113
context "construct" do
1214
{
1315
"query-construct-optional" => [
@@ -86,31 +88,38 @@
8688
@prefix ex: <http://example.com/> .
8789
ex:s ex:p1 [ex:p2 ex:o1] .
8890
),
89-
%q((prefix
91+
%q{(prefix
9092
((ex: <http://example.com/>))
91-
(construct ((triple ex:s ex:p1 _:b1) (triple _:b1 ex:p2 ex:o1)) (bgp)))),
93+
(construct ((triple ex:s ex:p1 _:b1) (triple _:b1 ex:p2 ex:o1)) (bgp)))},
9294
]
9395
}.each do |example, (source, result, query)|
9496
it "constructs #{example}" do
97+
logger.info "Source:\n#{source}"
98+
logger.info "Result:\n#{result}"
99+
logger.info "Query:\n#{query}"
95100
graph_r = RDF::Graph.new << RDF::Turtle::Reader.new(result)
96101

97102
expect(
98103
sparql_query(
99104
form: :construct, sse: true,
100105
graphs: {default: {data: source, format: :ttl}},
101106
query: query)
102-
).to be_isomorphic(graph_r)
107+
).to be_isomorphic(graph_r), logger.to_s
103108
end
104109

105110
it "constructs #{example} (with optimization)" do
111+
logger.info "Source:\n#{source}"
112+
logger.info "Result:\n#{result}"
113+
logger.info "Query:\n#{query}"
114+
logger.info "Optimized:\n#{SXP::Generator.string(SPARQL::Algebra.parse(query, optimize: true).to_sxp_bin)}"
106115
graph_r = RDF::Graph.new << RDF::Turtle::Reader.new(result)
107116

108117
expect(
109118
sparql_query(
110119
form: :construct, sse: true, optimize: true,
111120
graphs: {default: {data: source, format: :ttl}},
112121
query: query)
113-
).to be_isomorphic(graph_r)
122+
).to be_isomorphic(graph_r), logger.to_s
114123
end
115124
end
116125
end

spec/algebra/optimize_spec.rb

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
$:.unshift File.expand_path("../..", __FILE__)
2+
require 'spec_helper'
3+
require 'algebra/algebra_helper'
4+
require 'sparql/client'
5+
6+
include SPARQL::Algebra
7+
8+
describe SPARQL::Algebra::Query do
9+
describe :optimize do
10+
{
11+
"prefix": {
12+
input: %q(
13+
(prefix ((ex: <http://example.org/>))
14+
(bgp (triple ex:x1 ex:p2 ex:x2)))),
15+
expect: %q(
16+
(bgp (triple <http://example.org/x1> <http://example.org/p2> <http://example.org/x2>)))
17+
},
18+
"base": {
19+
input: %q(
20+
(base <http://example.org/>
21+
(bgp (triple <x1> <p2> <x2>)))),
22+
expect: %q(
23+
(bgp (triple <http://example.org/x1> <http://example.org/p2> <http://example.org/x2>)))
24+
},
25+
"join – empty (lhs)": {
26+
input: %q(
27+
(prefix ((ex: <http://example.org/>))
28+
(join
29+
(bgp)
30+
(bgp (triple ex:who ex:schoolHomepage ?schoolPage))))),
31+
expect: %q(
32+
(bgp (triple <http://example.org/who> <http://example.org/schoolHomepage> ?schoolPage)))
33+
},
34+
"join – empty (rhs)": {
35+
input: %q(
36+
(prefix ((ex: <http://example.org/>))
37+
(join
38+
(bgp (triple ex:who ex:schoolHomepage ?schoolPage))
39+
(bgp)))),
40+
expect: %q(
41+
(bgp (triple <http://example.org/who> <http://example.org/schoolHomepage> ?schoolPage)))
42+
},
43+
"join empty (both)": {
44+
input: %q(
45+
(prefix ((ex: <http://example.org/>))
46+
(join (bgp) (bgp)))),
47+
expect: %q(
48+
(bgp))
49+
},
50+
"left join empty lhs with filter expression": {
51+
input: %q{
52+
(prefix ((: <http://example/>))
53+
(leftjoin
54+
(bgp)
55+
(bgp (triple ?y :q ?w))
56+
(= ?v 2)))},
57+
expect: %q{
58+
(filter (= ?v 2)
59+
(bgp (triple ?y <http://example/q> ?w)))},
60+
pending: "Figure out LHS optimization"
61+
},
62+
"left join empty lhs with no filter expression": {
63+
input: %q{
64+
(prefix ((: <http://example/>))
65+
(leftjoin
66+
(bgp)
67+
(bgp (triple ?y :q ?w))))},
68+
expect: %q{
69+
(bgp (triple ?y <http://example/q> ?w))},
70+
pending: "Figure out LHS optimization"
71+
},
72+
"left join empty rhs with filter expression": {
73+
input: %q{
74+
(prefix ((: <http://example/>))
75+
(leftjoin
76+
(bgp (triple ?y :q ?w))
77+
(bgp)
78+
(= ?v 2)))},
79+
expect: %q{
80+
(bgp (triple ?y <http://example/q> ?w))s}
81+
},
82+
"left join empty rhs with no filter expression": {
83+
input: %q{
84+
(prefix ((: <http://example/>))
85+
(leftjoin
86+
(bgp (triple ?y :q ?w))
87+
(bgp)))},
88+
expect: %q{
89+
(bgp (triple ?y <http://example/q> ?w))}
90+
},
91+
"left join empty both with filter expression": {
92+
input: %q{
93+
(prefix ((: <http://example/>))
94+
(leftjoin (bgp) (bgp) (= ?v 2)))},
95+
expect: %q{(bgp)}
96+
},
97+
"left join empty both with no filter expression": {
98+
input: %q{
99+
(prefix ((: <http://example/>))
100+
(leftjoin (bgp) (bgp)))},
101+
expect: %q{(bgp)},
102+
},
103+
"mimus empty lhs": {pending: true},
104+
"mimus empty rhs": {pending: true},
105+
"mimus empty both": {pending: true},
106+
"path reverse": {
107+
input: %q{(prefix ((: <http://example.org/>)) (path :z (reverse :p) ?v))},
108+
expect: %q{(path ?v <http://example.org/p> <http://example.org/z>)}
109+
},
110+
"path path*": {
111+
input: %q{
112+
(prefix ((: <http://example.org/>))
113+
(path :a (seq (seq :p0 (path* :p1)) :p2) ?v))
114+
},
115+
expect: %q{
116+
(sequence
117+
(bgp
118+
(triple ??s <http://example.org/p2> <http://example.org/a>)
119+
(triple ?v <http://example.org/p1> ??o))
120+
(path ??o (path* <http://example.org/p2>) ??s))
121+
},
122+
pending: "How to compare ND variables"
123+
},
124+
"sameTerm": {pending: true},
125+
"service": {pending: true},
126+
"union": {pending: true},
127+
}.each do |name, params|
128+
it name do
129+
pending(params[:pending]) if params[:pending]
130+
query = SPARQL::Algebra.parse(params[:input])
131+
optimized = query.optimize
132+
expected = SPARQL::Algebra.parse(params[:expect])
133+
expect(optimized).to produce(expected, {})
134+
end
135+
end
136+
end
137+
end

spec/algebra/query_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,10 @@
181181
{ s: EX.x1, o: RDF::Literal.new(1) },
182182
{ s: EX.x2, o: RDF::Literal.new(2) },
183183
{ s: EX.x3, o: RDF::Literal.new(3) }]
184-
expect(query.optimize.execute(graph)).to have_result_set [
185-
{ s: EX.x1, o: RDF::Literal.new(1) },
186-
{ s: EX.x2, o: RDF::Literal.new(2) },
187-
{ s: EX.x3, o: RDF::Literal.new(3) }]
184+
expect(query.optimize.execute(graph)).to have_result_set [
185+
{ s: EX.x1, o: RDF::Literal.new(1) },
186+
{ s: EX.x2, o: RDF::Literal.new(2) },
187+
{ s: EX.x3, o: RDF::Literal.new(3) }]
188188
end
189189

190190
it "(filter (< ?o 3))" do

0 commit comments

Comments
 (0)