rdf 3.0.11 → 3.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/AUTHORS +1 -1
- data/README.md +127 -95
- data/UNLICENSE +1 -1
- data/VERSION +1 -1
- data/etc/doap.nt +79 -85
- data/lib/rdf.rb +35 -23
- data/lib/rdf/changeset.rb +80 -19
- data/lib/rdf/cli.rb +7 -7
- data/lib/rdf/format.rb +17 -10
- data/lib/rdf/mixin/enumerable.rb +4 -3
- data/lib/rdf/mixin/mutable.rb +5 -15
- data/lib/rdf/mixin/queryable.rb +12 -4
- data/lib/rdf/mixin/transactable.rb +2 -2
- data/lib/rdf/mixin/writable.rb +9 -14
- data/lib/rdf/model/dataset.rb +1 -1
- data/lib/rdf/model/graph.rb +7 -4
- data/lib/rdf/model/list.rb +5 -5
- data/lib/rdf/model/literal.rb +3 -3
- data/lib/rdf/model/statement.rb +32 -9
- data/lib/rdf/model/uri.rb +53 -32
- data/lib/rdf/nquads.rb +6 -6
- data/lib/rdf/ntriples.rb +7 -5
- data/lib/rdf/ntriples/reader.rb +29 -7
- data/lib/rdf/ntriples/writer.rb +10 -1
- data/lib/rdf/query.rb +27 -35
- data/lib/rdf/query/hash_pattern_normalizer.rb +14 -12
- data/lib/rdf/query/pattern.rb +51 -18
- data/lib/rdf/query/solution.rb +20 -1
- data/lib/rdf/query/solutions.rb +15 -5
- data/lib/rdf/query/variable.rb +17 -5
- data/lib/rdf/reader.rb +76 -25
- data/lib/rdf/repository.rb +32 -18
- data/lib/rdf/transaction.rb +1 -1
- data/lib/rdf/util.rb +6 -5
- data/lib/rdf/util/cache.rb +2 -2
- data/lib/rdf/util/coercions.rb +60 -0
- data/lib/rdf/util/file.rb +20 -10
- data/lib/rdf/util/logger.rb +6 -6
- data/lib/rdf/util/uuid.rb +4 -4
- data/lib/rdf/vocab/owl.rb +401 -86
- data/lib/rdf/vocab/rdfs.rb +81 -18
- data/lib/rdf/vocab/rdfv.rb +147 -1
- data/lib/rdf/vocab/writer.rb +41 -3
- data/lib/rdf/vocab/xsd.rb +203 -2
- data/lib/rdf/vocabulary.rb +73 -15
- data/lib/rdf/writer.rb +33 -11
- metadata +34 -28
data/lib/rdf/nquads.rb
CHANGED
@@ -69,9 +69,9 @@ module RDF
|
|
69
69
|
|
70
70
|
begin
|
71
71
|
unless blank? || read_comment
|
72
|
-
subject = read_uriref || read_node || fail_subject
|
72
|
+
subject = read_uriref || read_node || read_rdfstar || fail_subject
|
73
73
|
predicate = read_uriref(intern: true) || fail_predicate
|
74
|
-
object = read_uriref || read_node || read_literal || fail_object
|
74
|
+
object = read_uriref || read_node || read_literal || read_rdfstar || fail_object
|
75
75
|
graph_name = read_uriref || read_node
|
76
76
|
if validate? && !read_eos
|
77
77
|
log_error("Expected end of statement (found: #{current_line.inspect})", lineno: lineno, exception: RDF::ReaderError)
|
@@ -96,7 +96,7 @@ module RDF
|
|
96
96
|
# @param [RDF::Term] object
|
97
97
|
# @return [void]
|
98
98
|
def write_quad(subject, predicate, object, graph_name)
|
99
|
-
puts format_quad(subject, predicate, object, graph_name,
|
99
|
+
puts format_quad(subject, predicate, object, graph_name, **@options)
|
100
100
|
end
|
101
101
|
|
102
102
|
##
|
@@ -107,7 +107,7 @@ module RDF
|
|
107
107
|
# @return [String]
|
108
108
|
# @since 0.4.0
|
109
109
|
def format_statement(statement, **options)
|
110
|
-
format_quad(*statement.to_quad, options)
|
110
|
+
format_quad(*statement.to_quad, **options)
|
111
111
|
end
|
112
112
|
|
113
113
|
##
|
@@ -120,8 +120,8 @@ module RDF
|
|
120
120
|
# @param [Hash{Symbol => Object}] options = ({})
|
121
121
|
# @return [String]
|
122
122
|
def format_quad(subject, predicate, object, graph_name, **options)
|
123
|
-
s = "%s %s %s " % [subject, predicate, object].map { |value| format_term(value, options) }
|
124
|
-
s += format_term(graph_name, options) + " " if graph_name
|
123
|
+
s = "%s %s %s " % [subject, predicate, object].map { |value| format_term(value, **options) }
|
124
|
+
s += format_term(graph_name, **options) + " " if graph_name
|
125
125
|
s + "."
|
126
126
|
end
|
127
127
|
end # Writer
|
data/lib/rdf/ntriples.rb
CHANGED
@@ -13,17 +13,19 @@ module RDF
|
|
13
13
|
#
|
14
14
|
# An example of an RDF statement in N-Triples format:
|
15
15
|
#
|
16
|
-
# <
|
16
|
+
# <https://rubygems.org/gems/rdf> <http://purl.org/dc/terms/title> "rdf" .
|
17
17
|
#
|
18
|
-
#
|
19
|
-
#
|
18
|
+
# ## RDFStar (RDF*)
|
19
|
+
#
|
20
|
+
# Supports statements as resources using `<<s p o>>`.
|
21
|
+
#
|
22
|
+
# ## Installation
|
20
23
|
#
|
21
24
|
# This is the only RDF serialization format that is directly supported by
|
22
25
|
# RDF.rb. Support for other formats is available in the form of add-on
|
23
26
|
# gems, e.g. 'rdf-xml' or 'rdf-json'.
|
24
27
|
#
|
25
|
-
# Documentation
|
26
|
-
# -------------
|
28
|
+
# ## Documentation
|
27
29
|
#
|
28
30
|
# * {RDF::NTriples::Format}
|
29
31
|
# * {RDF::NTriples::Reader}
|
data/lib/rdf/ntriples/reader.rb
CHANGED
@@ -25,6 +25,10 @@ module RDF::NTriples
|
|
25
25
|
# end
|
26
26
|
# end
|
27
27
|
#
|
28
|
+
# ** RDFStar (RDF*)
|
29
|
+
#
|
30
|
+
# Supports statements as resources using `<<s p o>>`.
|
31
|
+
#
|
28
32
|
# @see http://www.w3.org/TR/rdf-testcases/#ntriples
|
29
33
|
# @see http://www.w3.org/TR/n-triples/
|
30
34
|
class Reader < RDF::Reader
|
@@ -70,6 +74,10 @@ module RDF::NTriples
|
|
70
74
|
# 22
|
71
75
|
STRING_LITERAL_QUOTE = /"((?:[^\"\\\n\r]|#{ECHAR}|#{UCHAR})*)"/.freeze
|
72
76
|
|
77
|
+
# RDF*
|
78
|
+
ST_START = /^<</.freeze
|
79
|
+
ST_END = /^\s*>>/.freeze
|
80
|
+
|
73
81
|
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar
|
74
82
|
COMMENT = /^#\s*(.*)$/.freeze
|
75
83
|
NODEID = /^#{BLANK_NODE_LABEL}/.freeze
|
@@ -96,7 +104,7 @@ module RDF::NTriples
|
|
96
104
|
def self.unserialize(input, **options)
|
97
105
|
case input
|
98
106
|
when nil then nil
|
99
|
-
else self.new(input,
|
107
|
+
else self.new(input, logger: [], **options).read_value
|
100
108
|
end
|
101
109
|
end
|
102
110
|
|
@@ -104,20 +112,20 @@ module RDF::NTriples
|
|
104
112
|
# (see unserialize)
|
105
113
|
# @return [RDF::Resource]
|
106
114
|
def self.parse_subject(input, **options)
|
107
|
-
parse_uri(input, options) || parse_node(input, options)
|
115
|
+
parse_uri(input, **options) || parse_node(input, **options)
|
108
116
|
end
|
109
117
|
|
110
118
|
##
|
111
119
|
# (see unserialize)
|
112
120
|
# @return [RDF::URI]
|
113
|
-
def self.parse_predicate(input,
|
121
|
+
def self.parse_predicate(input, **options)
|
114
122
|
parse_uri(input, intern: true)
|
115
123
|
end
|
116
124
|
|
117
125
|
##
|
118
126
|
# (see unserialize)
|
119
127
|
def self.parse_object(input, **options)
|
120
|
-
parse_uri(input, options) || parse_node(input, options) || parse_literal(input, options)
|
128
|
+
parse_uri(input, **options) || parse_node(input, **options) || parse_literal(input, **options)
|
121
129
|
end
|
122
130
|
|
123
131
|
##
|
@@ -202,7 +210,7 @@ module RDF::NTriples
|
|
202
210
|
begin
|
203
211
|
read_statement
|
204
212
|
rescue RDF::ReaderError
|
205
|
-
value = read_uriref || read_node || read_literal
|
213
|
+
value = read_uriref || read_node || read_literal || read_rdfstar
|
206
214
|
log_recover
|
207
215
|
value
|
208
216
|
end
|
@@ -218,9 +226,9 @@ module RDF::NTriples
|
|
218
226
|
|
219
227
|
begin
|
220
228
|
unless blank? || read_comment
|
221
|
-
subject = read_uriref || read_node || fail_subject
|
229
|
+
subject = read_uriref || read_node || read_rdfstar || fail_subject
|
222
230
|
predicate = read_uriref(intern: true) || fail_predicate
|
223
|
-
object = read_uriref || read_node || read_literal || fail_object
|
231
|
+
object = read_uriref || read_node || read_literal || read_rdfstar || fail_object
|
224
232
|
|
225
233
|
if validate? && !read_eos
|
226
234
|
log_error("Expected end of statement (found: #{current_line.inspect})", lineno: lineno, exception: RDF::ReaderError)
|
@@ -234,6 +242,20 @@ module RDF::NTriples
|
|
234
242
|
end
|
235
243
|
end
|
236
244
|
|
245
|
+
##
|
246
|
+
# @return [RDF::Statement]
|
247
|
+
def read_rdfstar
|
248
|
+
if @options[:rdfstar] && match(ST_START)
|
249
|
+
subject = read_uriref || read_node || read_rdfstar || fail_subject
|
250
|
+
predicate = read_uriref(intern: true) || fail_predicate
|
251
|
+
object = read_uriref || read_node || read_literal || read_rdfstar || fail_object
|
252
|
+
if !match(ST_END)
|
253
|
+
log_error("Expected end of statement (found: #{current_line.inspect})", lineno: lineno, exception: RDF::ReaderError)
|
254
|
+
end
|
255
|
+
RDF::Statement.new(subject, predicate, object)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
237
259
|
##
|
238
260
|
# @return [Boolean]
|
239
261
|
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (comment)
|
data/lib/rdf/ntriples/writer.rb
CHANGED
@@ -208,7 +208,7 @@ module RDF::NTriples
|
|
208
208
|
# @param [RDF::Term] object
|
209
209
|
# @return [void]
|
210
210
|
def write_triple(subject, predicate, object)
|
211
|
-
puts format_triple(subject, predicate, object,
|
211
|
+
puts format_triple(subject, predicate, object, **@options)
|
212
212
|
end
|
213
213
|
|
214
214
|
##
|
@@ -221,6 +221,15 @@ module RDF::NTriples
|
|
221
221
|
format_triple(*statement.to_triple, **options)
|
222
222
|
end
|
223
223
|
|
224
|
+
##
|
225
|
+
# Returns the N-Triples representation of an RDF* reified statement.
|
226
|
+
#
|
227
|
+
# @param [RDF::Statement] statement
|
228
|
+
# @param [Hash{Symbol => Object}] options ({})
|
229
|
+
# @return [String]
|
230
|
+
def format_rdfstar(statement, **options)
|
231
|
+
"<<%s %s %s>>" % statement.to_a.map { |value| format_term(value, **options) }
|
232
|
+
end
|
224
233
|
##
|
225
234
|
# Returns the N-Triples representation of a triple.
|
226
235
|
#
|
data/lib/rdf/query.rb
CHANGED
@@ -90,7 +90,7 @@ module RDF
|
|
90
90
|
# the resulting solution sequence
|
91
91
|
# @see RDF::Query#execute
|
92
92
|
def self.execute(queryable, patterns = {}, options = {}, &block)
|
93
|
-
self.new(patterns, options, &block).execute(queryable, options)
|
93
|
+
self.new(patterns, **options, &block).execute(queryable, **options)
|
94
94
|
end
|
95
95
|
|
96
96
|
##
|
@@ -134,6 +134,12 @@ module RDF
|
|
134
134
|
# @return [Hash]
|
135
135
|
attr_reader :options
|
136
136
|
|
137
|
+
##
|
138
|
+
# Scope the query to named graphs matching value
|
139
|
+
#
|
140
|
+
# @return [RDF::Resource, RDF::Query::Variable, false] graph_name
|
141
|
+
attr_accessor :graph_name
|
142
|
+
|
137
143
|
##
|
138
144
|
# Initializes a new basic graph pattern query.
|
139
145
|
#
|
@@ -180,6 +186,7 @@ module RDF
|
|
180
186
|
@options = options.dup
|
181
187
|
@solutions = Query::Solutions(solutions)
|
182
188
|
graph_name = name if graph_name.nil?
|
189
|
+
@graph_name = graph_name
|
183
190
|
|
184
191
|
patterns << @options if patterns.empty?
|
185
192
|
|
@@ -189,8 +196,6 @@ module RDF
|
|
189
196
|
else patterns
|
190
197
|
end
|
191
198
|
|
192
|
-
self.graph_name = graph_name
|
193
|
-
|
194
199
|
if block_given?
|
195
200
|
case block.arity
|
196
201
|
when 1 then block.call(self)
|
@@ -223,7 +228,7 @@ module RDF
|
|
223
228
|
# whether this is an optional pattern
|
224
229
|
# @return [void] self
|
225
230
|
def pattern(pattern, **options)
|
226
|
-
@patterns << Pattern.from(pattern, options)
|
231
|
+
@patterns << Pattern.from(pattern, **options)
|
227
232
|
self
|
228
233
|
end
|
229
234
|
|
@@ -235,7 +240,7 @@ module RDF
|
|
235
240
|
# @return [RDF::Query] a copy of `self`
|
236
241
|
# @since 0.3.0
|
237
242
|
def optimize(**options)
|
238
|
-
self.dup.optimize!(options)
|
243
|
+
self.dup.optimize!(**options)
|
239
244
|
end
|
240
245
|
|
241
246
|
##
|
@@ -284,6 +289,8 @@ module RDF
|
|
284
289
|
# any additional keyword options
|
285
290
|
# @option options [Hash{Symbol => RDF::Term}] bindings
|
286
291
|
# optional variable bindings to use
|
292
|
+
# @option options [Boolean] :optimize
|
293
|
+
# Optimize query before execution.
|
287
294
|
# @option options [RDF::Query::Solutions] solutions
|
288
295
|
# optional initial solutions for chained queries
|
289
296
|
# @yield [solution]
|
@@ -294,9 +301,7 @@ module RDF
|
|
294
301
|
# the resulting solution sequence
|
295
302
|
# @see http://www.holygoat.co.uk/blog/entry/2005-10-25-1
|
296
303
|
# @see http://www.w3.org/TR/sparql11-query/#emptyGroupPattern
|
297
|
-
def execute(queryable, solutions: Solution.new, graph_name: nil, name: nil, **options, &block)
|
298
|
-
options = {bindings: {}}.merge(options)
|
299
|
-
|
304
|
+
def execute(queryable, bindings: {}, solutions: Solution.new, graph_name: nil, name: nil, **options, &block)
|
300
305
|
# Use provided solutions to allow for query chaining
|
301
306
|
# Otherwise, a quick empty solution simplifies the logic below; no special case for
|
302
307
|
# the first pattern
|
@@ -308,17 +313,17 @@ module RDF
|
|
308
313
|
return @solutions
|
309
314
|
end
|
310
315
|
|
316
|
+
self.optimize! if options[:optimize]
|
311
317
|
patterns = @patterns
|
312
318
|
graph_name = name if graph_name.nil?
|
313
|
-
graph_name =
|
314
|
-
options[:graph_name] = graph_name unless graph_name.nil?
|
319
|
+
@graph_name = graph_name unless graph_name.nil?
|
315
320
|
|
316
321
|
# Add graph_name to pattern, if necessary
|
317
|
-
unless graph_name.nil?
|
322
|
+
unless @graph_name.nil?
|
318
323
|
if patterns.empty?
|
319
|
-
patterns = [Pattern.new(nil, nil, nil, graph_name: graph_name)]
|
324
|
+
patterns = [Pattern.new(nil, nil, nil, graph_name: @graph_name)]
|
320
325
|
else
|
321
|
-
apply_graph_name(graph_name)
|
326
|
+
apply_graph_name(@graph_name)
|
322
327
|
end
|
323
328
|
end
|
324
329
|
|
@@ -326,15 +331,15 @@ module RDF
|
|
326
331
|
|
327
332
|
old_solutions, @solutions = @solutions, Query::Solutions()
|
328
333
|
|
329
|
-
|
334
|
+
bindings.each_key do |variable|
|
330
335
|
if pattern.variables.include?(variable)
|
331
336
|
unbound_solutions, old_solutions = old_solutions, Query::Solutions()
|
332
|
-
|
337
|
+
bindings[variable].each do |binding|
|
333
338
|
unbound_solutions.each do |solution|
|
334
339
|
old_solutions << solution.merge(variable => binding)
|
335
340
|
end
|
336
341
|
end
|
337
|
-
|
342
|
+
bindings.delete(variable)
|
338
343
|
end
|
339
344
|
end
|
340
345
|
|
@@ -407,38 +412,26 @@ module RDF
|
|
407
412
|
# Is this query scoped to a named graph?
|
408
413
|
# @return [Boolean]
|
409
414
|
def named?
|
410
|
-
!!
|
415
|
+
!!graph_name
|
411
416
|
end
|
412
417
|
|
413
418
|
# Is this query scoped to the default graph?
|
414
419
|
# @return [Boolean]
|
415
420
|
def default?
|
416
|
-
|
421
|
+
graph_name == false
|
417
422
|
end
|
418
423
|
|
419
424
|
# Is this query unscoped? This indicates that it can return results from
|
420
425
|
# either a named graph or the default graph.
|
421
426
|
# @return [Boolean]
|
422
427
|
def unnamed?
|
423
|
-
|
424
|
-
end
|
425
|
-
|
426
|
-
# Scope the query to named graphs matching value
|
427
|
-
# @param [RDF::IRI, RDF::Query::Variable] value
|
428
|
-
# @return [RDF::IRI, RDF::Query::Variable]
|
429
|
-
def graph_name=(value)
|
430
|
-
options[:graph_name] = value
|
431
|
-
end
|
432
|
-
|
433
|
-
# Scope of this query, if any
|
434
|
-
# @return [RDF::IRI, RDF::Query::Variable]
|
435
|
-
def graph_name
|
436
|
-
options[:graph_name]
|
428
|
+
graph_name.nil?
|
437
429
|
end
|
438
430
|
|
439
431
|
# Apply the graph name specified (or configured) to all patterns that have no graph name
|
440
432
|
# @param [RDF::IRI, RDF::Query::Variable] graph_name (self.graph_name)
|
441
|
-
def apply_graph_name(graph_name =
|
433
|
+
def apply_graph_name(graph_name = nil)
|
434
|
+
graph_name ||= self.graph_name
|
442
435
|
patterns.each {|pattern| pattern.graph_name = graph_name if pattern.graph_name.nil?} unless graph_name.nil?
|
443
436
|
end
|
444
437
|
|
@@ -515,8 +508,7 @@ module RDF
|
|
515
508
|
# @return [RDF::Query]
|
516
509
|
def dup
|
517
510
|
patterns = @patterns.map {|p| p.dup}
|
518
|
-
patterns
|
519
|
-
Query.new(*patterns)
|
511
|
+
Query.new(patterns, graph_name: graph_name, solutions: @solutions.dup, **options)
|
520
512
|
end
|
521
513
|
|
522
514
|
##
|
@@ -77,21 +77,23 @@ module RDF; class Query
|
|
77
77
|
##
|
78
78
|
# Returns the normalization of the specified `hash_pattern`.
|
79
79
|
#
|
80
|
-
# @
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
|
80
|
+
# @overload normalize!(hash_pattern, **options)
|
81
|
+
# @param [Hash{Symbol => Object}] hash_pattern (Hash.new)
|
82
|
+
# the query pattern as a hash.
|
83
|
+
# @param [Hash{Symbol => Object}] **options
|
84
|
+
# any additional normalization options.
|
85
|
+
# @option options [String] :anonymous_subject_format ("__%s__")
|
86
|
+
# the string format for anonymous subjects.
|
87
|
+
# @return [Hash{Symbol => Object}]
|
88
|
+
# the resulting query pattern as a normalized hash.
|
89
|
+
def normalize!(*args)
|
90
|
+
hash_pattern = args.shift
|
91
|
+
options = args.shift || {}
|
92
|
+
anonymous_subject_format = options.fetch(:anonymous_subject_format, '__%s__')
|
89
93
|
raise ArgumentError, "invalid hash pattern: #{hash_pattern.inspect}" unless hash_pattern.is_a?(Hash)
|
90
94
|
|
91
95
|
counter = RDF::Query::HashPatternNormalizer::Counter.new
|
92
96
|
|
93
|
-
anonymous_subject_format = (options[:anonymous_subject_format] || '__%s__').to_s
|
94
|
-
|
95
97
|
hash_pattern.inject({}) { |acc, pair|
|
96
98
|
subject, predicate_to_object = pair
|
97
99
|
|
@@ -184,7 +186,7 @@ module RDF; class Query
|
|
184
186
|
# the query pattern as a hash.
|
185
187
|
# @return [Hash{Symbol => Object}]
|
186
188
|
# the resulting query pattern as a normalized hash.
|
187
|
-
def normalize!(
|
189
|
+
def normalize!(hash_pattern)
|
188
190
|
self.class.normalize!(hash_pattern, @options)
|
189
191
|
end
|
190
192
|
end # RDF::Query::HashPatternNormalizer
|
data/lib/rdf/query/pattern.rb
CHANGED
@@ -17,7 +17,7 @@ module RDF; class Query
|
|
17
17
|
end
|
18
18
|
|
19
19
|
##
|
20
|
-
# @overload initialize(
|
20
|
+
# @overload initialize(options = {})
|
21
21
|
# @param [Hash{Symbol => Object}] options
|
22
22
|
# @option options [Variable, Resource, Symbol, nil] :subject (nil)
|
23
23
|
# @option options [Variable, URI, Symbol, nil] :predicate (nil)
|
@@ -26,7 +26,7 @@ module RDF; class Query
|
|
26
26
|
# A graph_name of nil matches any graph, a graph_name of false, matches only the default graph.
|
27
27
|
# @option options [Boolean] :optional (false)
|
28
28
|
#
|
29
|
-
# @overload initialize(subject, predicate, object,
|
29
|
+
# @overload initialize(subject, predicate, object, options = {})
|
30
30
|
# @param [Variable, Resource, Symbol, nil] subject
|
31
31
|
# @param [Variable, URI, Symbol, nil] predicate
|
32
32
|
# @param [Variable, Termm, Symbol, nil] object
|
@@ -50,10 +50,12 @@ module RDF; class Query
|
|
50
50
|
|
51
51
|
# Estmate cost positionally, with variables being least expensive as objects, then predicates, then subjects, then graph_names.
|
52
52
|
# XXX does not consider bound variables, which would need to be dynamically calculated.
|
53
|
-
@cost = (@object.nil? || @object.is_a?(Variable) ?
|
54
|
-
(@predicate.nil? || @predicate.is_a?(Variable) ?
|
55
|
-
(@subject.nil? || @subject.is_a?(Variable) ?
|
56
|
-
(@graph_name.is_a?(Variable) ?
|
53
|
+
@cost = (@object.nil? || @object.is_a?(Variable) ? 8 : 0) +
|
54
|
+
(@predicate.nil? || @predicate.is_a?(Variable) ? 4 : 0) +
|
55
|
+
(@subject.nil? || @subject.is_a?(Variable) ? 2 : 0) +
|
56
|
+
(@graph_name.is_a?(Variable) ? 1 : 0) +
|
57
|
+
(@object.is_a?(Pattern) ? (@object.cost * 4) : 0) +
|
58
|
+
(@subject.is_a?(Pattern) ? (@subject.cost * 2) : 0)
|
57
59
|
super
|
58
60
|
end
|
59
61
|
|
@@ -84,10 +86,10 @@ module RDF; class Query
|
|
84
86
|
# @return [Boolean] `true` or `false`
|
85
87
|
# @since 0.3.0
|
86
88
|
def has_variables?
|
87
|
-
subject.
|
88
|
-
|
89
|
-
|
90
|
-
|
89
|
+
subject && subject.variable? ||
|
90
|
+
predicate && predicate.variable? ||
|
91
|
+
object && object.variable? ||
|
92
|
+
graph_name && graph_name.variable?
|
91
93
|
end
|
92
94
|
alias_method :variables?, :has_variables?
|
93
95
|
|
@@ -117,13 +119,33 @@ module RDF; class Query
|
|
117
119
|
false
|
118
120
|
end
|
119
121
|
|
122
|
+
##
|
123
|
+
# Checks pattern equality against a statement, considering nesting.
|
124
|
+
#
|
125
|
+
# * A pattern which has a pattern as a subject or an object, matches
|
126
|
+
# a statement having a statement as a subject or an object using {#eql?}.
|
127
|
+
#
|
128
|
+
# @param [Statement] other
|
129
|
+
# @return [Boolean]
|
130
|
+
#
|
131
|
+
# @see RDF::URI#==
|
132
|
+
# @see RDF::Node#==
|
133
|
+
# @see RDF::Literal#==
|
134
|
+
# @see RDF::Query::Variable#==
|
135
|
+
def eql?(other)
|
136
|
+
return false unless other.is_a?(Statement) && (self.graph_name || false) == (other.graph_name || false)
|
137
|
+
|
138
|
+
predicate == other.predicate &&
|
139
|
+
(subject.is_a?(Pattern) ? subject.eql?(other.subject) : subject == other.subject) &&
|
140
|
+
(object.is_a?(Pattern) ? object.eql?(other.object) : object == other.object)
|
141
|
+
end
|
142
|
+
|
120
143
|
##
|
121
144
|
# Executes this query pattern on the given `queryable` object.
|
122
145
|
#
|
123
146
|
# Values are matched using using Queryable#query_pattern.
|
124
147
|
#
|
125
|
-
# If the optional `bindings` are given, variables will be substituted with their values
|
126
|
-
# when executing the query.
|
148
|
+
# If the optional `bindings` are given, variables will be substituted with their values when executing the query.
|
127
149
|
#
|
128
150
|
# To match triples only in the default graph, set graph_name to `false`.
|
129
151
|
#
|
@@ -198,6 +220,8 @@ module RDF; class Query
|
|
198
220
|
solution[predicate.to_sym] = statement.predicate if predicate.is_a?(Variable)
|
199
221
|
solution[object.to_sym] = statement.object if object.is_a?(Variable)
|
200
222
|
solution[graph_name.to_sym] = statement.graph_name if graph_name.is_a?(Variable)
|
223
|
+
solution.merge!(subject.solution(statement.subject)) if subject.is_a?(Pattern)
|
224
|
+
solution.merge!(object.solution(statement.object)) if object.is_a?(Pattern)
|
201
225
|
end
|
202
226
|
end
|
203
227
|
|
@@ -229,7 +253,8 @@ module RDF; class Query
|
|
229
253
|
# @return [Integer] (0..3)
|
230
254
|
def variable_count
|
231
255
|
[subject, predicate, object, graph_name].inject(0) do |memo, term|
|
232
|
-
memo += (term.is_a?(Variable) ? 1 :
|
256
|
+
memo += (term.is_a?(Variable) ? 1 :
|
257
|
+
(term.is_a?(Pattern) ? term.variable_count : 0))
|
233
258
|
end
|
234
259
|
end
|
235
260
|
alias_method :cardinality, :variable_count
|
@@ -243,7 +268,7 @@ module RDF; class Query
|
|
243
268
|
# @return [Hash{Symbol => Variable}]
|
244
269
|
def variables
|
245
270
|
[subject, predicate, object, graph_name].inject({}) do |memo, term|
|
246
|
-
term.
|
271
|
+
term && term.variable? ? memo.merge(term.variables) : memo
|
247
272
|
end
|
248
273
|
end
|
249
274
|
|
@@ -254,8 +279,10 @@ module RDF; class Query
|
|
254
279
|
# @return [self]
|
255
280
|
def bind(solution)
|
256
281
|
self.to_quad.each_with_index do |term, index|
|
257
|
-
if term
|
282
|
+
if term.is_a?(Variable) && solution[term]
|
258
283
|
self[index] = solution[term]
|
284
|
+
elsif term.is_a?(Pattern)
|
285
|
+
term.bind(solution)
|
259
286
|
end
|
260
287
|
end
|
261
288
|
self
|
@@ -283,9 +310,9 @@ module RDF; class Query
|
|
283
310
|
# @return [Hash{Symbol => RDF::Term}]
|
284
311
|
def bindings
|
285
312
|
bindings = {}
|
286
|
-
bindings.merge!(subject.bindings) if subject.
|
313
|
+
bindings.merge!(subject.bindings) if subject && subject.variable?
|
287
314
|
bindings.merge!(predicate.bindings) if predicate.is_a?(Variable)
|
288
|
-
bindings.merge!(object.bindings) if object.
|
315
|
+
bindings.merge!(object.bindings) if object && object.variable?
|
289
316
|
bindings.merge!(graph_name.bindings) if graph_name.is_a?(Variable)
|
290
317
|
bindings
|
291
318
|
end
|
@@ -330,7 +357,13 @@ module RDF; class Query
|
|
330
357
|
StringIO.open do |buffer| # FIXME in RDF::Statement
|
331
358
|
buffer << 'OPTIONAL ' if optional?
|
332
359
|
buffer << [subject, predicate, object].map do |r|
|
333
|
-
r.is_a?(RDF::Query::Variable)
|
360
|
+
if r.is_a?(RDF::Query::Variable)
|
361
|
+
r.to_s
|
362
|
+
elsif r.is_a?(RDF::Query::Pattern)
|
363
|
+
"<<#{r.to_s[0..-3]}>>"
|
364
|
+
else
|
365
|
+
RDF::NTriples.serialize(r)
|
366
|
+
end
|
334
367
|
end.join(" ")
|
335
368
|
buffer << case graph_name
|
336
369
|
when nil, false then " ."
|