Skip to content

Commit 8dac2da

Browse files
committed
Calculate pattern cost based on position of variables.
1 parent 911566c commit 8dac2da

2 files changed

Lines changed: 65 additions & 21 deletions

File tree

lib/rdf/query/pattern.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ def initialize!
4747
@subject = Variable.new(@subject) if @subject.is_a?(Symbol)
4848
@predicate = Variable.new(@predicate) if @predicate.is_a?(Symbol)
4949
@object = Variable.new(@object) if @object.is_a?(Symbol)
50+
51+
# Estmate cost positionally, with variables being least expensive as objects, then predicates, then subjects, then graph_names.
52+
# XXX does not consider bound variables, which would need to be dynamically calculated.
53+
@cost = (@object.nil? || @object.variable? ? 1 : 0) +
54+
(@predicate.nil? || @predicate.variable? ? 2 : 0) +
55+
(@subject.nil? || @subject.variable? ? 4 : 0) +
56+
(@graph_name && @graph_name.variable? ? 8 : 0)
5057
super
5158
end
5259

spec/query_pattern_spec.rb

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
end
2323
end
2424

25+
its(:cost) {is_expected.to be (4+2+1)}
26+
2527
it "should not have variables" do
2628
expect(subject.variables?).to be_falsey
2729
expect(subject.variable_count).to eq 0
@@ -52,9 +54,11 @@
5254
let(:s) {RDF::Query::Variable.new(:s, true)}
5355
subject {described_class.new(s)}
5456

55-
specify {expect(subject).not_to be_constant}
56-
specify {expect(subject).to be_variable}
57-
specify {expect(subject).to be_bound}
57+
specify {is_expected.not_to be_constant}
58+
specify {is_expected.to be_variable}
59+
specify {is_expected.to be_bound}
60+
61+
its(:cost) {is_expected.to be (4+2+1)}
5862

5963
it "should have one variable" do
6064
expect(subject).to be_variables
@@ -73,8 +77,8 @@
7377
end
7478

7579
it "should be fully bound" do
76-
expect(subject).not_to be_unbound
77-
expect(subject).to be_bound
80+
is_expected.not_to be_unbound
81+
is_expected.to be_bound
7882
end
7983

8084
it "should have one binding" do
@@ -90,9 +94,11 @@
9094
let(:o) {RDF::Query::Variable.new(:o, true)}
9195
subject {described_class.new(s, p, o)}
9296

93-
specify {expect(subject).not_to be_constant}
94-
specify {expect(subject).to be_variable}
95-
specify {expect(subject).to be_bound}
97+
specify {is_expected.not_to be_constant}
98+
specify {is_expected.to be_variable}
99+
specify {is_expected.to be_bound}
100+
101+
its(:cost) {is_expected.to be (4+2+1)}
96102

97103
it "should have three variables" do
98104
expect(subject).to be_variables
@@ -111,8 +117,8 @@
111117
end
112118

113119
it "should be fully bound" do
114-
expect(subject).not_to be_unbound
115-
expect(subject).to be_bound
120+
is_expected.not_to be_unbound
121+
is_expected.to be_bound
116122
end
117123

118124
it "should have three bindings" do
@@ -122,18 +128,45 @@
122128
end
123129
end
124130

131+
context "with variable in different locations" do
132+
{
133+
"spog": [[RDF::URI("s"), RDF::URI("p"), RDF::URI("o"), graph_name: RDF::URI("g")], 0],
134+
"spo?": [[RDF::URI("s"), RDF::URI("p"), RDF::URI("o"), graph_name: :g], 8],
135+
"sp?g": [[RDF::URI("s"), RDF::URI("p"), :o, graph_name: RDF::URI("g")], 1],
136+
"s?og": [[RDF::URI("s"), :p, RDF::URI("o"), graph_name: RDF::URI("g")], 2],
137+
"?pog": [[:s, RDF::URI("p"), RDF::URI("o"), graph_name: RDF::URI("g")], 4],
138+
}.each do |name, (args, cost)|
139+
it "cost for #{name} should be #{cost}" do
140+
pattern = described_class.new(*args)
141+
expect(pattern.cost).to be cost
142+
end
143+
end
144+
end
145+
146+
context "#cost" do
147+
it "can be set separately" do
148+
expect(subject.cost).to be (4+2+1)
149+
subject.cost = 0
150+
expect(subject.cost).to be 0
151+
end
152+
end
153+
125154
context "with a graph_name" do
126155
let(:s) {RDF::Query::Variable.new(:s, true)}
127156
let(:p) {RDF::Query::Variable.new(:p, true)}
128157
let(:o) {RDF::Query::Variable.new(:o, true)}
158+
subject {described_class.new(s, p, o, graph_name: :c)}
159+
129160
it "uses a variable for a symbol" do
130-
pattern = described_class.new(s, p, o, graph_name: :c)
131-
expect(pattern.graph_name).to eq RDF::Query::Variable.new(:c)
161+
expect(subject.graph_name).to eq RDF::Query::Variable.new(:c)
132162
end
133163

164+
its(:cost) {is_expected.to be (8+4+2+1)}
165+
134166
it "uses a constant for :default" do
135167
pattern = described_class.new(s, p, o, graph_name: false)
136168
expect(pattern.graph_name).to eq false
169+
expect(pattern.cost).to eq (4+2+1)
137170
end
138171
end
139172

@@ -143,9 +176,11 @@
143176
let(:o) {RDF::Query::Variable.new(:o)}
144177
subject {described_class.new(s, p, o)}
145178

146-
specify {expect(subject).not_to be_constant}
147-
specify {expect(subject).to be_variable}
148-
specify {expect(subject).not_to be_bound}
179+
specify {is_expected.not_to be_constant}
180+
specify {is_expected.to be_variable}
181+
specify {is_expected.not_to be_bound}
182+
183+
its(:cost) {is_expected.to be (4+2+1)}
149184

150185
describe "#bind" do
151186
context "complete solution" do
@@ -196,9 +231,11 @@
196231
let(:p) {RDF::Query::Variable.new(:p)}
197232
subject {described_class.new(s, p)}
198233

199-
specify {expect(subject).not_to be_constant}
200-
specify {expect(subject).to be_variable}
201-
specify {expect(subject).not_to be_bound}
234+
specify {is_expected.not_to be_constant}
235+
specify {is_expected.to be_variable}
236+
specify {is_expected.not_to be_bound}
237+
238+
its(:cost) {is_expected.to be (4+2+1)}
202239

203240
it "should have two variable" do
204241
expect(subject).to be_variables
@@ -218,8 +255,8 @@
218255
end
219256

220257
it "should not be fully bound" do
221-
expect(subject).not_to be_unbound
222-
expect(subject).not_to be_bound
258+
is_expected.not_to be_unbound
259+
is_expected.not_to be_bound
223260
end
224261

225262
it "should have one binding" do
@@ -249,7 +286,7 @@
249286

250287
describe "#variable_terms" do
251288
it "has term" do
252-
expect(described_class.new(RDF::Node.new, :p, 123).variable_terms).to eq([:predicate])
289+
expect(described_class.new(RDF::Node.new, :p, RDF::Literal(123)).variable_terms).to eq([:predicate])
253290
end
254291
end
255292

0 commit comments

Comments
 (0)