openlogic-rdf 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/AUTHORS +3 -0
  2. data/CREDITS +9 -0
  3. data/README +361 -0
  4. data/UNLICENSE +24 -0
  5. data/VERSION +1 -0
  6. data/bin/rdf +18 -0
  7. data/etc/doap.nt +62 -0
  8. data/lib/df.rb +1 -0
  9. data/lib/rdf/cli.rb +200 -0
  10. data/lib/rdf/format.rb +383 -0
  11. data/lib/rdf/mixin/countable.rb +39 -0
  12. data/lib/rdf/mixin/durable.rb +31 -0
  13. data/lib/rdf/mixin/enumerable.rb +637 -0
  14. data/lib/rdf/mixin/indexable.rb +26 -0
  15. data/lib/rdf/mixin/inferable.rb +5 -0
  16. data/lib/rdf/mixin/mutable.rb +191 -0
  17. data/lib/rdf/mixin/queryable.rb +265 -0
  18. data/lib/rdf/mixin/readable.rb +15 -0
  19. data/lib/rdf/mixin/type_check.rb +21 -0
  20. data/lib/rdf/mixin/writable.rb +152 -0
  21. data/lib/rdf/model/graph.rb +263 -0
  22. data/lib/rdf/model/list.rb +731 -0
  23. data/lib/rdf/model/literal/boolean.rb +121 -0
  24. data/lib/rdf/model/literal/date.rb +73 -0
  25. data/lib/rdf/model/literal/datetime.rb +72 -0
  26. data/lib/rdf/model/literal/decimal.rb +86 -0
  27. data/lib/rdf/model/literal/double.rb +189 -0
  28. data/lib/rdf/model/literal/integer.rb +126 -0
  29. data/lib/rdf/model/literal/numeric.rb +184 -0
  30. data/lib/rdf/model/literal/time.rb +87 -0
  31. data/lib/rdf/model/literal/token.rb +47 -0
  32. data/lib/rdf/model/literal/xml.rb +39 -0
  33. data/lib/rdf/model/literal.rb +373 -0
  34. data/lib/rdf/model/node.rb +156 -0
  35. data/lib/rdf/model/resource.rb +28 -0
  36. data/lib/rdf/model/statement.rb +296 -0
  37. data/lib/rdf/model/term.rb +77 -0
  38. data/lib/rdf/model/uri.rb +570 -0
  39. data/lib/rdf/model/value.rb +133 -0
  40. data/lib/rdf/nquads.rb +152 -0
  41. data/lib/rdf/ntriples/format.rb +48 -0
  42. data/lib/rdf/ntriples/reader.rb +239 -0
  43. data/lib/rdf/ntriples/writer.rb +219 -0
  44. data/lib/rdf/ntriples.rb +104 -0
  45. data/lib/rdf/query/pattern.rb +329 -0
  46. data/lib/rdf/query/solution.rb +252 -0
  47. data/lib/rdf/query/solutions.rb +237 -0
  48. data/lib/rdf/query/variable.rb +221 -0
  49. data/lib/rdf/query.rb +404 -0
  50. data/lib/rdf/reader.rb +511 -0
  51. data/lib/rdf/repository.rb +389 -0
  52. data/lib/rdf/transaction.rb +161 -0
  53. data/lib/rdf/util/aliasing.rb +63 -0
  54. data/lib/rdf/util/cache.rb +139 -0
  55. data/lib/rdf/util/file.rb +38 -0
  56. data/lib/rdf/util/uuid.rb +36 -0
  57. data/lib/rdf/util.rb +6 -0
  58. data/lib/rdf/version.rb +19 -0
  59. data/lib/rdf/vocab/cc.rb +18 -0
  60. data/lib/rdf/vocab/cert.rb +13 -0
  61. data/lib/rdf/vocab/dc.rb +63 -0
  62. data/lib/rdf/vocab/dc11.rb +23 -0
  63. data/lib/rdf/vocab/doap.rb +45 -0
  64. data/lib/rdf/vocab/exif.rb +168 -0
  65. data/lib/rdf/vocab/foaf.rb +69 -0
  66. data/lib/rdf/vocab/geo.rb +13 -0
  67. data/lib/rdf/vocab/http.rb +26 -0
  68. data/lib/rdf/vocab/owl.rb +59 -0
  69. data/lib/rdf/vocab/rdfs.rb +17 -0
  70. data/lib/rdf/vocab/rsa.rb +12 -0
  71. data/lib/rdf/vocab/rss.rb +14 -0
  72. data/lib/rdf/vocab/sioc.rb +93 -0
  73. data/lib/rdf/vocab/skos.rb +36 -0
  74. data/lib/rdf/vocab/wot.rb +21 -0
  75. data/lib/rdf/vocab/xhtml.rb +9 -0
  76. data/lib/rdf/vocab/xsd.rb +58 -0
  77. data/lib/rdf/vocab.rb +215 -0
  78. data/lib/rdf/writer.rb +475 -0
  79. data/lib/rdf.rb +192 -0
  80. metadata +173 -0
data/lib/rdf/query.rb ADDED
@@ -0,0 +1,404 @@
1
+ module RDF
2
+ ##
3
+ # An RDF basic graph pattern (BGP) query.
4
+ #
5
+ # Named queries either match against a specifically named
6
+ # contexts if the name is an RDF::Term or bound RDF::Query::Variable.
7
+ # Names that are against unbound variables match either default
8
+ # or named contexts.
9
+ # The name of `false' will only match against the default context.
10
+ #
11
+ # Variable names cause the variable to be added to the solution set
12
+ # elements.
13
+ #
14
+ # @example Constructing a basic graph pattern query (1)
15
+ # query = RDF::Query.new do
16
+ # pattern [:person, RDF.type, FOAF.Person]
17
+ # pattern [:person, FOAF.name, :name]
18
+ # pattern [:person, FOAF.mbox, :email]
19
+ # end
20
+ #
21
+ # @example Constructing a basic graph pattern query (2)
22
+ # query = RDF::Query.new({
23
+ # :person => {
24
+ # RDF.type => FOAF.Person,
25
+ # FOAF.name => :name,
26
+ # FOAF.mbox => :email,
27
+ # }
28
+ # })
29
+ #
30
+ # @example Executing a basic graph pattern query
31
+ # graph = RDF::Graph.load('etc/doap.nt')
32
+ # query.execute(graph).each do |solution|
33
+ # puts solution.inspect
34
+ # end
35
+ #
36
+ # @example Constructing and executing a query in one go (1)
37
+ # solutions = RDF::Query.execute(graph) do
38
+ # pattern [:person, RDF.type, FOAF.Person]
39
+ # end
40
+ #
41
+ # @example Constructing and executing a query in one go (2)
42
+ # solutions = RDF::Query.execute(graph, {
43
+ # :person => {
44
+ # RDF.type => FOAF.Person,
45
+ # }
46
+ # })
47
+ #
48
+ # @example In this example, the default graph contains the names of the publishers of two named graphs. The triples in the named graphs are not visible in the default graph in this example.
49
+ # # default graph
50
+ # @prefix dc: <http://purl.org/dc/elements/1.1/
51
+ #
52
+ # <http://example.org/bob> dc:publisher "Bob" .
53
+ # <http://example.org/alice> dc:publisher "Alice" .
54
+ #
55
+ # # Named graph: http://example.org/bob
56
+ # @prefix foaf: <http://xmlns.com/foaf/0.1/> .
57
+ #
58
+ # _:a foaf:name "Bob" .
59
+ # _:a foaf:mbox <mailto:bob@oldcorp.example.org> .
60
+ #
61
+ # # Named graph: http://example.org/alice
62
+ # @prefix foaf: <http://xmlns.com/foaf/0.1/> .
63
+ #
64
+ # _:a foaf:name "Alice" .
65
+ # _:a foaf:mbox <mailto:alice@work.example.org> .
66
+ #
67
+ #
68
+ # @see http://www.w3.org/TR/rdf-sparql-query/#rdfDataset
69
+ # @since 0.3.0
70
+ class Query
71
+ autoload :Pattern, 'rdf/query/pattern'
72
+ autoload :Solution, 'rdf/query/solution'
73
+ autoload :Solutions, 'rdf/query/solutions'
74
+ autoload :Variable, 'rdf/query/variable'
75
+
76
+ ##
77
+ # Executes a query on the given `queryable` graph or repository.
78
+ #
79
+ # @param [RDF::Queryable] queryable
80
+ # the graph or repository to query
81
+ # @param [Hash{Object => Object}] patterns
82
+ # optional hash patterns to initialize the query with
83
+ # @param [Hash{Symbol => Object}] options
84
+ # any additional keyword options (see {RDF::Query#initialize})
85
+ # @yield [query]
86
+ # @yieldparam [RDF::Query] query
87
+ # @yieldreturn [void] ignored
88
+ # @return [RDF::Query::Solutions]
89
+ # the resulting solution sequence
90
+ # @see RDF::Query#execute
91
+ def self.execute(queryable, patterns = nil, options = {}, &block)
92
+ self.new(patterns, options, &block).execute(queryable, options)
93
+ end
94
+
95
+ ##
96
+ # The variables used in this query.
97
+ #
98
+ # @return [Hash{Symbol => RDF::Query::Variable}]
99
+ attr_reader :variables
100
+
101
+ ##
102
+ # The patterns that constitute this query.
103
+ #
104
+ # @return [Array<RDF::Query::Pattern>]
105
+ attr_reader :patterns
106
+
107
+ ##
108
+ # The solution sequence for this query.
109
+ #
110
+ # @return [RDF::Query::Solutions]
111
+ attr_reader :solutions
112
+
113
+ ##
114
+ # Any additional options for this query.
115
+ #
116
+ # @return [Hash]
117
+ attr_reader :options
118
+
119
+ ##
120
+ # Initializes a new basic graph pattern query.
121
+ #
122
+ # @overload initialize(patterns = [], options = {})
123
+ # @param [Array<RDF::Query::Pattern>] patterns
124
+ # ...
125
+ # @param [Hash{Symbol => Object}] options
126
+ # any additional keyword options
127
+ # @option options [RDF::Query::Solutions] :solutions (Solutions.new)
128
+ # @option options [RDF::Term, RDF::Query::Variable, Boolean] :context (nil)
129
+ # Default context for matching against queryable.
130
+ # Named queries either match against a specifically named
131
+ # contexts if the name is an RDF::Term or bound RDF::Query::Variable.
132
+ # Names that are against unbound variables match either detault
133
+ # or named contexts.
134
+ # The name of `false' will only match against the default context.
135
+ # @yield [query]
136
+ # @yieldparam [RDF::Query] query
137
+ # @yieldreturn [void] ignored
138
+ #
139
+ # @overload initialize(patterns, options = {})
140
+ # @param [Hash{Object => Object}] patterns
141
+ # ...
142
+ # @param [Hash{Symbol => Object}] options
143
+ # any additional keyword options
144
+ # @option options [RDF::Query::Solutions] :solutions (Solutions.new)
145
+ # @option options [RDF::Term, RDF::Query::Variable, Boolean] :context (nil)
146
+ # Default context for matching against queryable.
147
+ # Named queries either match against a specifically named
148
+ # contexts if the name is an RDF::Term or bound RDF::Query::Variable.
149
+ # Names that are against unbound variables match either detault
150
+ # or named contexts.
151
+ # @yield [query]
152
+ # @yieldparam [RDF::Query] query
153
+ # @yieldreturn [void] ignored
154
+ def initialize(*patterns, &block)
155
+ @options = patterns.last.is_a?(Hash) ? patterns.pop.dup : {}
156
+ patterns << @options if patterns.empty?
157
+ @variables = {}
158
+ @solutions = @options.delete(:solutions) || Solutions.new
159
+ context = @options.delete(:context)
160
+
161
+ @patterns = case patterns.first
162
+ when Hash then compile_hash_patterns(patterns.first.dup)
163
+ when Array then patterns.first
164
+ else patterns
165
+ end
166
+
167
+ self.context = context
168
+
169
+ if block_given?
170
+ case block.arity
171
+ when 1 then block.call(self)
172
+ else instance_eval(&block)
173
+ end
174
+ end
175
+ end
176
+
177
+ ##
178
+ # Appends the given query `pattern` to this query.
179
+ #
180
+ # @param [RDF::Query::Pattern] pattern
181
+ # a triple query pattern
182
+ # @return [void] self
183
+ def <<(pattern)
184
+ @patterns << Pattern.from(pattern)
185
+ self
186
+ end
187
+
188
+ ##
189
+ # Appends the given query `pattern` to this query.
190
+ #
191
+ # @param [RDF::Query::Pattern] pattern
192
+ # a triple query pattern
193
+ # @param [Hash{Symbol => Object}] options
194
+ # any additional keyword options
195
+ # @option options [Boolean] :optional (false)
196
+ # whether this is an optional pattern
197
+ # @return [void] self
198
+ def pattern(pattern, options = {})
199
+ @patterns << Pattern.from(pattern, options)
200
+ self
201
+ end
202
+
203
+ ##
204
+ # Returns an optimized copy of this query.
205
+ #
206
+ # @param [Hash{Symbol => Object}] options
207
+ # any additional options for optimization
208
+ # @return [RDF::Query] a copy of `self`
209
+ # @since 0.3.0
210
+ def optimize(options = {})
211
+ self.dup.optimize!(options)
212
+ end
213
+
214
+ ##
215
+ # Optimizes this query by reordering its constituent triple patterns
216
+ # according to their cost estimates.
217
+ #
218
+ # @param [Hash{Symbol => Object}] options
219
+ # any additional options for optimization
220
+ # @return [void] `self`
221
+ # @see RDF::Query::Pattern#cost
222
+ # @since 0.3.0
223
+ def optimize!(options = {})
224
+ @patterns.sort! do |a, b|
225
+ (a.cost || 0) <=> (b.cost || 0)
226
+ end
227
+ self
228
+ end
229
+
230
+ ##
231
+ # Executes this query on the given `queryable` graph or repository.
232
+ #
233
+ # Named queries either match against a specifically named
234
+ # contexts if the name is an RDF::Term or bound RDF::Query::Variable.
235
+ # Names that are against unbound variables match either detault
236
+ # or named contexts.
237
+ # The name of `false' will only match against the default context.
238
+ #
239
+ # @param [RDF::Queryable] queryable
240
+ # the graph or repository to query
241
+ # @param [Hash{Symbol => Object}] options
242
+ # any additional keyword options
243
+ # @option options [Hash{Symbol => RDF::Term}] bindings
244
+ # optional variable bindings to use
245
+ # @option options [Hash{Symbol => RDF::Term}] solutions
246
+ # optional initial solutions for chained queries
247
+ # @return [RDF::Query::Solutions]
248
+ # the resulting solution sequence
249
+ # @see http://www.holygoat.co.uk/blog/entry/2005-10-25-1
250
+ def execute(queryable, options = {})
251
+ options = options.dup
252
+
253
+ # just so we can call #keys below without worrying
254
+ options[:bindings] ||= {}
255
+
256
+ # Use provided solutions to allow for query chaining
257
+ # Otherwise, a quick empty solution simplifies the logic below; no special case for
258
+ # the first pattern
259
+ @solutions = options[:solutions] || (Solutions.new << RDF::Query::Solution.new({}))
260
+
261
+ patterns = @patterns
262
+
263
+ # Add context to pattern, if necessary
264
+ unless self.context.nil?
265
+ if patterns.empty?
266
+ patterns = [Pattern.new(nil, nil, nil, :context => self.context)]
267
+ elsif patterns.first.context.nil?
268
+ patterns.first.context = self.context
269
+ end
270
+ end
271
+
272
+ patterns.each do |pattern|
273
+
274
+ old_solutions, @solutions = @solutions, Solutions.new
275
+
276
+ options[:bindings].keys.each do |variable|
277
+ if pattern.variables.include?(variable)
278
+ unbound_solutions, old_solutions = old_solutions, Solutions.new
279
+ options[:bindings][variable].each do |binding|
280
+ unbound_solutions.each do |solution|
281
+ old_solutions << solution.merge(variable => binding)
282
+ end
283
+ end
284
+ options[:bindings].delete(variable)
285
+ end
286
+ end
287
+
288
+ old_solutions.each do |solution|
289
+ pattern.execute(queryable, solution) do |statement|
290
+ @solutions << solution.merge(pattern.solution(statement))
291
+ end
292
+ end
293
+
294
+ #puts "solutions after #{pattern} are #{@solutions.to_a.inspect}"
295
+
296
+ # It's important to abort failed queries quickly because later patterns
297
+ # that can have constraints are often broad without them.
298
+ # We have no solutions at all:
299
+ return @solutions if @solutions.empty?
300
+ # We have no solutions for variables we should have solutions for:
301
+ if !pattern.optional? && pattern.variables.keys.any? { |variable| !@solutions.variable_names.include?(variable) }
302
+ return Solutions.new
303
+ end
304
+ end
305
+ @solutions
306
+ end
307
+
308
+ ##
309
+ # Returns `true` if this query did not match when last executed.
310
+ #
311
+ # When the solution sequence is empty, this method can be used to
312
+ # determine whether the query failed to match or not.
313
+ #
314
+ # @return [Boolean]
315
+ # @see #matched?
316
+ def failed?
317
+ @solutions.empty?
318
+ end
319
+
320
+ ##
321
+ # Returns `true` if this query matched when last executed.
322
+ #
323
+ # When the solution sequence is empty, this method can be used to
324
+ # determine whether the query matched successfully or not.
325
+ #
326
+ # @return [Boolean]
327
+ # @see #failed?
328
+ def matched?
329
+ !@failed
330
+ end
331
+
332
+ # Add patterns from another query to form a new Query
333
+ # @param [RDF::Query] other
334
+ # @return [RDF::Query]
335
+ def +(other)
336
+ Query.new(self.patterns + other.patterns)
337
+ end
338
+
339
+ # Is this is a named query?
340
+ # @return [Boolean]
341
+ def named?
342
+ !!options[:context]
343
+ end
344
+
345
+ # Is this is an unamed query?
346
+ # @return [Boolean]
347
+ def unnamed?
348
+ !named?
349
+ end
350
+
351
+ # Add name to query
352
+ # @param [RDF::Value] value
353
+ # @return [RDF::Value]
354
+ def context=(value)
355
+ options[:context] = value
356
+ end
357
+
358
+ # Name of this query, if any
359
+ # @return [RDF::Value]
360
+ def context
361
+ options[:context]
362
+ end
363
+
364
+ # Query has no patterns
365
+ # @return [Boolean]
366
+ def empty?
367
+ patterns.empty?
368
+ end
369
+
370
+ ##
371
+ # Enumerates over each matching query solution.
372
+ #
373
+ # @yield [solution]
374
+ # @yieldparam [RDF::Query::Solution] solution
375
+ # @return [Enumerator]
376
+ def each_solution(&block)
377
+ @solutions.each(&block)
378
+ end
379
+ alias_method :each, :each_solution
380
+
381
+ protected
382
+
383
+ ##
384
+ # @private
385
+ def compile_hash_patterns(hash_patterns)
386
+ patterns = []
387
+ hash_patterns.each do |s, pos|
388
+ raise ArgumentError, "invalid hash pattern: #{hash_patterns.inspect}" unless pos.is_a?(Hash)
389
+ pos.each do |p, os|
390
+ case os
391
+ when Hash
392
+ patterns += os.keys.map { |o| [s, p, o] }
393
+ patterns += compile_hash_patterns(os)
394
+ when Array
395
+ patterns += os.map { |o| [s, p, o] }
396
+ else
397
+ patterns << [s, p, os]
398
+ end
399
+ end
400
+ end
401
+ patterns.map { |pattern| Pattern.from(pattern) }
402
+ end
403
+ end # Query
404
+ end # RDF