rdf 3.0.11 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 " ."
|