Merge lp:~rockwalrus/activerdf/devel into lp:activerdf
- devel
- Merge into trunk
Proposed by
Rockwalrus
Status: | Needs review |
---|---|
Proposed branch: | lp:~rockwalrus/activerdf/devel |
Merge into: | lp:activerdf |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~rockwalrus/activerdf/devel |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
ActiveRDF | Pending | ||
Review via email:
|
Commit message
Description of the change
To post a comment you must log in.
Unmerged revisions
- 466. By Rockwalrus
-
Support for UNION queries.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'activerdf-rdflite/lib/activerdf_rdflite/rdflite.rb' | |||
2 | --- activerdf-rdflite/lib/activerdf_rdflite/rdflite.rb 2008-06-11 12:11:39 +0000 | |||
3 | +++ activerdf-rdflite/lib/activerdf_rdflite/rdflite.rb 2009-05-20 03:36:52 +0000 | |||
4 | @@ -27,7 +27,7 @@ | |||
5 | 27 | class RDFLite < ActiveRdfAdapter | 27 | class RDFLite < ActiveRdfAdapter |
6 | 28 | ConnectionPool.register_adapter(:rdflite,self) | 28 | ConnectionPool.register_adapter(:rdflite,self) |
7 | 29 | bool_accessor :keyword_search, :reasoning | 29 | bool_accessor :keyword_search, :reasoning |
9 | 30 | 30 | ||
10 | 31 | # instantiates RDFLite database | 31 | # instantiates RDFLite database |
11 | 32 | # available parameters: | 32 | # available parameters: |
12 | 33 | # * :location => filepath (defaults to memory) | 33 | # * :location => filepath (defaults to memory) |
13 | @@ -235,7 +235,20 @@ | |||
14 | 235 | # translates ActiveRDF query into internal sqlite query string | 235 | # translates ActiveRDF query into internal sqlite query string |
15 | 236 | def translate(query) | 236 | def translate(query) |
16 | 237 | where, conditions = construct_where(query) | 237 | where, conditions = construct_where(query) |
18 | 238 | [construct_select(query) + construct_join(query) + where + construct_sort(query) + construct_limit(query), conditions] | 238 | |
19 | 239 | statement = construct_select(query) + construct_join(query.where_clauses) + where | ||
20 | 240 | |||
21 | 241 | query.union_groups.each do |union| | ||
22 | 242 | where, new_conditions = construct_union_where(union, query.reasoning?) | ||
23 | 243 | |||
24 | 244 | conditions += new_conditions | ||
25 | 245 | |||
26 | 246 | statement << "union " + construct_select(query, union) + construct_join(union.clauses) + where | ||
27 | 247 | end | ||
28 | 248 | |||
29 | 249 | |||
30 | 250 | statement << construct_sort(query) + construct_limit(query) | ||
31 | 251 | [statement, conditions] | ||
32 | 239 | end | 252 | end |
33 | 240 | 253 | ||
34 | 241 | private | 254 | private |
35 | @@ -243,14 +256,18 @@ | |||
36 | 243 | SPOC = ['s','p','o','c'] | 256 | SPOC = ['s','p','o','c'] |
37 | 244 | 257 | ||
38 | 245 | # construct select clause | 258 | # construct select clause |
40 | 246 | def construct_select(query) | 259 | def construct_select(query, union = nil) |
41 | 247 | # ASK queries counts the results, and return true if results > 0 | 260 | # ASK queries counts the results, and return true if results > 0 |
42 | 248 | return "select count(*)" if query.ask? | 261 | return "select count(*)" if query.ask? |
43 | 249 | 262 | ||
44 | 250 | # add select terms for each selectclause in the query | 263 | # add select terms for each selectclause in the query |
45 | 251 | # the term names depend on the join conditions, e.g. t0.s or t1.p | 264 | # the term names depend on the join conditions, e.g. t0.s or t1.p |
46 | 252 | select = query.select_clauses.collect do |term| | 265 | select = query.select_clauses.collect do |term| |
48 | 253 | variable_name(query, term) | 266 | if union.nil? |
49 | 267 | variable_name(query, term) | ||
50 | 268 | else | ||
51 | 269 | union_variable_name(union, term) | ||
52 | 270 | end | ||
53 | 254 | end | 271 | end |
54 | 255 | 272 | ||
55 | 256 | # add possible distinct and count functions to select clause | 273 | # add possible distinct and count functions to select clause |
56 | @@ -274,7 +291,7 @@ | |||
57 | 274 | 291 | ||
58 | 275 | clause << " limit #{limit} offset #{offset}" | 292 | clause << " limit #{limit} offset #{offset}" |
59 | 276 | clause | 293 | clause |
61 | 277 | end | 294 | end |
62 | 278 | 295 | ||
63 | 279 | # sort query results on variable clause (optionally) | 296 | # sort query results on variable clause (optionally) |
64 | 280 | def construct_sort(query) | 297 | def construct_sort(query) |
65 | @@ -294,13 +311,13 @@ | |||
66 | 294 | # only, and we should only alias tables we didnt alias yet) | 311 | # only, and we should only alias tables we didnt alias yet) |
67 | 295 | # we should only look for one join clause in each where-clause: when we find | 312 | # we should only look for one join clause in each where-clause: when we find |
68 | 296 | # one, we skip the rest of the variables in this clause. | 313 | # one, we skip the rest of the variables in this clause. |
70 | 297 | def construct_join(query) | 314 | def construct_join(clauses) |
71 | 298 | join_stmt = '' | 315 | join_stmt = '' |
72 | 299 | 316 | ||
73 | 300 | # no join necessary if only one where clause given | 317 | # no join necessary if only one where clause given |
75 | 301 | return ' from triple as t0 ' if query.where_clauses.size == 1 | 318 | return ' from triple as t0 ' if clauses.size == 1 |
76 | 302 | 319 | ||
78 | 303 | where_clauses = query.where_clauses.flatten | 320 | where_clauses = clauses.flatten |
79 | 304 | considering = where_clauses.uniq.select{|w| w.is_a?(Symbol)} | 321 | considering = where_clauses.uniq.select{|w| w.is_a?(Symbol)} |
80 | 305 | 322 | ||
81 | 306 | # constructing hash with indices for all terms | 323 | # constructing hash with indices for all terms |
82 | @@ -370,32 +387,7 @@ | |||
83 | 370 | 387 | ||
84 | 371 | # construct where clause | 388 | # construct where clause |
85 | 372 | def construct_where(query) | 389 | def construct_where(query) |
112 | 373 | # collecting where clauses, these will be added to the sql string later | 390 | where, right_hand_sides = convert_where_clauses(query.where_clauses, query.reasoning?) |
87 | 374 | where = [] | ||
88 | 375 | |||
89 | 376 | # collecting all the right-hand sides of where clauses (e.g. where name = | ||
90 | 377 | # 'abc'), to add to query string later using ?-notation, because then | ||
91 | 378 | # sqlite will automatically encode quoted literals correctly | ||
92 | 379 | right_hand_sides = [] | ||
93 | 380 | |||
94 | 381 | # convert each where clause to SQL: | ||
95 | 382 | # add where clause for each subclause, except if it's a variable | ||
96 | 383 | query.where_clauses.each_with_index do |clause,level| | ||
97 | 384 | raise ActiveRdfError, "where clause #{clause} is not a triple" unless clause.is_a?(Array) | ||
98 | 385 | clause.each_with_index do |subclause, i| | ||
99 | 386 | # dont add where clause for variables | ||
100 | 387 | unless subclause.is_a?(Symbol) || subclause.nil? | ||
101 | 388 | conditions = compute_where_condition(i, subclause, query.reasoning? && reasoning?) | ||
102 | 389 | if conditions.size == 1 | ||
103 | 390 | where << "t#{level}.#{SPOC[i]} = ?" | ||
104 | 391 | right_hand_sides << conditions.first | ||
105 | 392 | else | ||
106 | 393 | conditions = conditions.collect {|c| "'#{c}'"} | ||
107 | 394 | where << "t#{level}.#{SPOC[i]} in (#{conditions.join(',')})" | ||
108 | 395 | end | ||
109 | 396 | end | ||
110 | 397 | end | ||
111 | 398 | end | ||
113 | 399 | 391 | ||
114 | 400 | # if keyword clause given, convert it using keyword index | 392 | # if keyword clause given, convert it using keyword index |
115 | 401 | if query.keyword? && keyword_search? | 393 | if query.keyword? && keyword_search? |
116 | @@ -418,6 +410,48 @@ | |||
117 | 418 | ["where " + where.join(' and '), right_hand_sides] | 410 | ["where " + where.join(' and '), right_hand_sides] |
118 | 419 | end | 411 | end |
119 | 420 | end | 412 | end |
120 | 413 | |||
121 | 414 | # construct union where clause | ||
122 | 415 | def construct_union_where(union, reasoning) | ||
123 | 416 | where, right_hand_sides = convert_where_clauses(union.clauses, reasoning) | ||
124 | 417 | |||
125 | 418 | if where.empty? | ||
126 | 419 | ['',[]] | ||
127 | 420 | else | ||
128 | 421 | ["where " + where.join(' and '), right_hand_sides] | ||
129 | 422 | end | ||
130 | 423 | end | ||
131 | 424 | |||
132 | 425 | def convert_where_clauses(clauses, reasoning) | ||
133 | 426 | # collecting where clauses, these will be added to the sql string later | ||
134 | 427 | where = [] | ||
135 | 428 | |||
136 | 429 | # collecting all the right-hand sides of where clauses (e.g. where name = | ||
137 | 430 | # 'abc'), to add to query string later using ?-notation, because then | ||
138 | 431 | # sqlite will automatically encode quoted literals correctly | ||
139 | 432 | right_hand_sides = [] | ||
140 | 433 | |||
141 | 434 | # convert each where clause to SQL: | ||
142 | 435 | # add where clause for each subclause, except if it's a variable | ||
143 | 436 | clauses.each_with_index do |clause,level| | ||
144 | 437 | raise ActiveRdfError, "where clause #{clause} is not a triple" unless clause.is_a?(Array) | ||
145 | 438 | clause.each_with_index do |subclause, i| | ||
146 | 439 | # dont add where clause for variables | ||
147 | 440 | unless subclause.is_a?(Symbol) || subclause.nil? | ||
148 | 441 | conditions = compute_where_condition(i, subclause, reasoning && reasoning?) | ||
149 | 442 | if conditions.size == 1 | ||
150 | 443 | where << "t#{level}.#{SPOC[i]} = ?" | ||
151 | 444 | right_hand_sides << conditions.first | ||
152 | 445 | else | ||
153 | 446 | conditions = conditions.collect {|c| "'#{c}'"} | ||
154 | 447 | where << "t#{level}.#{SPOC[i]} in (#{conditions.join(',')})" | ||
155 | 448 | end | ||
156 | 449 | end | ||
157 | 450 | end | ||
158 | 451 | end | ||
159 | 452 | |||
160 | 453 | return where, right_hand_sides | ||
161 | 454 | end | ||
162 | 421 | 455 | ||
163 | 422 | def compute_where_condition(index, subclause, reasoning) | 456 | def compute_where_condition(index, subclause, reasoning) |
164 | 423 | conditions = [subclause] | 457 | conditions = [subclause] |
165 | @@ -490,6 +524,20 @@ | |||
166 | 490 | termspo = SPOC[index % 4] | 524 | termspo = SPOC[index % 4] |
167 | 491 | return "#{termtable}.#{termspo}" | 525 | return "#{termtable}.#{termspo}" |
168 | 492 | end | 526 | end |
169 | 527 | |||
170 | 528 | def union_variable_name(union,term) | ||
171 | 529 | # look up the first occurence of this term in the where clauses, and compute | ||
172 | 530 | # the level and s/p/o position of it | ||
173 | 531 | index = union.clauses.flatten.index(term) | ||
174 | 532 | |||
175 | 533 | if index.nil? | ||
176 | 534 | raise ActiveRdfError, "unbound variable :#{term.to_s} in select of #{union}" | ||
177 | 535 | end | ||
178 | 536 | |||
179 | 537 | termtable = "t#{index / 4}" | ||
180 | 538 | termspo = SPOC[index % 4] | ||
181 | 539 | return "#{termtable}.#{termspo}" | ||
182 | 540 | end | ||
183 | 493 | 541 | ||
184 | 494 | # wrap resources into ActiveRDF resources, literals into Strings | 542 | # wrap resources into ActiveRDF resources, literals into Strings |
185 | 495 | def wrap(query, results) | 543 | def wrap(query, results) |
186 | 496 | 544 | ||
187 | === modified file 'activerdf-redland/lib/activerdf_redland/redland.rb' | |||
188 | --- activerdf-redland/lib/activerdf_redland/redland.rb 2008-11-28 09:29:34 +0000 | |||
189 | +++ activerdf-redland/lib/activerdf_redland/redland.rb 2009-05-20 03:36:52 +0000 | |||
190 | @@ -106,6 +106,7 @@ | |||
191 | 106 | def query(query) | 106 | def query(query) |
192 | 107 | qs = Query2SPARQL.translate(query) | 107 | qs = Query2SPARQL.translate(query) |
193 | 108 | $activerdflog.debug "RedlandAdapter: executing SPARQL query #{qs}" | 108 | $activerdflog.debug "RedlandAdapter: executing SPARQL query #{qs}" |
194 | 109 | $activerdflog.warn "RedlandAdapter: UNION not supported before Rasqal 0.9.17" unless query.union_groups.empty? || Redland::rasqal_version_decimal > 916 | ||
195 | 109 | 110 | ||
196 | 110 | clauses = query.select_clauses.size | 111 | clauses = query.select_clauses.size |
197 | 111 | redland_query = Redland::Query.new(qs, 'sparql') | 112 | redland_query = Redland::Query.new(qs, 'sparql') |
198 | 112 | 113 | ||
199 | === modified file 'lib/active_rdf/queryengine/query.rb' | |||
200 | --- lib/active_rdf/queryengine/query.rb 2007-09-20 20:35:26 +0000 | |||
201 | +++ lib/active_rdf/queryengine/query.rb 2009-05-20 03:36:52 +0000 | |||
202 | @@ -6,7 +6,7 @@ | |||
203 | 6 | # data source. In all clauses symbols represent variables: | 6 | # data source. In all clauses symbols represent variables: |
204 | 7 | # Query.new.select(:s).where(:s,:p,:o). | 7 | # Query.new.select(:s).where(:s,:p,:o). |
205 | 8 | class Query | 8 | class Query |
207 | 9 | attr_reader :select_clauses, :where_clauses, :sort_clauses, :keywords, :limits, :offsets, :reverse_sort_clauses, :filter_clauses | 9 | attr_reader :select_clauses, :where_clauses, :sort_clauses, :keywords, :limits, :offsets, :reverse_sort_clauses, :filter_clauses, :union_groups |
208 | 10 | 10 | ||
209 | 11 | bool_accessor :distinct, :ask, :select, :count, :keyword, :reasoning | 11 | bool_accessor :distinct, :ask, :select, :count, :keyword, :reasoning |
210 | 12 | 12 | ||
211 | @@ -21,6 +21,7 @@ | |||
212 | 21 | @keywords = {} | 21 | @keywords = {} |
213 | 22 | @reasoning = true | 22 | @reasoning = true |
214 | 23 | @reverse_sort_clauses = [] | 23 | @reverse_sort_clauses = [] |
215 | 24 | @union_groups = [] | ||
216 | 24 | end | 25 | end |
217 | 25 | 26 | ||
218 | 26 | # Clears the select clauses | 27 | # Clears the select clauses |
219 | @@ -159,6 +160,72 @@ | |||
220 | 159 | self | 160 | self |
221 | 160 | end | 161 | end |
222 | 161 | 162 | ||
223 | 163 | class UnionGroup | ||
224 | 164 | attr_reader :clauses, :filter_clauses | ||
225 | 165 | |||
226 | 166 | def initialize | ||
227 | 167 | @clauses = [] | ||
228 | 168 | @filter_clauses = [] | ||
229 | 169 | end | ||
230 | 170 | |||
231 | 171 | # adds a clause to this union group | ||
232 | 172 | def where s,p,o,c=nil | ||
233 | 173 | unless s.respond_to?(:uri) or s.is_a?(Symbol) | ||
234 | 174 | raise(ActiveRdfError, "cannot add a where clause with s #{s}: s must be a resource or a variable") | ||
235 | 175 | end | ||
236 | 176 | unless p.respond_to?(:uri) or p.is_a?(Symbol) | ||
237 | 177 | raise(ActiveRdfError, "cannot add a where clause with p #{p}: p must be a resource or a variable") | ||
238 | 178 | end | ||
239 | 179 | |||
240 | 180 | @clauses << [s,p,o,c].collect{|arg| parametrise(arg)} | ||
241 | 181 | |||
242 | 182 | self | ||
243 | 183 | end | ||
244 | 184 | |||
245 | 185 | # adds one or more generic filters | ||
246 | 186 | # NOTE: you have to use SPARQL syntax for variables, eg. regex(?s, 'abc') | ||
247 | 187 | def filter *s | ||
248 | 188 | # add filter clauses | ||
249 | 189 | @filter_clauses << s | ||
250 | 190 | @filter_clauses.uniq! | ||
251 | 191 | |||
252 | 192 | self | ||
253 | 193 | end | ||
254 | 194 | |||
255 | 195 | private | ||
256 | 196 | #borrowed from outer class | ||
257 | 197 | def parametrise s | ||
258 | 198 | case s | ||
259 | 199 | when Symbol, RDFS::Resource, Literal, Class | ||
260 | 200 | s | ||
261 | 201 | when nil | ||
262 | 202 | nil | ||
263 | 203 | else | ||
264 | 204 | '"' + s.to_s + '"' | ||
265 | 205 | end | ||
266 | 206 | end | ||
267 | 207 | end | ||
268 | 208 | |||
269 | 209 | # adds a union group to this query | ||
270 | 210 | # | ||
271 | 211 | # usage:: union_group = Query.new.select(:s).where(:s, :p, 'eyal').union | ||
272 | 212 | # usage:: Query.new.select(:s).where(:s, :p, 'eyal').union {|g| g.where(:s, :p, 'benjamin')}.execute | ||
273 | 213 | # usage:: Query.new.select(:s).where(:s, :p, 'eyal').union(:s, :p, 'benjamin').execute | ||
274 | 214 | def union *args | ||
275 | 215 | union_group = UnionGroup.new | ||
276 | 216 | @union_groups << union_group | ||
277 | 217 | |||
278 | 218 | union_group.where *args unless args.empty? | ||
279 | 219 | |||
280 | 220 | yield(union_group) if block_given? | ||
281 | 221 | |||
282 | 222 | if !args.empty? || block_given? | ||
283 | 223 | self | ||
284 | 224 | else | ||
285 | 225 | union_group | ||
286 | 226 | end | ||
287 | 227 | end | ||
288 | 228 | |||
289 | 162 | # Adds keyword constraint to the query. You can use all Ferret query syntax in | 229 | # Adds keyword constraint to the query. You can use all Ferret query syntax in |
290 | 163 | # the constraint (e.g. keyword_where(:s,'eyal|benjamin') | 230 | # the constraint (e.g. keyword_where(:s,'eyal|benjamin') |
291 | 164 | def keyword_where s,o | 231 | def keyword_where s,o |
292 | 165 | 232 | ||
293 | === modified file 'lib/active_rdf/queryengine/query2sparql.rb' | |||
294 | --- lib/active_rdf/queryengine/query2sparql.rb 2008-02-08 12:37:42 +0000 | |||
295 | +++ lib/active_rdf/queryengine/query2sparql.rb 2009-05-20 03:36:52 +0000 | |||
296 | @@ -13,7 +13,8 @@ | |||
297 | 13 | select_clauses = query.select_clauses.collect{|s| construct_clause(s)} | 13 | select_clauses = query.select_clauses.collect{|s| construct_clause(s)} |
298 | 14 | 14 | ||
299 | 15 | str << "SELECT #{distinct}#{select_clauses.join(' ')} " | 15 | str << "SELECT #{distinct}#{select_clauses.join(' ')} " |
301 | 16 | str << "WHERE { #{where_clauses(query)} #{filter_clauses(query)}} " | 16 | str << "WHERE { { #{where_clauses(query)} #{filter_clauses(query.filter_clauses)}} #{union_groups(query)}} " |
302 | 17 | str << | ||
303 | 17 | str << "LIMIT #{query.limits} " if query.limits | 18 | str << "LIMIT #{query.limits} " if query.limits |
304 | 18 | str << "OFFSET #{query.offsets} " if query.offsets | 19 | str << "OFFSET #{query.offsets} " if query.offsets |
305 | 19 | elsif query.ask? | 20 | elsif query.ask? |
306 | @@ -24,8 +25,8 @@ | |||
307 | 24 | end | 25 | end |
308 | 25 | 26 | ||
309 | 26 | # concatenate filters in query | 27 | # concatenate filters in query |
312 | 27 | def self.filter_clauses(query) | 28 | def self.filter_clauses(filter_clauses) |
313 | 28 | "FILTER (#{query.filter_clauses.join(" && ")})" unless query.filter_clauses.empty? | 29 | "FILTER (#{filter_clauses.join(" && ")})" unless filter_clauses.empty? |
314 | 29 | end | 30 | end |
315 | 30 | 31 | ||
316 | 31 | # concatenate each where clause using space (e.g. 's p o') | 32 | # concatenate each where clause using space (e.g. 's p o') |
317 | @@ -44,18 +45,22 @@ | |||
318 | 44 | end | 45 | end |
319 | 45 | end | 46 | end |
320 | 46 | 47 | ||
322 | 47 | where_clauses = query.where_clauses.collect do |s,p,o,c| | 48 | statements(query.where_clauses) |
323 | 49 | end | ||
324 | 50 | |||
325 | 51 | def self.statements(clauses) | ||
326 | 52 | where_clauses = clauses.collect do |s,p,o,c| | ||
327 | 48 | # does there where clause use a context ? | 53 | # does there where clause use a context ? |
334 | 49 | if c.nil? | 54 | if c.nil? |
335 | 50 | [s,p,o].collect {|term| construct_clause(term) }.join(' ') | 55 | [s,p,o].collect {|term| construct_clause(term) }.join(' ') |
336 | 51 | else | 56 | else |
337 | 52 | "GRAPH #{construct_clause(c)} { #{construct_clause(s)} #{construct_clause(p)} #{construct_clause(o)} }" | 57 | "GRAPH #{construct_clause(c)} { #{construct_clause(s)} #{construct_clause(p)} #{construct_clause(o)} }" |
338 | 53 | end | 58 | end |
339 | 54 | end | 59 | end |
340 | 55 | 60 | ||
341 | 56 | "#{where_clauses.join(' . ')} ." | 61 | "#{where_clauses.join(' . ')} ." |
342 | 57 | end | 62 | end |
344 | 58 | 63 | ||
345 | 59 | def self.construct_clause(term) | 64 | def self.construct_clause(term) |
346 | 60 | if term.is_a?(Symbol) | 65 | if term.is_a?(Symbol) |
347 | 61 | "?#{term}" | 66 | "?#{term}" |
348 | @@ -63,6 +68,10 @@ | |||
349 | 63 | term.to_ntriple | 68 | term.to_ntriple |
350 | 64 | end | 69 | end |
351 | 65 | end | 70 | end |
352 | 71 | |||
353 | 72 | def self.union_groups(query) | ||
354 | 73 | query.union_groups.collect {|union| "UNION { #{statements(union.clauses)} #{filter_clauses(union.filter_clauses)}} "}.join | ||
355 | 74 | end | ||
356 | 66 | 75 | ||
357 | 67 | def self.sparql_engine | 76 | def self.sparql_engine |
358 | 68 | sparql_adapters = ConnectionPool.read_adapters.select{|adp| adp.is_a? SparqlAdapter} | 77 | sparql_adapters = ConnectionPool.read_adapters.select{|adp| adp.is_a? SparqlAdapter} |
359 | 69 | 78 | ||
360 | === modified file 'test/queryengine/test_query2sparql.rb' | |||
361 | --- test/queryengine/test_query2sparql.rb 2008-02-08 12:37:42 +0000 | |||
362 | +++ test/queryengine/test_query2sparql.rb 2009-05-20 03:36:52 +0000 | |||
363 | @@ -19,7 +19,7 @@ | |||
364 | 19 | query.where(:s, RDFS::Resource.new('predicate'), 30) | 19 | query.where(:s, RDFS::Resource.new('predicate'), 30) |
365 | 20 | 20 | ||
366 | 21 | generated = Query2SPARQL.translate(query) | 21 | generated = Query2SPARQL.translate(query) |
368 | 22 | expected = "SELECT ?s WHERE { ?s <predicate> \"30\"^^<http://www.w3.org/2001/XMLSchema#integer> . } " | 22 | expected = "SELECT ?s WHERE { { ?s <predicate> \"30\"^^<http://www.w3.org/2001/XMLSchema#integer> . } } " |
369 | 23 | assert_equal expected, generated | 23 | assert_equal expected, generated |
370 | 24 | 24 | ||
371 | 25 | query = Query.new | 25 | query = Query.new |
372 | @@ -27,7 +27,7 @@ | |||
373 | 27 | query.where(:s, RDFS::Resource.new('foaf:age'), :a) | 27 | query.where(:s, RDFS::Resource.new('foaf:age'), :a) |
374 | 28 | query.where(:a, RDFS::Resource.new('rdf:type'), RDFS::Resource.new('xsd:int')) | 28 | query.where(:a, RDFS::Resource.new('rdf:type'), RDFS::Resource.new('xsd:int')) |
375 | 29 | generated = Query2SPARQL.translate(query) | 29 | generated = Query2SPARQL.translate(query) |
377 | 30 | expected = "SELECT ?s WHERE { ?s <foaf:age> ?a . ?a <rdf:type> <xsd:int> . } " | 30 | expected = "SELECT ?s WHERE { { ?s <foaf:age> ?a . ?a <rdf:type> <xsd:int> . } } " |
378 | 31 | assert_equal expected, generated | 31 | assert_equal expected, generated |
379 | 32 | end | 32 | end |
380 | 33 | 33 | ||
381 | @@ -36,7 +36,7 @@ | |||
382 | 36 | query.distinct(:s) | 36 | query.distinct(:s) |
383 | 37 | query.where(:s, RDFS::Resource.new('foaf:age'), :a) | 37 | query.where(:s, RDFS::Resource.new('foaf:age'), :a) |
384 | 38 | generated = Query2SPARQL.translate(query) | 38 | generated = Query2SPARQL.translate(query) |
386 | 39 | expected = "SELECT DISTINCT ?s WHERE { ?s <foaf:age> ?a . } " | 39 | expected = "SELECT DISTINCT ?s WHERE { { ?s <foaf:age> ?a . } } " |
387 | 40 | assert_equal expected, generated | 40 | assert_equal expected, generated |
388 | 41 | end | 41 | end |
389 | 42 | 42 | ||
390 | @@ -46,4 +46,15 @@ | |||
391 | 46 | q2 = Query.new.select(:s).select(:a) | 46 | q2 = Query.new.select(:s).select(:a) |
392 | 47 | assert_equal Query2SPARQL.translate(q1),Query2SPARQL.translate(q2) | 47 | assert_equal Query2SPARQL.translate(q1),Query2SPARQL.translate(q2) |
393 | 48 | end | 48 | end |
394 | 49 | |||
395 | 50 | def test_union | ||
396 | 51 | query = Query.new | ||
397 | 52 | query.distinct(:s) | ||
398 | 53 | query.where(:s, RDFS::Resource.new('foaf:age'), :a) | ||
399 | 54 | query.union.where(:s, RDFS::Resource.new('foaf:name'), :n) | ||
400 | 55 | query.union{|u| u.where(:s, RDFS::Resource.new('foaf:mbox'), :m)} | ||
401 | 56 | generated = Query2SPARQL.translate(query) | ||
402 | 57 | expected = "SELECT DISTINCT ?s WHERE { { ?s <foaf:age> ?a . } UNION { ?s <foaf:name> ?n . } UNION { ?s <foaf:mbox> ?m . } } " | ||
403 | 58 | assert_equal expected, generated | ||
404 | 59 | end | ||
405 | 49 | end | 60 | end |
Support for UNION queries.