sparql 1.0.8 → 1.1.0p0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +14 -6
- data/README.md +57 -25
- data/VERSION +1 -1
- data/lib/sinatra/sparql.rb +5 -3
- data/lib/sparql/algebra/aggregate.rb +67 -0
- data/lib/sparql/algebra/evaluatable.rb +49 -4
- data/lib/sparql/algebra/expression.rb +6 -4
- data/lib/sparql/algebra/extensions.rb +99 -9
- data/lib/sparql/algebra/operator/and.rb +7 -4
- data/lib/sparql/algebra/operator/asc.rb +6 -3
- data/lib/sparql/algebra/operator/avg.rb +36 -0
- data/lib/sparql/algebra/operator/bnode.rb +5 -4
- data/lib/sparql/algebra/operator/bound.rb +5 -2
- data/lib/sparql/algebra/operator/coalesce.rb +6 -3
- data/lib/sparql/algebra/operator/concat.rb +6 -3
- data/lib/sparql/algebra/operator/count.rb +30 -0
- data/lib/sparql/algebra/operator/exists.rb +39 -0
- data/lib/sparql/algebra/operator/exprlist.rb +6 -3
- data/lib/sparql/algebra/operator/extend.rb +3 -1
- data/lib/sparql/algebra/operator/filter.rb +2 -1
- data/lib/sparql/algebra/operator/group.rb +101 -0
- data/lib/sparql/algebra/operator/group_concat.rb +55 -0
- data/lib/sparql/algebra/operator/if.rb +5 -5
- data/lib/sparql/algebra/operator/in.rb +7 -4
- data/lib/sparql/algebra/operator/max.rb +38 -0
- data/lib/sparql/algebra/operator/min.rb +38 -0
- data/lib/sparql/algebra/operator/minus.rb +51 -16
- data/lib/sparql/algebra/operator/negate.rb +31 -0
- data/lib/sparql/algebra/operator/notexists.rb +37 -0
- data/lib/sparql/algebra/operator/notin.rb +7 -4
- data/lib/sparql/algebra/operator/or.rb +7 -4
- data/lib/sparql/algebra/operator/order.rb +2 -2
- data/lib/sparql/algebra/operator/sample.rb +32 -0
- data/lib/sparql/algebra/operator/strlang.rb +1 -1
- data/lib/sparql/algebra/operator/sum.rb +36 -0
- data/lib/sparql/algebra/operator/table.rb +44 -0
- data/lib/sparql/algebra/operator.rb +37 -2
- data/lib/sparql/algebra.rb +1 -0
- data/lib/sparql/grammar/meta.rb +1143 -696
- data/lib/sparql/grammar/parser11.rb +178 -32
- data/lib/sparql/grammar/terminals11.rb +2 -2
- data/lib/sparql/grammar.rb +0 -2
- data/lib/sparql/results.rb +55 -0
- metadata +78 -81
@@ -180,7 +180,7 @@ module SPARQL::Grammar
|
|
180
180
|
|MAX|MD5|MINUTES|MIN|MONTH|NOW|RAND|REPLACE|ROUND|SAMPLE|SECONDS|SEPARATOR
|
181
181
|
|SHA1|SHA224|SHA256|SHA384|SHA512
|
182
182
|
|STRAFTER|STRBEFORE|STRDT|STRENDS|STRLANG|STRLEN|STRSTARTS|STRUUID|SUBSTR|STR|SUM
|
183
|
-
|TIMEZONE|TZ|UCASE|URI|UUID|YEAR
|
183
|
+
|TIMEZONE|TZ|UCASE|UNDEF|URI|UUID|YEAR
|
184
184
|
|isBLANK|isIRI|isURI|isLITERAL|isNUMERIC|sameTerm
|
185
185
|
}x
|
186
186
|
add_prod_datum(token.value.downcase.to_sym, token.value.downcase.to_sym)
|
@@ -191,19 +191,24 @@ module SPARQL::Grammar
|
|
191
191
|
|
192
192
|
# Productions
|
193
193
|
# [2] Query ::= Prologue
|
194
|
-
# ( SelectQuery | ConstructQuery | DescribeQuery | AskQuery )
|
194
|
+
# ( SelectQuery | ConstructQuery | DescribeQuery | AskQuery )
|
195
195
|
production(:Query) do |input, data, callback|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
196
|
+
return unless data[:query]
|
197
|
+
|
198
|
+
query = data[:query].first
|
199
|
+
|
200
|
+
# Add prefix
|
201
|
+
if data[:PrefixDecl]
|
202
|
+
pfx = data[:PrefixDecl].shift
|
203
|
+
data[:PrefixDecl].each {|p| pfx.merge!(p)}
|
204
|
+
pfx.operands[1] = query
|
205
|
+
query = pfx
|
206
206
|
end
|
207
|
+
|
208
|
+
# Add base
|
209
|
+
query = SPARQL::Algebra::Expression[:base, data[:BaseDecl].first, query] if data[:BaseDecl]
|
210
|
+
|
211
|
+
add_prod_datum(:query, query)
|
207
212
|
end
|
208
213
|
|
209
214
|
# [4] Prologue ::= ( BaseDecl | PrefixDecl )*
|
@@ -233,13 +238,17 @@ module SPARQL::Grammar
|
|
233
238
|
end
|
234
239
|
end
|
235
240
|
|
236
|
-
# [7] SelectQuery ::= SelectClause DatasetClause* WhereClause SolutionModifier
|
241
|
+
# [7] SelectQuery ::= SelectClause DatasetClause* WhereClause SolutionModifier ValuesClause
|
237
242
|
production(:SelectQuery) do |input, data, callback|
|
238
243
|
query = merge_modifiers(data)
|
239
244
|
add_prod_datum :query, query
|
240
245
|
end
|
241
246
|
|
242
247
|
# [8] SubSelect ::= SelectClause WhereClause SolutionModifier
|
248
|
+
production(:SubSelect) do |input, data, callback|
|
249
|
+
query = merge_modifiers(data)
|
250
|
+
add_prod_datum :query, query
|
251
|
+
end
|
243
252
|
|
244
253
|
# [9] SelectClause ::= 'SELECT' ( 'DISTINCT' | 'REDUCED' )? ( ( Var | ( '(' Expression 'AS' Var ')' ) )+ | '*' )
|
245
254
|
|
@@ -256,6 +265,7 @@ module SPARQL::Grammar
|
|
256
265
|
# 'WHERE' '{' TriplesTemplate? '}'
|
257
266
|
# SolutionModifier
|
258
267
|
# )
|
268
|
+
# ValuesClause
|
259
269
|
production(:ConstructQuery) do |input, data, callback|
|
260
270
|
data[:query] ||= [SPARQL::Algebra::Operator::BGP.new(*data[:pattern])]
|
261
271
|
query = merge_modifiers(data)
|
@@ -264,14 +274,14 @@ module SPARQL::Grammar
|
|
264
274
|
end
|
265
275
|
|
266
276
|
# [11] DescribeQuery ::= 'DESCRIBE' ( VarOrIri+ | '*' )
|
267
|
-
# DatasetClause* WhereClause? SolutionModifier
|
277
|
+
# DatasetClause* WhereClause? SolutionModifier ValuesClause
|
268
278
|
production(:DescribeQuery) do |input, data, callback|
|
269
279
|
query = merge_modifiers(data)
|
270
280
|
to_describe = data[:VarOrIri] || []
|
271
281
|
add_prod_datum :query, SPARQL::Algebra::Expression[:describe, to_describe, query]
|
272
282
|
end
|
273
283
|
|
274
|
-
# [12] AskQuery ::= 'ASK' DatasetClause* WhereClause
|
284
|
+
# [12] AskQuery ::= 'ASK' DatasetClause* WhereClause ValuesClause
|
275
285
|
production(:AskQuery) do |input, data, callback|
|
276
286
|
query = merge_modifiers(data)
|
277
287
|
add_prod_datum :query, SPARQL::Algebra::Expression[:ask, query]
|
@@ -311,8 +321,9 @@ module SPARQL::Grammar
|
|
311
321
|
end
|
312
322
|
|
313
323
|
# [21] HavingClause ::= 'HAVING' HavingCondition+
|
314
|
-
|
315
|
-
|
324
|
+
production(:HavingClause) do |input, data, callback|
|
325
|
+
add_prod_datum(:having, data[:Constraint])
|
326
|
+
end
|
316
327
|
|
317
328
|
# [23] OrderClause ::= 'ORDER' 'BY' OrderCondition+
|
318
329
|
production(:OrderClause) do |input, data, callback|
|
@@ -353,6 +364,19 @@ module SPARQL::Grammar
|
|
353
364
|
add_prod_datum(:offset, data[:literal])
|
354
365
|
end
|
355
366
|
|
367
|
+
# [28] ValuesClause ::= ( 'VALUES' DataBlock )?
|
368
|
+
production(:ValuesClause) do |input, data, callback|
|
369
|
+
debug("ValuesClause") {"vars: #{data[:Var].inspect}, row: #{data[:row].inspect}"}
|
370
|
+
if data[:row]
|
371
|
+
add_prod_datum :ValuesClause, SPARQL::Algebra::Expression.for(:table,
|
372
|
+
data[:Var].to_a.unshift(:vars),
|
373
|
+
*data[:row]
|
374
|
+
)
|
375
|
+
else
|
376
|
+
add_prod_datum :ValuesClause, SPARQL::Algebra::Expression.for(:table, :empty)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
356
380
|
# [54] GroupGraphPatternSub ::= TriplesBlock?
|
357
381
|
# ( GraphPatternNotTriples '.'? TriplesBlock? )*
|
358
382
|
production(:GroupGraphPatternSub) do |input, data, callback|
|
@@ -411,6 +435,8 @@ module SPARQL::Grammar
|
|
411
435
|
add_prod_datum(:query, SPARQL::Algebra::Expression.for(:leftjoin, lhs, *data[:leftjoin]))
|
412
436
|
elsif data[:query] && !lhs.empty?
|
413
437
|
add_prod_datum(:query, SPARQL::Algebra::Expression.for(:join, lhs, *data[:query]))
|
438
|
+
elsif data[:minus]
|
439
|
+
add_prod_datum(:query, SPARQL::Algebra::Expression.for(:minus, lhs, *data[:minus]))
|
414
440
|
elsif data[:query]
|
415
441
|
add_prod_datum(:query, data[:query])
|
416
442
|
else
|
@@ -447,6 +473,60 @@ module SPARQL::Grammar
|
|
447
473
|
add_prod_datum :extend, [data[:Expression].unshift(data[:Var].first)]
|
448
474
|
end
|
449
475
|
|
476
|
+
# [61] InlineData ::= 'VALUES' DataBlock
|
477
|
+
production(:InlineData) do |input, data, callback|
|
478
|
+
debug("InlineData") {"vars: #{data[:Var].inspect}, row: #{data[:row].inspect}"}
|
479
|
+
add_prod_datum :query, SPARQL::Algebra::Expression.for(:table,
|
480
|
+
data[:Var].unshift(:vars),
|
481
|
+
*data[:row]
|
482
|
+
)
|
483
|
+
end
|
484
|
+
|
485
|
+
# [63] InlineDataOneVar ::= Var '{' DataBlockValue* '}'
|
486
|
+
production(:InlineDataOneVar) do |input, data, callback|
|
487
|
+
add_prod_datum :Var, data[:Var]
|
488
|
+
|
489
|
+
data[:DataBlockValue].each do |d|
|
490
|
+
add_prod_datum :row, [[:row, data[:Var].dup << d]]
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
# [64] InlineDataFull ::= ( NIL | '(' Var* ')' )
|
495
|
+
# '{' ( '(' DataBlockValue* ')' | NIL )* '}'
|
496
|
+
production(:InlineDataFull) do |input, data, callback|
|
497
|
+
vars = data[:Var]
|
498
|
+
add_prod_datum :Var, vars
|
499
|
+
|
500
|
+
if data[:NIL].to_a.length > 1
|
501
|
+
add_prod_data :row, [:row]
|
502
|
+
else
|
503
|
+
data[:DataBlockValue].to_a.each do |ds|
|
504
|
+
r = [:row]
|
505
|
+
ds.each_with_index do |d, i|
|
506
|
+
r << [vars[i], d] if d
|
507
|
+
end
|
508
|
+
add_prod_data :row, r unless r.empty?
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
# _InlineDataFull_8 ::= '(' DataBlockValue* ')'
|
514
|
+
production(:_InlineDataFull_8) do |input, data, callback|
|
515
|
+
add_prod_data :DataBlockValue, data[:DataBlockValue].map {|v| v unless v == :undef}
|
516
|
+
end
|
517
|
+
|
518
|
+
# [65] DataBlockValue ::= iri | RDFLiteral | NumericLiteral | BooleanLiteral | 'UNDEF'
|
519
|
+
production(:DataBlockValue) do |input, data, callback|
|
520
|
+
add_prod_datum :DataBlockValue, data.values.first
|
521
|
+
end
|
522
|
+
|
523
|
+
# [66] MinusGraphPattern ::= 'MINUS' GroupGraphPattern
|
524
|
+
production(:MinusGraphPattern) do |input, data, callback|
|
525
|
+
expr = nil
|
526
|
+
query = data[:query] ? data[:query].first : SPARQL::Algebra::Operator::BGP.new
|
527
|
+
add_prod_data(:minus, query)
|
528
|
+
end
|
529
|
+
|
450
530
|
# [67] GroupOrUnionGraphPattern ::= GroupGraphPattern
|
451
531
|
# ( 'UNION' GroupGraphPattern )*
|
452
532
|
production(:GroupOrUnionGraphPattern) do |input, data, callback|
|
@@ -665,10 +745,10 @@ module SPARQL::Grammar
|
|
665
745
|
if data[:_Compare_Numeric]
|
666
746
|
add_prod_datum(:Expression, SPARQL::Algebra::Expression.for(data[:_Compare_Numeric].insert(1, *data[:Expression])))
|
667
747
|
elsif data[:in]
|
668
|
-
expr = (data[:Expression] + data[:in]).reject {|v| v
|
748
|
+
expr = (data[:Expression] + data[:in]).reject {|v| v.equal?(RDF.nil)}
|
669
749
|
add_prod_datum(:Expression, SPARQL::Algebra::Expression.for(expr.unshift(:in)))
|
670
750
|
elsif data[:notin]
|
671
|
-
expr = (data[:Expression] + data[:notin]).reject {|v| v
|
751
|
+
expr = (data[:Expression] + data[:notin]).reject {|v| v.equal?(RDF.nil)}
|
672
752
|
add_prod_datum(:Expression, SPARQL::Algebra::Expression.for(expr.unshift(:notin)))
|
673
753
|
else
|
674
754
|
# NumericExpression with no comparitor
|
@@ -753,7 +833,7 @@ module SPARQL::Grammar
|
|
753
833
|
if e.is_a?(RDF::Literal::Numeric)
|
754
834
|
add_prod_datum(:Expression, -e) # Simple optimization to match ARQ generation
|
755
835
|
else
|
756
|
-
add_prod_datum(:Expression, SPARQL::Algebra::Expression[:
|
836
|
+
add_prod_datum(:Expression, SPARQL::Algebra::Expression[:"-", e])
|
757
837
|
end
|
758
838
|
else
|
759
839
|
add_prod_datum(:Expression, data[:Expression])
|
@@ -898,7 +978,7 @@ module SPARQL::Grammar
|
|
898
978
|
production(:Aggregate) do |input, data, callback|
|
899
979
|
if aggregate_rule = data.keys.detect {|k| AGGREGATE_RULES.include?(k)}
|
900
980
|
parts = [aggregate_rule]
|
901
|
-
parts << [:separator, data[:string].first] if data[:separator] && data[:string]
|
981
|
+
parts << [:separator, RDF::Literal(data[:string].first)] if data[:separator] && data[:string]
|
902
982
|
parts << :distinct if data[:DISTINCT_REDUCED]
|
903
983
|
parts << data[:Expression].first if data[:Expression]
|
904
984
|
add_prod_data(aggregate_rule, SPARQL::Algebra::Expression.for(parts))
|
@@ -1038,7 +1118,7 @@ module SPARQL::Grammar
|
|
1038
1118
|
# @param [Symbol, #to_s] prod The starting production for the parser.
|
1039
1119
|
# It may be a URI from the grammar, or a symbol representing the local_name portion of the grammar URI.
|
1040
1120
|
# @return [Array]
|
1041
|
-
# @see http://www.w3.org/
|
1121
|
+
# @see http://www.w3.org/TR/sparql11-query/#sparqlAlgebra
|
1042
1122
|
# @see http://axel.deri.ie/sparqltutorial/ESWC2007_SPARQL_Tutorial_unit2b.pdf
|
1043
1123
|
def parse(prod = START)
|
1044
1124
|
ll1_parse(@input, prod.to_sym, @options.merge(:branch => BRANCH,
|
@@ -1048,7 +1128,7 @@ module SPARQL::Grammar
|
|
1048
1128
|
case context
|
1049
1129
|
when :trace
|
1050
1130
|
level, lineno, depth, *args = data
|
1051
|
-
message =
|
1131
|
+
message = args.to_sse
|
1052
1132
|
d_str = depth > 100 ? ' ' * 100 + '+' : ' ' * depth
|
1053
1133
|
str = "[#{lineno}](#{level})#{d_str}#{message}"
|
1054
1134
|
case @options[:debug]
|
@@ -1314,27 +1394,93 @@ module SPARQL::Grammar
|
|
1314
1394
|
#
|
1315
1395
|
# @see http://www.w3.org/TR/sparql11-query/#convertGroupAggSelectExpressions
|
1316
1396
|
def merge_modifiers(data)
|
1397
|
+
debug("merge modifiers") {data.inspect}
|
1317
1398
|
query = data[:query] ? data[:query].first : SPARQL::Algebra::Operator::BGP.new
|
1318
1399
|
|
1319
1400
|
vars = data[:Var] || []
|
1320
1401
|
order = data[:order] ? data[:order].first : []
|
1402
|
+
extensions = data.fetch(:extend, [])
|
1403
|
+
having = data.fetch(:having, [])
|
1404
|
+
values = data.fetch(:ValuesClause, []).first
|
1405
|
+
|
1406
|
+
# extension variables must not appear in projected variables.
|
1407
|
+
# Add them to the projection otherwise
|
1408
|
+
extensions.each do |(var, expr)|
|
1409
|
+
raise Error, "Extension variable #{var} also in SELECT" if vars.map(&:to_s).include?(var.to_s)
|
1410
|
+
vars << var
|
1411
|
+
end
|
1412
|
+
|
1413
|
+
# If any extension contains an aggregate, and there is now group, implicitly group by 1
|
1414
|
+
if !data[:group] &&
|
1415
|
+
extensions.any? {|(var, function)| function.aggregate?} ||
|
1416
|
+
having.any? {|c| c.aggregate? }
|
1417
|
+
debug {"Implicit group"}
|
1418
|
+
data[:group] = [[]]
|
1419
|
+
end
|
1321
1420
|
|
1322
1421
|
# Add datasets and modifiers in order
|
1323
1422
|
if data[:group]
|
1324
|
-
|
1325
|
-
|
1423
|
+
group_vars = data[:group].first
|
1424
|
+
|
1425
|
+
# For creating temporary variables
|
1426
|
+
agg = 0
|
1427
|
+
|
1428
|
+
# Find aggregated varirables in extensions
|
1429
|
+
aggregates = []
|
1430
|
+
aggregated_vars = extensions.map do |(var, function)|
|
1431
|
+
var if function.aggregate?
|
1432
|
+
end.compact
|
1433
|
+
|
1434
|
+
# Common function for replacing aggregates with temporary variables,
|
1435
|
+
# as defined in http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#convertGroupAggSelectExpressions
|
1436
|
+
aggregate_expression = lambda do |expr|
|
1437
|
+
# Replace unaggregated variables in expr
|
1438
|
+
# - For each unaggregated variable V in X
|
1439
|
+
expr.replace_vars! do |v|
|
1440
|
+
aggregated_vars.include?(v) ? v : SPARQL::Algebra::Expression[:sample, v]
|
1441
|
+
end
|
1442
|
+
|
1443
|
+
# Replace aggregates in expr as above
|
1444
|
+
expr.replace_aggregate! do |function|
|
1445
|
+
if avf = aggregates.detect {|(v, f)| f == function}
|
1446
|
+
avf.first
|
1447
|
+
else
|
1448
|
+
# Allocate a temporary variable for this function, and retain the mapping for outside the group
|
1449
|
+
av = RDF::Query::Variable.new(".#{agg}")
|
1450
|
+
av.distinguished = false
|
1451
|
+
agg += 1
|
1452
|
+
aggregates << [av, function]
|
1453
|
+
av
|
1454
|
+
end
|
1455
|
+
end
|
1456
|
+
end
|
1457
|
+
|
1458
|
+
# If there are extensions, they are aggregated if necessary and bound
|
1459
|
+
# to temporary variables
|
1460
|
+
extensions.map! do |(var, expr)|
|
1461
|
+
[var, aggregate_expression.call(expr)]
|
1462
|
+
end
|
1326
1463
|
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
data[:extend].each do |(var, expr)|
|
1331
|
-
raise Error, "Extension variable #{var} also in SELECT" if vars.map(&:to_s).include?(var.to_s)
|
1332
|
-
vars << var
|
1464
|
+
# Having clauses
|
1465
|
+
having.map! do |expr|
|
1466
|
+
aggregate_expression.call(expr)
|
1333
1467
|
end
|
1334
1468
|
|
1335
|
-
query =
|
1469
|
+
query = if aggregates.empty?
|
1470
|
+
SPARQL::Algebra::Expression[:group, group_vars, query]
|
1471
|
+
else
|
1472
|
+
SPARQL::Algebra::Expression[:group, group_vars, aggregates, query]
|
1473
|
+
end
|
1474
|
+
end
|
1475
|
+
|
1476
|
+
if values
|
1477
|
+
query = query ? SPARQL::Algebra::Expression[:join, query, values] : values
|
1336
1478
|
end
|
1337
1479
|
|
1480
|
+
query = SPARQL::Algebra::Expression[:extend, extensions, query] unless extensions.empty?
|
1481
|
+
|
1482
|
+
query = SPARQL::Algebra::Expression[:filter, *having, query] unless having.empty?
|
1483
|
+
|
1338
1484
|
query = SPARQL::Algebra::Expression[:order, data[:order].first, query] unless order.empty?
|
1339
1485
|
|
1340
1486
|
query = SPARQL::Algebra::Expression[:project, vars, query] unless vars.empty?
|
@@ -105,7 +105,7 @@ module SPARQL::Grammar
|
|
105
105
|
|SELECT|SEPARATOR|SERVICE
|
106
106
|
|SHA1|SHA224|SHA256|SHA384|SHA512
|
107
107
|
|STRAFTER|STRBEFORE|STRDT|STRENDS|STRLANG|STRLEN|STRSTARTS|STRUUID|SUBSTR|STR|SUM
|
108
|
-
|TIMEZONE|TO|TZ|UCASE|UNDEF|UNION|URI|USING|UUID
|
108
|
+
|TIMEZONE|TO|TZ|UCASE|UNDEF|UNION|URI|USING|UUID|VALUES
|
109
109
|
|WHERE|WITH|YEAR
|
110
110
|
|isBLANK|isIRI|isURI|isLITERAL|isNUMERIC|sameTerm
|
111
111
|
|true
|
@@ -129,7 +129,7 @@ module SPARQL::Grammar
|
|
129
129
|
SHA1 SHA224 SHA256 SHA384 SHA512
|
130
130
|
STRAFTER STRBEFORE STRDT STRENDS STRLANG STRLEN STRSTARTS STRUUID SUBSTR STR SUM
|
131
131
|
TIMEZONE TO TZ UCASE UNDEF UNION URI USING UUID
|
132
|
-
WHERE WITH YEAR
|
132
|
+
VALUES WHERE WITH YEAR
|
133
133
|
isBLANK isIRI isURI isLITERAL isNUMERIC sameTerm
|
134
134
|
true false
|
135
135
|
} + [
|
data/lib/sparql/grammar.rb
CHANGED
data/lib/sparql/results.rb
CHANGED
@@ -7,6 +7,8 @@ module SPARQL
|
|
7
7
|
:json => 'application/sparql-results+json',
|
8
8
|
:html => 'text/html',
|
9
9
|
:xml => 'application/sparql-results+xml',
|
10
|
+
:csv => 'text/csv',
|
11
|
+
:tsv => 'text/tab-separated-values'
|
10
12
|
}
|
11
13
|
|
12
14
|
##
|
@@ -112,6 +114,57 @@ module SPARQL
|
|
112
114
|
end
|
113
115
|
end
|
114
116
|
end
|
117
|
+
|
118
|
+
##
|
119
|
+
# Generate Solutions as CSV
|
120
|
+
# @return [String]
|
121
|
+
# @see http://www.w3.org/TR/rdf-sparql-json-res/#results
|
122
|
+
def to_csv
|
123
|
+
require 'csv' unless defined?(::CSV)
|
124
|
+
bnode_map = {}
|
125
|
+
bnode_gen = "_:a"
|
126
|
+
CSV.generate(:row_sep => "\r\n") do |csv|
|
127
|
+
csv << variable_names.to_a
|
128
|
+
self.each do |solution|
|
129
|
+
csv << variable_names.map do |n|
|
130
|
+
case term = solution[n]
|
131
|
+
when RDF::Node then bnode_map[term] ||=
|
132
|
+
begin
|
133
|
+
this = bnode_gen
|
134
|
+
bnode_gen = bnode_gen.succ
|
135
|
+
this
|
136
|
+
end
|
137
|
+
else
|
138
|
+
solution[n].to_s
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
##
|
145
|
+
# Generate Solutions as TSV
|
146
|
+
# @return [String]
|
147
|
+
# @see http://www.w3.org/TR/rdf-sparql-json-res/#results
|
148
|
+
def to_tsv
|
149
|
+
require 'csv' unless defined?(::CSV)
|
150
|
+
bnode_map = {}
|
151
|
+
bnode_gen = "_:a"
|
152
|
+
results = [
|
153
|
+
variable_names.map {|v| "?#{v}"}.join("\t")
|
154
|
+
] + self.map do |solution|
|
155
|
+
variable_names.map do |n|
|
156
|
+
case term = solution[n]
|
157
|
+
when RDF::Literal::Integer, RDF::Literal::Decimal, RDF::Literal::Double
|
158
|
+
term.canonicalize.to_s
|
159
|
+
when nil
|
160
|
+
""
|
161
|
+
else
|
162
|
+
RDF::NTriples.serialize(term)
|
163
|
+
end
|
164
|
+
end.join("\t")
|
165
|
+
end
|
166
|
+
results.join("\n") + "\n"
|
167
|
+
end
|
115
168
|
end
|
116
169
|
|
117
170
|
##
|
@@ -189,6 +242,8 @@ module SPARQL
|
|
189
242
|
when :json then solutions.to_json
|
190
243
|
when :xml then solutions.to_xml
|
191
244
|
when :html then solutions.to_html
|
245
|
+
when :csv then solutions.to_csv
|
246
|
+
when :tsv then solutions.to_tsv
|
192
247
|
else
|
193
248
|
raise RDF::WriterError, "Unknown format #{(format || content_type).inspect} for #{solutions.class}"
|
194
249
|
end
|