grel 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/grel.rb +85 -0
- data/lib/grel/base.rb +373 -0
- data/lib/grel/ql.rb +763 -0
- metadata +5 -2
data/lib/grel.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'stardog'
|
2
|
+
require 'time'
|
3
|
+
require 'uri'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'debugger'
|
6
|
+
|
7
|
+
class Array
|
8
|
+
def triples_id
|
9
|
+
self.first.first
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module GRel
|
14
|
+
|
15
|
+
class ValidationError < Stardog::ICVException
|
16
|
+
attr_accessor :icv_exception
|
17
|
+
def initialize(msg, exception)
|
18
|
+
super(msg)
|
19
|
+
@icv_exception = exception
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
DEBUG = ENV["GREL_DEBUG"] || false
|
24
|
+
|
25
|
+
class Debugger
|
26
|
+
def self.debug(msg)
|
27
|
+
puts msg if DEBUG
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
NAMESPACE = "http://grel.org/vocabulary#"
|
32
|
+
ID_REGEX = /^\@id\((\w+)\)$/
|
33
|
+
NIL = "\"http://www.w3.org/1999/02/22-rdf-syntax-ns#nil\""
|
34
|
+
BNODE = "BNODE"
|
35
|
+
|
36
|
+
class NonNegativeInteger
|
37
|
+
|
38
|
+
def initialize(number)
|
39
|
+
@number = number
|
40
|
+
end
|
41
|
+
|
42
|
+
def method_missing(name, *args, &blk)
|
43
|
+
ret = @number.send(name, *args, &blk)
|
44
|
+
ret.is_a?(Numeric) ? MyNum.new(ret) : ret
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
"\"#{@number}\"^^<http://www.w3.org/2001/XMLSchema#nonNegativeInteger>"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class BlankId
|
54
|
+
|
55
|
+
attr_reader :blank_id
|
56
|
+
|
57
|
+
def initialize
|
58
|
+
@blank_id = BlankId.next_id
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.next_id
|
62
|
+
next_id = (@counter ||= 0)
|
63
|
+
@counter += 1
|
64
|
+
next_id
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
"_:#{@blank_id}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def graph(name='http://localhost:5822/',options = {})
|
73
|
+
options[:user] ||= "admin"
|
74
|
+
options[:password] ||= "admin"
|
75
|
+
options[:validate] ||= false
|
76
|
+
g = Base.new(name, options)
|
77
|
+
g.with_db(options[:db]) if(options[:db])
|
78
|
+
g
|
79
|
+
end
|
80
|
+
|
81
|
+
end # end of module GRel
|
82
|
+
|
83
|
+
# remaining modules
|
84
|
+
require File.join(File.dirname(__FILE__), "grel", "ql")
|
85
|
+
require File.join(File.dirname(__FILE__), "grel", "base")
|
data/lib/grel/base.rb
ADDED
@@ -0,0 +1,373 @@
|
|
1
|
+
module GRel
|
2
|
+
|
3
|
+
class Base
|
4
|
+
|
5
|
+
include Stardog
|
6
|
+
|
7
|
+
attr_accessor :stardog, :last_query_context
|
8
|
+
attr_reader :db_name, :schema_graph
|
9
|
+
|
10
|
+
def initialize(endpoint, options)
|
11
|
+
@options = options
|
12
|
+
@endpoint = endpoint
|
13
|
+
@stardog = Stardog::stardog(endpoint,options)
|
14
|
+
@validations = options[:validate] || false
|
15
|
+
@dbs = @stardog.list_dbs.body["databases"]
|
16
|
+
@reasoning = false
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def with_reasoning(reasoning="QL")
|
21
|
+
@reasoning = true
|
22
|
+
@stardog = Stardog::stardog(@endpoint,@options.merge(:reasoning => reasoning))
|
23
|
+
@stardog.offline_db(@db_name)
|
24
|
+
@stardog.set_db_options(@db_name, "icv.reasoning.type" => reasoning)
|
25
|
+
@stardog.online_db(@db_name, 'WAIT')
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def without_reasoning
|
30
|
+
@reasoning = false
|
31
|
+
@stardog = Stardog::stardog(@endpoint,@options)
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def with_db(db_name)
|
36
|
+
ensure_db(db_name) do
|
37
|
+
old_db_name = @db_name
|
38
|
+
@db_name = db_name
|
39
|
+
@schema_graph = "#{db_name}:schema"
|
40
|
+
if block_given?
|
41
|
+
yield
|
42
|
+
@db_name = old_db_name
|
43
|
+
end
|
44
|
+
end
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def store(data, db_name=nil)
|
50
|
+
if(db_name)
|
51
|
+
with_db(db_name) do
|
52
|
+
store(data)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
GRel::Debugger.debug "STORING"
|
56
|
+
GRel::Debugger.debug QL.to_turtle(data)
|
57
|
+
GRel::Debugger.debug "IN"
|
58
|
+
GRel::Debugger.debug @db_name
|
59
|
+
@stardog.add(@db_name, QL.to_turtle(data), nil, "text/turtle")
|
60
|
+
end
|
61
|
+
self
|
62
|
+
rescue Stardog::ICVException => ex
|
63
|
+
raise ValidationError.new("Error storing objects in the graph. A Validation has failed.", ex)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def where(query)
|
68
|
+
@last_query_context = QL::QueryContext.new(self)
|
69
|
+
@last_query_context.register_query(query)
|
70
|
+
@last_query_context = QL.to_query(query, @last_query_context)
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def union(query)
|
75
|
+
union_context = QL::QueryContext.new(self)
|
76
|
+
union_context.register_query(query)
|
77
|
+
union_context = QL.to_query(query, union_context)
|
78
|
+
|
79
|
+
@last_query_context.union(union_context)
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
def limit(limit)
|
84
|
+
@last_query_context.limit = limit
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
def offset(offset)
|
89
|
+
@last_query_context.offset = offset
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
def order(order)
|
94
|
+
@last_query_context.order = order
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
def run
|
99
|
+
@last_query_context.run
|
100
|
+
end
|
101
|
+
|
102
|
+
def define(*args)
|
103
|
+
unless(args.length == 3 && !args.first.is_a?(Array))
|
104
|
+
args = args.inject([]) {|a,i| a += i; a }
|
105
|
+
end
|
106
|
+
|
107
|
+
args = parse_schema_axioms(args)
|
108
|
+
|
109
|
+
triples = QL.to_turtle(args, true)
|
110
|
+
GRel::Debugger.debug "STORING IN SCHEMA #{@schema_graph}"
|
111
|
+
GRel::Debugger.debug triples
|
112
|
+
GRel::Debugger.debug "IN"
|
113
|
+
GRel::Debugger.debug @db_name
|
114
|
+
@stardog.add(@db_name, triples, @schema_graph, "text/turtle")
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
def retract_definition(*args)
|
119
|
+
unless(args.length == 3 && !args.first.is_a?(Array))
|
120
|
+
args = args.inject([]) {|a,i| a += i }
|
121
|
+
end
|
122
|
+
additional_triples = []
|
123
|
+
|
124
|
+
triples = QL.to_turtle(args + additional_triples, true)
|
125
|
+
GRel::Debugger.debug "REMOVING FROM SCHEMA #{@schema_graph}"
|
126
|
+
GRel::Debugger.debug triples
|
127
|
+
GRel::Debugger.debug "IN"
|
128
|
+
GRel::Debugger.debug @db_name
|
129
|
+
@stardog.remove(@db_name, triples, @schema_graph, "text/turtle")
|
130
|
+
self
|
131
|
+
end
|
132
|
+
|
133
|
+
def validate(*args)
|
134
|
+
unless(args.detect{|e| !e.is_a?(Array)})
|
135
|
+
args = args.inject([]) {|a,i| a += i; a }
|
136
|
+
end
|
137
|
+
|
138
|
+
args = parse_schema_axioms(args)
|
139
|
+
additional_triples = []
|
140
|
+
found = args.each_slice(3).detect{|(s,p,o)| p == :@range && o.is_a?(Class)}
|
141
|
+
if(found)
|
142
|
+
additional_triples += [found.first, :@type, :"<http://www.w3.org/2002/07/owl#DatatypeProperty>"]
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
triples = QL.to_turtle(args + additional_triples, true)
|
147
|
+
GRel::Debugger.debug "STORING IN VALIDATIONS #{@schema_graph}"
|
148
|
+
GRel::Debugger.debug triples
|
149
|
+
GRel::Debugger.debug "IN"
|
150
|
+
GRel::Debugger.debug @db_name
|
151
|
+
@stardog.add_icv(@db_name, triples, "text/turtle")
|
152
|
+
self
|
153
|
+
end
|
154
|
+
|
155
|
+
def retract_validation(*args)
|
156
|
+
unless(args.length == 3 && !args.first.is_a?(Array))
|
157
|
+
args = args.inject([]) {|a,i| a += i }
|
158
|
+
end
|
159
|
+
triples = QL.to_turtle(args, true)
|
160
|
+
GRel::Debugger.debug "REMOVING FROM SCHEMA #{@schema_graph}"
|
161
|
+
GRel::Debugger.debug triples
|
162
|
+
GRel::Debugger.debug "IN"
|
163
|
+
GRel::Debugger.debug @db_name
|
164
|
+
@stardog.remove_icv(@db_name, triples, "text/turtle")
|
165
|
+
self
|
166
|
+
end
|
167
|
+
|
168
|
+
def remove(data = nil, options = {})
|
169
|
+
if data
|
170
|
+
GRel::Debugger.debug "REMMOVING"
|
171
|
+
GRel::Debugger.debug QL.to_turtle(data)
|
172
|
+
GRel::Debugger.debug "IN"
|
173
|
+
GRel::Debugger.debug @db_name
|
174
|
+
@stardog.remove(@db_name, QL.to_turtle(data), nil, "text/turtle")
|
175
|
+
else
|
176
|
+
args = {:describe => true}
|
177
|
+
args = {:accept => "application/rdf+xml"}
|
178
|
+
|
179
|
+
sparql = @last_query_context.to_sparql_describe
|
180
|
+
triples = @stardog.query(@db_name,sparql, args).body
|
181
|
+
|
182
|
+
@stardog.remove(@db_name, triples, nil, "application/rdf+xml")
|
183
|
+
end
|
184
|
+
self
|
185
|
+
end
|
186
|
+
|
187
|
+
def all(options = {})
|
188
|
+
unlinked = options[:unlinked] || false
|
189
|
+
|
190
|
+
results = run
|
191
|
+
nodes = QL.from_bindings_to_nodes(results, @last_query_context, :unlinked => unlinked)
|
192
|
+
nodes
|
193
|
+
#sets = @last_query_context.query_keys
|
194
|
+
#nodes.select do |node|
|
195
|
+
# valid = false
|
196
|
+
# c = 0
|
197
|
+
# while(!valid && c<sets.length)
|
198
|
+
# sets_keys, sets_query = sets[c]
|
199
|
+
# valid = (sets_keys.empty?) || sets_keys.inject(true) do |ac,k|
|
200
|
+
# value = nil
|
201
|
+
# if (sets_query[k].is_a?(Hash) || (sets_query[k].is_a?(Symbol)))
|
202
|
+
# value = ac && node[k]
|
203
|
+
# end
|
204
|
+
# if(value.nil? && @reasoning == true)
|
205
|
+
# value = ac && node.values.include?(sets_query[k])
|
206
|
+
# end
|
207
|
+
# if (value.nil? && sets_query[k].is_a?(String) && sets_query[k].index("@id("))
|
208
|
+
# value = ac && node[k]
|
209
|
+
# end
|
210
|
+
# if(value.nil?)
|
211
|
+
# ac && node[k] == sets_query[k]
|
212
|
+
# else
|
213
|
+
# value
|
214
|
+
# end
|
215
|
+
# end
|
216
|
+
# c += 1
|
217
|
+
# end
|
218
|
+
# valid
|
219
|
+
#end
|
220
|
+
end
|
221
|
+
|
222
|
+
def tuples
|
223
|
+
results = run_tuples(@last_query_context.to_sparql_select)
|
224
|
+
results["results"]["bindings"].map do |h|
|
225
|
+
h.keys.each do |k|
|
226
|
+
h[k.to_sym] = QL.from_tuple_binding(h[k])
|
227
|
+
h.delete(k)
|
228
|
+
end
|
229
|
+
h
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def first(options = {})
|
234
|
+
all(options).first
|
235
|
+
end
|
236
|
+
|
237
|
+
def query(query, options = {})
|
238
|
+
GRel::Debugger.debug "QUERYING DESCRIBE..."
|
239
|
+
GRel::Debugger.debug query
|
240
|
+
GRel::Debugger.debug "** LIMIT #{@last_query_context.limit}" if @last_query_context.limit
|
241
|
+
GRel::Debugger.debug "** OFFSET #{@last_query_context.offset}" if @last_query_context.offset
|
242
|
+
GRel::Debugger.debug "----------------------"
|
243
|
+
args = {:describe => true}
|
244
|
+
args[:accept] = options[:accept] if options[:accept]
|
245
|
+
args[:offset] = @last_query_context.offset if @last_query_context.offset
|
246
|
+
args[:limit] = @last_query_context.limit if @last_query_context.limit
|
247
|
+
@stardog.query(@db_name,query, args).body
|
248
|
+
end
|
249
|
+
|
250
|
+
def run_tuples(query, options = {})
|
251
|
+
GRel::Debugger.debug "QUERYING SELECT..."
|
252
|
+
GRel::Debugger.debug query
|
253
|
+
GRel::Debugger.debug "** LIMIT #{@last_query_context.limit}" if @last_query_context.limit
|
254
|
+
GRel::Debugger.debug "** OFFSET #{@last_query_context.offset}" if @last_query_context.offset
|
255
|
+
GRel::Debugger.debug "----------------------"
|
256
|
+
args = {}
|
257
|
+
args[:accept] = options[:accept] if options[:accept]
|
258
|
+
args[:offset] = @last_query_context.offset if @last_query_context.offset
|
259
|
+
args[:limit] = @last_query_context.limit if @last_query_context.limit
|
260
|
+
@stardog.query(@db_name,query, args).body
|
261
|
+
end
|
262
|
+
|
263
|
+
def with_validations(state = true)
|
264
|
+
@validations = state
|
265
|
+
@stardog.offline_db(@db_name)
|
266
|
+
@stardog.set_db_options(@db_name, "icv.enabled" => @validations)
|
267
|
+
@stardog.online_db(@db_name, 'WAIT')
|
268
|
+
|
269
|
+
self
|
270
|
+
end
|
271
|
+
|
272
|
+
def without_validations
|
273
|
+
with_validations(false)
|
274
|
+
end
|
275
|
+
|
276
|
+
private
|
277
|
+
|
278
|
+
def ensure_db(db_name)
|
279
|
+
unless(@dbs.include?(db_name))
|
280
|
+
@stardog.create_db(db_name, :options => { "reasoning.schema.graphs" => "#{db_name}:schema" })
|
281
|
+
@stardog.with_validations(@validations) if @validations == true
|
282
|
+
@dbs << db_name
|
283
|
+
end
|
284
|
+
yield if block_given?
|
285
|
+
end
|
286
|
+
|
287
|
+
def parse_schema_axioms(args)
|
288
|
+
unfolded = []
|
289
|
+
args.each_slice(3) do |(s,p,o)|
|
290
|
+
if(p == :@range && o.is_a?(Class))
|
291
|
+
unfolded += [s, :@type, :"<http://www.w3.org/2002/07/owl#DatatypeProperty>"]
|
292
|
+
elsif(p == :@range)
|
293
|
+
unfolded += [s, :@type, :"<http://www.w3.org/2002/07/owl#ObjectProperty>"]
|
294
|
+
end
|
295
|
+
|
296
|
+
if(p == :@some)
|
297
|
+
restriction = BlankId.new
|
298
|
+
unfolded += [s, :@subclass, restriction]
|
299
|
+
unfolded += [restriction, :@type, :"<http://www.w3.org/2002/07/owl#Restriction>"]
|
300
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#onProperty>", o.first]
|
301
|
+
unfolded += [restriction, :@some,o.last]
|
302
|
+
elsif(p == :@all)
|
303
|
+
restriction = BlankId.new
|
304
|
+
unfolded += [s, :@subclass, restriction]
|
305
|
+
unfolded += [restriction, :@type, :"<http://www.w3.org/2002/07/owl#Restriction>"]
|
306
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#onProperty>", o.first]
|
307
|
+
unfolded += [restriction, :@all,o.last]
|
308
|
+
elsif(p == :@cardinality)
|
309
|
+
property = o[:property]
|
310
|
+
klass = o[:class]
|
311
|
+
exact = o[:exact]
|
312
|
+
min = o[:min]
|
313
|
+
max = o[:max]
|
314
|
+
if klass.nil?
|
315
|
+
if(exact)
|
316
|
+
restriction = BlankId.new
|
317
|
+
unfolded += [s, :@subclass, restriction]
|
318
|
+
unfolded += [restriction, :@type, :"<http://www.w3.org/2002/07/owl#Restriction>"]
|
319
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#onProperty>", property]
|
320
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#cardinality>", NonNegativeInteger.new(exact)]
|
321
|
+
else
|
322
|
+
if(min)
|
323
|
+
restriction = BlankId.new
|
324
|
+
unfolded += [s, :@subclass, restriction]
|
325
|
+
unfolded += [restriction, :@type, :"<http://www.w3.org/2002/07/owl#Restriction>"]
|
326
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#onProperty>", property]
|
327
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#minCardinality>", NonNegativeInteger.new(min)]
|
328
|
+
end
|
329
|
+
if(max)
|
330
|
+
restriction = BlankId.new
|
331
|
+
unfolded += [s, :@subclass, restriction]
|
332
|
+
unfolded += [restriction, :@type, :"<http://www.w3.org/2002/07/owl#Restriction>"]
|
333
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#onProperty>", property]
|
334
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#maxCardinality>", NonNegativeInteger.new(max)]
|
335
|
+
end
|
336
|
+
end
|
337
|
+
else
|
338
|
+
if(exact)
|
339
|
+
restriction = BlankId.new
|
340
|
+
unfolded += [s, :@subclass, restriction]
|
341
|
+
unfolded += [restriction, :@type, :"<http://www.w3.org/2002/07/owl#Restriction>"]
|
342
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#onProperty>", property]
|
343
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#qualifiedCardinality>", NonNegativeInteger.new(exact)]
|
344
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#onClass>", klass]
|
345
|
+
else
|
346
|
+
if(min)
|
347
|
+
restriction = BlankId.new
|
348
|
+
unfolded += [s, :@subclass, restriction]
|
349
|
+
unfolded += [restriction, :@type, :"<http://www.w3.org/2002/07/owl#Restriction>"]
|
350
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#onProperty>", property]
|
351
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#minQualifiedCardinality>", NonNegativeInteger.new(min)]
|
352
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#onClass>", klass]
|
353
|
+
end
|
354
|
+
if(max)
|
355
|
+
restriction = BlankId.new
|
356
|
+
unfolded += [s, :@subclass, restriction]
|
357
|
+
unfolded += [restriction, :@type, :"<http://www.w3.org/2002/07/owl#Restriction>"]
|
358
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#onProperty>", property]
|
359
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#maxQualifiedCardinality>", NonNegativeInteger.new(max)]
|
360
|
+
unfolded += [restriction, :"<http://www.w3.org/2002/07/owl#onClass>", klass]
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
else
|
365
|
+
unfolded += [s,p,o]
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
unfolded
|
370
|
+
end
|
371
|
+
|
372
|
+
end # end of Base class
|
373
|
+
end # end of Grel module
|
data/lib/grel/ql.rb
ADDED
@@ -0,0 +1,763 @@
|
|
1
|
+
module GRel
|
2
|
+
module QL
|
3
|
+
def self.to_id(obj)
|
4
|
+
if(obj =~ ID_REGEX)
|
5
|
+
"<http://grel.org/ids/id/#{URI.encode(ID_REGEX.match(obj)[1])}>"
|
6
|
+
else
|
7
|
+
"<http://grel.org/ids/#{URI.encode(obj)}>"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.to_turtle(obj, schema=false)
|
12
|
+
data = "@prefix : <http://grel.org/vocabulary#> . @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . "
|
13
|
+
data = data + "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . " if schema
|
14
|
+
|
15
|
+
data + QL.to_triples(obj).map{|t| t.map(&:to_s).join(" ") }.join(" .\n ") + " ."
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.to_triples(obj)
|
19
|
+
if(obj.is_a?(BlankId))
|
20
|
+
obj.to_s
|
21
|
+
elsif(obj.is_a?(Symbol))
|
22
|
+
if(obj == :@type)
|
23
|
+
"rdf:type"
|
24
|
+
elsif(obj == :@subclass)
|
25
|
+
"rdfs:subClassOf"
|
26
|
+
elsif(obj == :@subproperty)
|
27
|
+
"rdfs:subPropertyOf"
|
28
|
+
elsif(obj == :@domain)
|
29
|
+
"rdfs:domain"
|
30
|
+
elsif(obj == :@range)
|
31
|
+
"rdfs:range"
|
32
|
+
elsif(obj == :"<http://www.w3.org/2002/07/owl#onClass>")
|
33
|
+
"<http://www.w3.org/2002/07/owl#onClass>"
|
34
|
+
elsif(obj == :"<http://www.w3.org/2002/07/owl#qualifiedCardinality>")
|
35
|
+
"<http://www.w3.org/2002/07/owl#qualifiedCardinality>"
|
36
|
+
elsif(obj == :"<http://www.w3.org/2002/07/owl#minCardinality>")
|
37
|
+
"<http://www.w3.org/2002/07/owl#minCardinality>"
|
38
|
+
elsif(obj == :"<http://www.w3.org/2002/07/owl#maxCardinality>")
|
39
|
+
"<http://www.w3.org/2002/07/owl#maxCardinality>"
|
40
|
+
elsif(obj == :"<http://www.w3.org/2002/07/owl#minQualifiedCardinality>")
|
41
|
+
"<http://www.w3.org/2002/07/owl#minQualifiedCardinality>"
|
42
|
+
elsif(obj == :"<http://www.w3.org/2002/07/owl#maxQualifiedCardinality>")
|
43
|
+
"<http://www.w3.org/2002/07/owl#maxQualifiedCardinality>"
|
44
|
+
elsif(obj == :"<http://www.w3.org/2002/07/owl#cardinality>")
|
45
|
+
"<http://www.w3.org/2002/07/owl#cardinality>"
|
46
|
+
elsif(obj == :@some)
|
47
|
+
"<http://www.w3.org/2002/07/owl#someValuesFrom>"
|
48
|
+
elsif(obj == :@all)
|
49
|
+
"<http://www.w3.org/2002/07/owl#allValuesFrom>"
|
50
|
+
elsif(obj == :"<http://www.w3.org/2002/07/owl#Restriction>")
|
51
|
+
"<http://www.w3.org/2002/07/owl#Restriction>"
|
52
|
+
elsif(obj == :"<http://www.w3.org/2002/07/owl#onProperty>")
|
53
|
+
"<http://www.w3.org/2002/07/owl#onProperty>"
|
54
|
+
elsif(obj == :"<http://www.w3.org/2002/07/owl#DatatypeProperty>")
|
55
|
+
"<http://www.w3.org/2002/07/owl#DatatypeProperty>"
|
56
|
+
elsif(obj == :"<http://www.w3.org/2002/07/owl#ObjectProperty>")
|
57
|
+
"<http://www.w3.org/2002/07/owl#ObjectProperty>"
|
58
|
+
else
|
59
|
+
":#{obj}"
|
60
|
+
end
|
61
|
+
elsif(obj.is_a?(String))
|
62
|
+
if(obj =~ ID_REGEX)
|
63
|
+
QL.to_id(obj)
|
64
|
+
else
|
65
|
+
"\"#{obj}\""
|
66
|
+
end
|
67
|
+
elsif(obj == nil)
|
68
|
+
NIL
|
69
|
+
elsif(obj == Float)
|
70
|
+
"<http://www.w3.org/2001/XMLSchema#float>"
|
71
|
+
elsif(obj == Numeric || obj == Fixnum || obj == "Bignum")
|
72
|
+
"<http://www.w3.org/2001/XMLSchema#integer>"
|
73
|
+
elsif(obj == Time || obj == Date || obj == DateTime)
|
74
|
+
"<http://www.w3.org/2001/XMLSchema#dateTime>"
|
75
|
+
elsif(obj == TrueClass || obj == FalseClass)
|
76
|
+
"<http://www.w3.org/2001/XMLSchema#boolean>"
|
77
|
+
elsif(obj.is_a?(NonNegativeInteger))
|
78
|
+
obj.to_s
|
79
|
+
elsif(obj == true || obj == false)
|
80
|
+
"\"#{obj}\"^^<http://www.w3.org/2001/XMLSchema#boolean>"
|
81
|
+
elsif(obj.is_a?(Float))
|
82
|
+
"\"#{obj}\"^^<http://www.w3.org/2001/XMLSchema#float>"
|
83
|
+
elsif(obj.is_a?(Numeric))
|
84
|
+
"\"#{obj}\"^^<http://www.w3.org/2001/XMLSchema#integer>"
|
85
|
+
elsif(obj.is_a?(Time))
|
86
|
+
"\"#{obj.iso8601}\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"
|
87
|
+
elsif(obj.is_a?(Date))
|
88
|
+
"\"#{Time.new(obj.to_s).iso8601}\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"
|
89
|
+
elsif(obj.is_a?(Array)) # top level array, not array property in a hash
|
90
|
+
if(obj.detect{|e| e.is_a?(Hash) || e.respond_to?(:to_triples) })
|
91
|
+
obj.map{|e| QL.to_triples(e) }.inject([]){|a,i| a += i}
|
92
|
+
else
|
93
|
+
obj.each_slice(3).map do |s|
|
94
|
+
s.map{|e| QL.to_triples(e) }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
elsif(obj.is_a?(Hash))
|
98
|
+
# no blank nodes
|
99
|
+
obj[:@id] = "@id(#{SecureRandom.hex})" if obj[:@id].nil?
|
100
|
+
# normalising id values
|
101
|
+
obj[:@id] = "@id(#{obj[:@id]})" if obj[:@id].index("@id(").nil?
|
102
|
+
|
103
|
+
acum = []
|
104
|
+
triples_acum = []
|
105
|
+
triples_nested = []
|
106
|
+
id = nil
|
107
|
+
obj.each_pair do |k,v|
|
108
|
+
p = QL.to_triples(k)
|
109
|
+
if(v.is_a?(Hash))
|
110
|
+
next_triples = QL.to_triples(v)
|
111
|
+
triples_acum += next_triples
|
112
|
+
v = next_triples.triples_id
|
113
|
+
acum << [p,v] if v && k != :@id
|
114
|
+
elsif(v.is_a?(Array)) # array as a property in a hash
|
115
|
+
v.map{|o| QL.to_triples(o) }.each do |o|
|
116
|
+
if(o.is_a?(Array) && o.length > 0)
|
117
|
+
acum << [p, o[0][0]]
|
118
|
+
triples_nested += o
|
119
|
+
else
|
120
|
+
acum << [p, o]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
else
|
124
|
+
if(k == :@id)
|
125
|
+
id = QL.to_triples(v)
|
126
|
+
else
|
127
|
+
v = QL.to_triples(v)
|
128
|
+
end
|
129
|
+
acum << [p,v] if v && k != :@id
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
id = id || BlankId.new
|
134
|
+
|
135
|
+
triples_acum + acum.map{|(p,o)| [id, p, o] } + triples_nested
|
136
|
+
else
|
137
|
+
if(obj.respond_to?(:to_triples))
|
138
|
+
obj.to_triples
|
139
|
+
else
|
140
|
+
"\"#{obj}\""
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end # end of to_triples
|
144
|
+
|
145
|
+
class QueryContext
|
146
|
+
|
147
|
+
TUPLE = "TUPLE"
|
148
|
+
NODE = "NODE"
|
149
|
+
|
150
|
+
attr_reader :last_registered_subject, :nodes, :projection, :limit, :offset, :order, :orig_query
|
151
|
+
attr_accessor :triples, :optional_triples, :optional, :unions
|
152
|
+
|
153
|
+
def initialize(graph=nil)
|
154
|
+
@id_counter = -1
|
155
|
+
@filters_counter = -1
|
156
|
+
@triples = []
|
157
|
+
@optional_triples = []
|
158
|
+
@optional_bgps = []
|
159
|
+
@projection = {}
|
160
|
+
@nodes = {}
|
161
|
+
@graph = graph
|
162
|
+
@optional = false
|
163
|
+
@last_registered_subject = []
|
164
|
+
@unions = []
|
165
|
+
@limit = nil
|
166
|
+
@offset = nil
|
167
|
+
@orig_query = nil
|
168
|
+
@query_keys = []
|
169
|
+
end
|
170
|
+
|
171
|
+
def register_query(query)
|
172
|
+
@orig_query = query
|
173
|
+
query.each_pair do |k,v|
|
174
|
+
@query_keys << k if(k != :@id && k.to_s.index("$inv_").nil?)
|
175
|
+
end
|
176
|
+
self
|
177
|
+
end
|
178
|
+
|
179
|
+
def query_keys
|
180
|
+
sets = [[@query_keys,@orig_query]]
|
181
|
+
unions.each do |c|
|
182
|
+
sets += c.query_keys
|
183
|
+
end
|
184
|
+
sets
|
185
|
+
end
|
186
|
+
|
187
|
+
def optional=(value)
|
188
|
+
if(value == true)
|
189
|
+
@optional = true
|
190
|
+
else
|
191
|
+
@optional = false
|
192
|
+
@optional_bgps << @optional_triples
|
193
|
+
@optional_triples = []
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def append(triples)
|
198
|
+
if(@optional)
|
199
|
+
@optional_triples += triples
|
200
|
+
else
|
201
|
+
@triples += triples
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def next_node_id
|
206
|
+
@id_counter += 1
|
207
|
+
@id_counter
|
208
|
+
end
|
209
|
+
|
210
|
+
def register_node(id,node_id,inverse=false)
|
211
|
+
@filters_counter = -1
|
212
|
+
subject = id
|
213
|
+
predicate = "?P_mg_#{node_id}"
|
214
|
+
object = "?O_mg_#{node_id}"
|
215
|
+
if(id.nil?)
|
216
|
+
subject = "?S_mg_#{node_id}"
|
217
|
+
@projection[subject] = true if(!inverse)
|
218
|
+
else
|
219
|
+
@projection[id] = true if(!inverse)
|
220
|
+
end
|
221
|
+
|
222
|
+
@last_registered_subject << subject
|
223
|
+
# @projection[predicate] = true
|
224
|
+
# @projection[object] = true
|
225
|
+
@nodes[node_id] = subject
|
226
|
+
|
227
|
+
[subject, predicate, object]
|
228
|
+
end
|
229
|
+
|
230
|
+
def last_registered_subject
|
231
|
+
@last_registered_subject.pop
|
232
|
+
end
|
233
|
+
|
234
|
+
def node_to_optional(node_id) #, last_registered_subject)
|
235
|
+
@projection.delete(node_id)
|
236
|
+
@nodes.delete(node_id)
|
237
|
+
# @last_registered_subject << last_registered_subject
|
238
|
+
end
|
239
|
+
|
240
|
+
def fresh_filter_predicate
|
241
|
+
@filters_counter+=1
|
242
|
+
"?P_#{@id_counter}_#{@filters_counter}"
|
243
|
+
end
|
244
|
+
|
245
|
+
def to_sparql_describe(preamble=true)
|
246
|
+
|
247
|
+
bgps = @triples.map{|t|
|
248
|
+
if(t.is_a?(Filter))
|
249
|
+
t.acum
|
250
|
+
else
|
251
|
+
t.join(' ')
|
252
|
+
end
|
253
|
+
}.join(" . ")
|
254
|
+
|
255
|
+
optional_bgps = @optional_bgps.map{|optional_triples|
|
256
|
+
"OPTIONAL { "+ optional_triples.map { |t|
|
257
|
+
if(t.is_a?(Filter))
|
258
|
+
t.acum
|
259
|
+
else
|
260
|
+
t.join(' ')
|
261
|
+
end
|
262
|
+
}.join(" . ") + " }"
|
263
|
+
}.join(" ")
|
264
|
+
|
265
|
+
main_bgp = "#{bgps}"
|
266
|
+
unless(@optional_bgps.empty?)
|
267
|
+
main_bgp += " #{optional_bgps}"
|
268
|
+
end
|
269
|
+
|
270
|
+
query = if(preamble)
|
271
|
+
query = "PREFIX : <http://grel.org/vocabulary#> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX rdfs: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>"
|
272
|
+
query = query + " PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX fn: <http://www.w3.org/2005/xpath-functions#> "
|
273
|
+
|
274
|
+
if(@unions.length>0)
|
275
|
+
union_bgps = @unions.map{|u| u.to_sparql_describe(false) }.join(" UNION ")
|
276
|
+
union_projections = @unions.map{|u| u.projection.keys }.flatten.uniq
|
277
|
+
all_subjects = (@projection.keys + union_projections).uniq
|
278
|
+
query += "DESCRIBE #{all_subjects.join(' ')} WHERE { { #{main_bgp} } UNION #{union_bgps} }"
|
279
|
+
else
|
280
|
+
query += "DESCRIBE #{@projection.keys.join(' ')} WHERE { #{main_bgp} }"
|
281
|
+
end
|
282
|
+
else
|
283
|
+
"{ #{main_bgp} }"
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def to_sparql_select(preamble=true)
|
288
|
+
|
289
|
+
projection = {}
|
290
|
+
|
291
|
+
bgps = @triples.map{ |t|
|
292
|
+
s,p,o = t
|
293
|
+
if(s.to_s.index("?X_") == 0 && p.to_s.index("?X_") == 0 && o.to_s.index("?X_") == 0)
|
294
|
+
elsif(s.to_s.index("?S_mg") == 0 && p.to_s.index("?P_mg") == 0 && o.to_s.index("?O_mg") == 0)
|
295
|
+
nil
|
296
|
+
else
|
297
|
+
projection[s] = true if((s.to_s.index("?") == 0) && s.to_s.index("?X_").nil? && s.index("_mg_").nil?)
|
298
|
+
projection[p] = true if((p.to_s.index("?") == 0) && p.to_s.index("?X_").nil? && p.index("_mg_").nil?)
|
299
|
+
projection[o] = true if((o.to_s.index("?") == 0) && o.to_s.index("?X_").nil? && o.index("_mg_").nil?)
|
300
|
+
if(t.is_a?(Filter))
|
301
|
+
t.acum
|
302
|
+
else
|
303
|
+
t.join(' ')
|
304
|
+
end
|
305
|
+
end
|
306
|
+
}.compact.join(" . ")
|
307
|
+
|
308
|
+
optional_bgps = @optional_bgps.map{|optional_triples|
|
309
|
+
"OPTIONAL { "+ optional_triples.map { |t|
|
310
|
+
s,p,o = t
|
311
|
+
if(s.to_s.index("?") == 0 && p.to_s.index("?") == 0 && o.to_s.index("?") == 0)
|
312
|
+
nil
|
313
|
+
else
|
314
|
+
projection[s] = true if((s.to_s.index("?") == 0) && s.to_s.index("?X_").nil?)
|
315
|
+
projection[p] = true if((p.to_s.index("?") == 0) && p.to_s.index("?X_").nil?)
|
316
|
+
projection[o] = true if((o.to_s.index("?") == 0) && o.to_s.index("?X_").nil?)
|
317
|
+
if(t.is_a?(Filter))
|
318
|
+
t.acum
|
319
|
+
else
|
320
|
+
t.join(' ')
|
321
|
+
end
|
322
|
+
end
|
323
|
+
}.compact.join(" . ") + " }"
|
324
|
+
}.join(" ")
|
325
|
+
|
326
|
+
main_bgp = "#{bgps}"
|
327
|
+
unless(@optional_bgps.empty?)
|
328
|
+
main_bgp += " #{optional_bgps}"
|
329
|
+
end
|
330
|
+
|
331
|
+
query = if(preamble)
|
332
|
+
query = "PREFIX : <http://grel.org/vocabulary#> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX rdfs: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>"
|
333
|
+
query = query + " PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX fn: <http://www.w3.org/2005/xpath-functions#> "
|
334
|
+
|
335
|
+
if(@unions.length>0)
|
336
|
+
union_bgps = @unions.map{|u| u.to_sparql_describe(false) }.join(" UNION ")
|
337
|
+
all_subjects = projection.keys
|
338
|
+
query += "SELECT DISTINCT #{all_subjects.join(' ')} WHERE { { #{main_bgp} } UNION #{union_bgps} }"
|
339
|
+
else
|
340
|
+
all_subjects = projection.keys
|
341
|
+
query += "SELECT DISTINCT #{all_subjects.join(' ')} WHERE { #{main_bgp} }"
|
342
|
+
end
|
343
|
+
else
|
344
|
+
"{ #{main_bgp} }"
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def limit=(limit)
|
349
|
+
@limit = limit
|
350
|
+
self
|
351
|
+
end
|
352
|
+
|
353
|
+
def offset=(offset)
|
354
|
+
@offset = offset
|
355
|
+
self
|
356
|
+
end
|
357
|
+
|
358
|
+
def union(other_context)
|
359
|
+
@unions << other_context
|
360
|
+
self
|
361
|
+
end
|
362
|
+
|
363
|
+
def run
|
364
|
+
@graph.query(to_sparql_describe)
|
365
|
+
end
|
366
|
+
|
367
|
+
def required_properties
|
368
|
+
props = []
|
369
|
+
|
370
|
+
end
|
371
|
+
end # end of QueryContext
|
372
|
+
|
373
|
+
class Filter
|
374
|
+
DEFINED_FILTERS = {
|
375
|
+
:$neq => true, :$eq => true, :$lt => true,
|
376
|
+
:$lteq => true, :$gt => true, :$gteq => true,
|
377
|
+
:$not => true, :$like => true,
|
378
|
+
:$or => true, :$and => true, :$in => true
|
379
|
+
}
|
380
|
+
|
381
|
+
def self.filter?(h)
|
382
|
+
h.keys.length == 1 && DEFINED_FILTERS[h.keys.first]
|
383
|
+
end
|
384
|
+
|
385
|
+
attr_reader :variable, :acum
|
386
|
+
|
387
|
+
def initialize(context)
|
388
|
+
@context = context
|
389
|
+
@acum = ""
|
390
|
+
@variable = context.fresh_filter_predicate
|
391
|
+
end
|
392
|
+
|
393
|
+
def parse(h)
|
394
|
+
parse_filter(h)
|
395
|
+
@acum = "FILTER("+ @acum + ")"
|
396
|
+
end
|
397
|
+
|
398
|
+
def parse_filter(h)
|
399
|
+
k = h.keys.first
|
400
|
+
v = h.values.first
|
401
|
+
self.send "generate_#{k.to_s.split("$").last}".to_sym, v
|
402
|
+
end
|
403
|
+
|
404
|
+
def generate_in(v)
|
405
|
+
generate_or v.map{|x| {:$eq => x} }
|
406
|
+
end
|
407
|
+
|
408
|
+
def generate_neq(v)
|
409
|
+
@acum = @acum + "(#{variable} != "
|
410
|
+
operator = if(v.is_a?(Hash))
|
411
|
+
parse_filter(v)
|
412
|
+
else
|
413
|
+
QL.to_query(v,@context)
|
414
|
+
end
|
415
|
+
@acum+="#{operator})"
|
416
|
+
end
|
417
|
+
|
418
|
+
def generate_eq(v)
|
419
|
+
@acum = @acum + "(#{variable} = "
|
420
|
+
operator = if(v.is_a?(Hash))
|
421
|
+
parse_filter(v)
|
422
|
+
else
|
423
|
+
QL.to_query(v,@context)
|
424
|
+
end
|
425
|
+
@acum+="#{operator})"
|
426
|
+
end
|
427
|
+
|
428
|
+
def generate_lt(v)
|
429
|
+
@acum = @acum + "(#{variable} < "
|
430
|
+
operator = if(v.is_a?(Hash))
|
431
|
+
parse_filter(v)
|
432
|
+
else
|
433
|
+
QL.to_query(v,@context)
|
434
|
+
end
|
435
|
+
@acum+="#{operator})"
|
436
|
+
end
|
437
|
+
|
438
|
+
def generate_lteq(v)
|
439
|
+
@acum = @acum + "(#{variable} <= "
|
440
|
+
operator = if(v.is_a?(Hash))
|
441
|
+
parse_filter(v)
|
442
|
+
else
|
443
|
+
QL.to_query(v,@context)
|
444
|
+
end
|
445
|
+
@acum+="#{operator})"
|
446
|
+
end
|
447
|
+
|
448
|
+
def generate_gt(v)
|
449
|
+
@acum = @acum + "(#{variable} > "
|
450
|
+
operator = if(v.is_a?(Hash))
|
451
|
+
parse_filter(v)
|
452
|
+
else
|
453
|
+
QL.to_query(v,@context)
|
454
|
+
end
|
455
|
+
@acum+="#{operator})"
|
456
|
+
end
|
457
|
+
|
458
|
+
def generate_gteq(v)
|
459
|
+
@acum = @acum + "(#{variable} <= "
|
460
|
+
operator = if(v.is_a?(Hash))
|
461
|
+
parse_filter(v)
|
462
|
+
else
|
463
|
+
QL.to_query(v,@context)
|
464
|
+
end
|
465
|
+
@acum+="#{operator})"
|
466
|
+
end
|
467
|
+
|
468
|
+
def generate_not(v)
|
469
|
+
@acum += "!("
|
470
|
+
if(v.is_a?(Hash))
|
471
|
+
parse_filter(v)
|
472
|
+
else
|
473
|
+
@acum += QL.to_query(v,@context)
|
474
|
+
end
|
475
|
+
@acum += ")"
|
476
|
+
end
|
477
|
+
|
478
|
+
def generate_like(v)
|
479
|
+
operator = if(v.is_a?(Regexp))
|
480
|
+
v.source
|
481
|
+
elsif(v.is_a?(String))
|
482
|
+
v
|
483
|
+
else
|
484
|
+
raise Exception.new("Only Regexes and Strings can be used with the $like operator")
|
485
|
+
end
|
486
|
+
@acum += "(regex(#{variable},\"#{operator}\",\"i\"))"
|
487
|
+
end
|
488
|
+
|
489
|
+
def generate_or(v)
|
490
|
+
if(v.is_a?(Array))
|
491
|
+
@acum += "("
|
492
|
+
v.each_with_index do |f,i|
|
493
|
+
parse_filter(f)
|
494
|
+
@acum += "||" if i<v.length-1
|
495
|
+
end
|
496
|
+
@acum += ")"
|
497
|
+
else
|
498
|
+
raise Exception.new("$or filter must accept an array of conditions")
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
def generate_and(v)
|
503
|
+
if(v.is_a?(Array))
|
504
|
+
@acum += "("
|
505
|
+
v.each_with_index do |f,i|
|
506
|
+
parse_filter(f)
|
507
|
+
@acum += "&&" if i<v.length-1
|
508
|
+
end
|
509
|
+
@acum += ")"
|
510
|
+
else
|
511
|
+
raise Exception.new("$or filter must accept an array of conditions")
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
end
|
516
|
+
|
517
|
+
class BGP
|
518
|
+
|
519
|
+
attr_reader :bgp_id, :context, :data
|
520
|
+
|
521
|
+
def initialize(data, context, inverse=false)
|
522
|
+
@data = data
|
523
|
+
@context = context
|
524
|
+
@bgp_id = nil
|
525
|
+
@inverse = inverse
|
526
|
+
end
|
527
|
+
|
528
|
+
def to_query
|
529
|
+
acum = []
|
530
|
+
|
531
|
+
node_id = @context.next_node_id
|
532
|
+
id = @data[:@id]
|
533
|
+
id = "@id(#{id})" if id && id.is_a?(String) && id.index("@id(").nil?
|
534
|
+
id = QL.to_query(id, context) if id
|
535
|
+
subject_var_id,pred_var_id, obj_var_id = @context.register_node(id,node_id,@inverse)
|
536
|
+
|
537
|
+
filters = []
|
538
|
+
|
539
|
+
@data.inject([]) do |ac,(k,v)|
|
540
|
+
# $optional can point to an array of conditions that must be handled separetedly
|
541
|
+
if(k.to_s == "$optional" && v.is_a?(Array))
|
542
|
+
v.each{ |c| ac << [k,c] }
|
543
|
+
else
|
544
|
+
ac << [k,v]
|
545
|
+
end
|
546
|
+
ac
|
547
|
+
end.each do |(k,v)|
|
548
|
+
# process each property in the query hash
|
549
|
+
inverse = false
|
550
|
+
optional = false
|
551
|
+
unless(k == :@id)
|
552
|
+
prop = if(k.to_s.index("$inv_"))
|
553
|
+
inverse = true
|
554
|
+
k.to_s
|
555
|
+
elsif(k.to_s == "$optional")
|
556
|
+
context.optional = true
|
557
|
+
optional = true
|
558
|
+
else
|
559
|
+
parse_property(k)
|
560
|
+
end
|
561
|
+
value = QL.to_query(v,context,inverse)
|
562
|
+
if(value.is_a?(Array))
|
563
|
+
context.append(value,false)
|
564
|
+
value = value.triples_id
|
565
|
+
elsif(value.is_a?(Filter))
|
566
|
+
filters << value
|
567
|
+
value = value.variable
|
568
|
+
elsif(value == context) # It was a nested hash, retrieve the subject as object value
|
569
|
+
if(optional)
|
570
|
+
subject_to_replace = value.last_registered_subject
|
571
|
+
context.node_to_optional(subject_to_replace)#, subject_var_id)
|
572
|
+
context.optional_triples = context.optional_triples.map do |(s,p,o)|
|
573
|
+
s = (s == subject_to_replace ? subject_var_id : s)
|
574
|
+
o = (o == subject_to_replace ? subject_var_id : o)
|
575
|
+
[s, p, o]
|
576
|
+
end
|
577
|
+
context.optional = false
|
578
|
+
else
|
579
|
+
value = context.last_registered_subject
|
580
|
+
end
|
581
|
+
end
|
582
|
+
acum << [prop,value] unless optional
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
bgp_id = subject_var_id
|
587
|
+
acum = acum.map do |(p,o)|
|
588
|
+
if(p.index("$inv_"))
|
589
|
+
[o,QL.to_query(p.to_sym,@context),subject_var_id]
|
590
|
+
else
|
591
|
+
[subject_var_id,p,o]
|
592
|
+
end
|
593
|
+
end
|
594
|
+
acum << [subject_var_id, pred_var_id, obj_var_id] if acum.empty?
|
595
|
+
acum += filters
|
596
|
+
acum
|
597
|
+
end
|
598
|
+
|
599
|
+
def parse_property(k)
|
600
|
+
QL.to_query(k, context)
|
601
|
+
end
|
602
|
+
|
603
|
+
end # end of BGP
|
604
|
+
|
605
|
+
def self.to_query(obj,context=QL::QueryContext.new, inverse=false)
|
606
|
+
if(obj.is_a?(Symbol))
|
607
|
+
if(obj == :@type)
|
608
|
+
"rdf:type"
|
609
|
+
elsif(obj.to_s.index("$inv_"))
|
610
|
+
":#{obj.to_s.split("$inv_").last}"
|
611
|
+
elsif(obj.to_s == "_")
|
612
|
+
"?X_#{obj.to_s.split('_').last}_#{context.next_node_id}"
|
613
|
+
elsif(obj.to_s.index("_"))
|
614
|
+
"?#{obj.to_s.split("_").drop(1).join("_")}"
|
615
|
+
else
|
616
|
+
":#{obj}"
|
617
|
+
end
|
618
|
+
elsif(obj.is_a?(String))
|
619
|
+
if(obj =~ ID_REGEX)
|
620
|
+
QL.to_id(obj)
|
621
|
+
else
|
622
|
+
"\"#{obj}\""
|
623
|
+
end
|
624
|
+
elsif(obj.is_a?(Float))
|
625
|
+
"\"#{obj}\"^^<http://www.w3.org/2001/XMLSchema#float>"
|
626
|
+
elsif(obj.is_a?(Numeric))
|
627
|
+
"\"#{obj}\"^^<http://www.w3.org/2001/XMLSchema#integer>"
|
628
|
+
elsif(obj.is_a?(Time))
|
629
|
+
"\"#{obj.iso8601}\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"
|
630
|
+
elsif(obj.is_a?(Date))
|
631
|
+
"\"#{Time.new(obj.to_s).iso8601}\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"
|
632
|
+
elsif(obj.is_a?(Array))
|
633
|
+
# TODO
|
634
|
+
elsif(obj.is_a?(Hash))
|
635
|
+
if(Filter.filter?(obj))
|
636
|
+
filter = Filter.new(context)
|
637
|
+
filter.parse(obj)
|
638
|
+
filter
|
639
|
+
else
|
640
|
+
bgp = BGP.new(obj,context,inverse)
|
641
|
+
context.append(bgp.to_query)
|
642
|
+
context
|
643
|
+
end
|
644
|
+
else
|
645
|
+
if(obj.respond_to?(:to_query))
|
646
|
+
obj.to_query(context)
|
647
|
+
else
|
648
|
+
"\"#{obj}\""
|
649
|
+
end
|
650
|
+
end
|
651
|
+
end # end of to_query
|
652
|
+
|
653
|
+
def self.from_tuple_binding(tuple_value)
|
654
|
+
if(tuple_value["type"] == "uri")
|
655
|
+
from_binding_to_id(tuple_value["value"])
|
656
|
+
elsif(tuple_value["type"] == "literal")
|
657
|
+
tuple_value["value"]
|
658
|
+
else
|
659
|
+
tuple_value["@type"] = tuple_value["datatype"]
|
660
|
+
tuple_value["@value"] = tuple_value["value"]
|
661
|
+
from_binding_value(tuple_value)
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
def self.from_binding_to_id(obj)
|
666
|
+
if(!obj.is_a?(String))
|
667
|
+
obj
|
668
|
+
elsif(obj == "http://www.w3.org/1999/02/22-rdf-syntax-ns#nil")
|
669
|
+
nil
|
670
|
+
elsif(obj.index("http://grel.org/ids/id/"))
|
671
|
+
val = URI.unescape(obj.split("http://grel.org/ids/id/").last)
|
672
|
+
"@id(#{val})"
|
673
|
+
elsif(obj.index("http://grel.org/ids/"))
|
674
|
+
URI.unescape(obj.split("http://grel.org/ids/").last)
|
675
|
+
elsif(obj.index("http://grel.org/vocabulary#"))
|
676
|
+
obj.split("http://grel.org/vocabulary#").last.to_sym
|
677
|
+
else
|
678
|
+
obj
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
682
|
+
def self.from_binding_value(obj)
|
683
|
+
if(obj.is_a?(Hash) && obj["@id"])
|
684
|
+
from_binding_hash(obj)
|
685
|
+
elsif(obj.is_a?(Hash) && obj["@type"])
|
686
|
+
if(obj["@type"] == "http://www.w3.org/2001/XMLSchema#dateTime")
|
687
|
+
Time.parse(obj["@value"])
|
688
|
+
elsif(obj["@type"] == "http://www.w3.org/2001/XMLSchema#integer")
|
689
|
+
obj["@value"].to_i
|
690
|
+
elsif(obj["@type"] == "http://www.w3.org/2001/XMLSchema#float")
|
691
|
+
obj["@value"].to_f
|
692
|
+
elsif(obj["@type"] == "http://www.w3.org/2001/XMLSchema#boolean")
|
693
|
+
(obj["@value"] == "true" ? true : false)
|
694
|
+
else
|
695
|
+
obj["@value"]
|
696
|
+
end
|
697
|
+
elsif(obj.is_a?(Array))
|
698
|
+
obj.map{|o| from_binding_value(o)}
|
699
|
+
else
|
700
|
+
from_binding_to_id(obj)
|
701
|
+
end
|
702
|
+
end # end of from_binding_value
|
703
|
+
|
704
|
+
def self.from_binding_hash(node)
|
705
|
+
node = node.raw_json if node.respond_to?(:raw_json)
|
706
|
+
node.delete("@context")
|
707
|
+
node = node.to_a.inject({}) do |ac, (p,v)|
|
708
|
+
p = p[1..-1].to_sym if(p.index(":") == 0)
|
709
|
+
p = p.to_sym if(p == "@id" || p == "@type")
|
710
|
+
v = from_binding_value(v)
|
711
|
+
ac[p] = v; ac
|
712
|
+
end
|
713
|
+
node
|
714
|
+
end
|
715
|
+
|
716
|
+
def self.from_bindings_to_nodes(bindings,context, options = {})
|
717
|
+
unlinked = options[:unlinked] || false
|
718
|
+
nodes = {}
|
719
|
+
uris = {}
|
720
|
+
json = bindings
|
721
|
+
json = [json] unless json.is_a?(Array)
|
722
|
+
json.each do|node|
|
723
|
+
node = from_binding_hash(node)
|
724
|
+
nodes[node[:@id]] = node
|
725
|
+
node.delete(:@id) unless node[:@id].index("@id(")
|
726
|
+
end
|
727
|
+
|
728
|
+
nodes.each do |(node_id,node)|
|
729
|
+
node.each_pair do |k,v|
|
730
|
+
# weird things happening with describe queries and arrays
|
731
|
+
if(v.is_a?(Array))
|
732
|
+
v = v.uniq
|
733
|
+
v = v.first if v.length ==1
|
734
|
+
node[k] = v
|
735
|
+
end
|
736
|
+
|
737
|
+
if(v.is_a?(Hash) && v[:@id] && nodes[v[:@id]])
|
738
|
+
node[k] = nodes[v[:@id]]
|
739
|
+
nodes.delete(v[:@id]) if unlinked
|
740
|
+
elsif(v.is_a?(Hash) && v[:@id])
|
741
|
+
node[k] = from_binding_to_id(v[:@id])
|
742
|
+
elsif(v.is_a?(Array))
|
743
|
+
node[k] = v.map do |o|
|
744
|
+
# recursive execution for each element in the array
|
745
|
+
if(o.is_a?(Hash) && o[:@id] && nodes[o[:@id]])
|
746
|
+
to_link = nodes[o[:@id]]
|
747
|
+
nodes.delete(o[:@id]) if unlinked
|
748
|
+
to_link
|
749
|
+
elsif(o.is_a?(Hash) && o[:@id])
|
750
|
+
from_binding_to_id(o[:@id])
|
751
|
+
else
|
752
|
+
o
|
753
|
+
end
|
754
|
+
end
|
755
|
+
end
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
nodes.values
|
760
|
+
end
|
761
|
+
|
762
|
+
end # end of class QL
|
763
|
+
end # end of GRel module
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -33,7 +33,10 @@ email:
|
|
33
33
|
executables: []
|
34
34
|
extensions: []
|
35
35
|
extra_rdoc_files: []
|
36
|
-
files:
|
36
|
+
files:
|
37
|
+
- lib/grel/base.rb
|
38
|
+
- lib/grel/ql.rb
|
39
|
+
- lib/grel.rb
|
37
40
|
homepage: https://github.com/antoniogarrote/grel
|
38
41
|
licenses: []
|
39
42
|
post_install_message:
|