activerdf_net7 1.6.16 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/CHANGELOG +63 -0
  2. data/activerdf-jena/lib/activerdf_jena/jena.rb +4 -4
  3. data/activerdf-jena/lib/activerdf_jena/jena_adapter.rb +55 -55
  4. data/activerdf-jena/lib/activerdf_jena/lucene.rb +1 -1
  5. data/activerdf-jena/lib/activerdf_jena/ng4j.rb +7 -7
  6. data/activerdf-jena/lib/activerdf_jena/ng4j_adapter.rb +47 -47
  7. data/activerdf-jena/lib/activerdf_jena/pellet.rb +1 -1
  8. data/activerdf-jena/test/test_jena_adapter.rb +121 -120
  9. data/activerdf-jena/test/test_ng4j_adapter.rb +111 -110
  10. data/activerdf-rdflite/lib/activerdf_rdflite/fetching.rb +23 -19
  11. data/activerdf-rdflite/lib/activerdf_rdflite/rdflite.rb +153 -277
  12. data/activerdf-rdflite/lib/activerdf_rdflite/suggesting.rb +2 -2
  13. data/activerdf-rdflite/test/test_fetching.rb +7 -22
  14. data/activerdf-rdflite/test/test_rdflite.rb +44 -257
  15. data/activerdf-redland/lib/activerdf_redland/redland.rb +246 -282
  16. data/activerdf-redland/test/test_redland_adapter.rb +62 -224
  17. data/activerdf-sesame/ext/wrapper-sesame2.jar +0 -0
  18. data/activerdf-sesame/java/build.number +2 -2
  19. data/activerdf-sesame/java/build.xml +0 -0
  20. data/activerdf-sesame/java/lib/junit-3.8.2.jar +0 -0
  21. data/activerdf-sesame/java/settings.xml +0 -0
  22. data/activerdf-sesame/java/src/org/activerdf/wrapper/sesame2/WrapperForSesame2.java +0 -0
  23. data/activerdf-sesame/java/temp/build/org/activerdf/wrapper/sesame2/WrapperForSesame2.class +0 -0
  24. data/activerdf-sesame/java/temp/manifest/MANIFEST.MF +2 -2
  25. data/activerdf-sesame/java/test-src/org/activerdf/wrapper/sesame2/TestWrapperForSesame2.java +0 -0
  26. data/activerdf-sesame/lib/activerdf_sesame/sesame.rb +360 -364
  27. data/activerdf-sesame/test/test_sesame_adapter.rb +85 -83
  28. data/activerdf-sparql/lib/activerdf_sparql/sparql.rb +147 -148
  29. data/activerdf-sparql/lib/activerdf_sparql/sparql_result_parser.rb +5 -5
  30. data/activerdf-sparql/test/test_sparql_adapter.rb +2 -0
  31. data/activerdf-yars/lib/activerdf_yars/jars2.rb +85 -83
  32. data/lib/active_rdf/federation/active_rdf_adapter.rb +26 -39
  33. data/lib/active_rdf/federation/connection_pool.rb +119 -110
  34. data/lib/active_rdf/federation/federation_manager.rb +51 -51
  35. data/lib/active_rdf/objectmanager/bnode.rb +8 -2
  36. data/lib/active_rdf/objectmanager/literal.rb +81 -50
  37. data/lib/active_rdf/objectmanager/namespace.rb +117 -84
  38. data/lib/active_rdf/objectmanager/object_manager.rb +101 -96
  39. data/lib/active_rdf/objectmanager/ordered_set.rb +1 -1
  40. data/lib/active_rdf/objectmanager/property.rb +345 -0
  41. data/lib/active_rdf/objectmanager/property_list.rb +4 -4
  42. data/lib/active_rdf/objectmanager/property_lookup.rb +57 -0
  43. data/lib/active_rdf/objectmanager/resource.rb +293 -501
  44. data/lib/active_rdf/objectmanager/resource_like.rb +2 -2
  45. data/lib/active_rdf/objectmanager/resource_query.rb +85 -0
  46. data/lib/active_rdf/queryengine/ntriples_parser.rb +75 -68
  47. data/lib/active_rdf/queryengine/query.rb +237 -183
  48. data/lib/active_rdf/queryengine/query2jars2.rb +17 -15
  49. data/lib/active_rdf/queryengine/query2sparql.rb +107 -101
  50. data/lib/active_rdf.rb +28 -17
  51. data/lib/active_rdf_helpers.rb +37 -5
  52. data/lib/active_rdf_log.rb +11 -11
  53. data/test/adapters/test_activerdf_adapter.rb +138 -0
  54. data/test/{test_adapters.rb → adapters/test_adapters.rb} +6 -24
  55. data/test/adapters/test_bnode_capable_adapter.rb +31 -0
  56. data/test/adapters/test_context_aware_adapter.rb +31 -0
  57. data/test/adapters/test_network_aware_adapter.rb +29 -0
  58. data/test/adapters/test_persistent_adapter.rb +21 -0
  59. data/test/adapters/test_read_only_adapter.rb +15 -0
  60. data/test/adapters/test_reasoning_adapter.rb +11 -0
  61. data/test/adapters/test_writable_adapter.rb +163 -0
  62. data/test/common.rb +78 -96
  63. data/test/federation/test_connection_pool.rb +25 -44
  64. data/test/federation/test_federation_manager.rb +45 -45
  65. data/test/objectmanager/test_literal.rb +47 -26
  66. data/test/objectmanager/test_namespace.rb +3 -1
  67. data/test/objectmanager/test_object_manager.rb +35 -45
  68. data/test/objectmanager/test_ordered_set.rb +1 -1
  69. data/test/objectmanager/test_property.rb +261 -0
  70. data/test/objectmanager/test_resource_reading.rb +196 -104
  71. data/test/objectmanager/test_resource_reasoning.rb +26 -0
  72. data/test/objectmanager/test_resource_writing.rb +34 -25
  73. data/test/queryengine/my_external_resource.rb +5 -1
  74. data/test/queryengine/test_external_resource_class.rb +1 -8
  75. data/test/queryengine/test_ntriples_parser.rb +5 -3
  76. data/test/queryengine/test_query.rb +3 -3
  77. data/test/queryengine/test_query2jars2.rb +2 -2
  78. data/test/queryengine/test_query2sparql.rb +2 -2
  79. data/test/queryengine/test_query_engine.rb +46 -28
  80. metadata +16 -8
  81. data/activerdf-rdflite/test/test_bnode_data.nt +0 -5
  82. data/activerdf-rdflite/test/test_data.nt +0 -32
  83. data/activerdf-rdflite/test/test_escaped_data.nt +0 -2
  84. data/activerdf-redland/test/test_person_data.nt +0 -42
  85. data/test/objectmanager/test_talia_syntax.rb +0 -68
@@ -10,28 +10,19 @@ require 'queryengine/ntriples_parser'
10
10
  require 'open-uri'
11
11
  require 'mime/types'
12
12
 
13
- ActiveRdfLogger::log_info "Loading RDFLite adapter", self
14
-
15
-
16
-
17
- # RDFLite is a lightweight RDF database on top of sqlite3. It can act as adapter
18
- # in ActiveRDF. It supports on-disk and in-memory usage, and allows keyword
13
+ # RDFLite is a lightweight RDF database on top of sqlite3. It can act as adapter
14
+ # in ActiveRDF. It supports on-disk and in-memory usage, and allows keyword
19
15
  # search if ferret is installed.
20
- class RDFLite < ActiveRdfAdapter
16
+ module ActiveRDF
17
+ class RDFLite < ActiveRdfAdapter
18
+ ActiveRdfLogger::log_info "Loading RDFLite adapter", self
21
19
 
22
- class << self
23
- begin
20
+ begin
24
21
  require 'ferret'
25
- @have_ferret = true
22
+ @@have_ferret = true
26
23
  rescue LoadError
27
- ActiveRdfLogger::log_info "Keyword search is disabled since we could not load Ferret. To
28
- enable, please do \"gem install ferret\"", self
29
- @have_ferret = false
30
- end
31
-
32
- def has_ferret?
33
- @have_ferret
34
- end
24
+ ActiveRdfLogger::log_info "Keyword search is disabled since we could not load Ferret. To enable, please do \"gem install ferret\"", self
25
+ @@have_ferret = false
35
26
  end
36
27
 
37
28
  ConnectionPool.register_adapter(:rdflite,self)
@@ -46,19 +37,27 @@ class RDFLite < ActiveRdfAdapter
46
37
  super()
47
38
  ActiveRdfLogger::log_info(self) { "Initialised rdflite with params #{params.to_s}" }
48
39
 
49
- @reads = true
50
- @writes = true
40
+ @reasoning = truefalse(params[:reasoning], false)
41
+ @subprops = {} if @reasoning
51
42
 
52
43
  # if no file-location given, we use in-memory store
53
44
  file = params[:location] || ':memory:'
54
- @db = SQLite3::Database.new(file)
45
+ @db = SQLite3::Database.new(file)
55
46
 
56
47
  # disable keyword search by default, enable only if ferret is found
57
- @keyword_search = params[:keyword].nil? ? false : params[:keyword]
58
- @keyword_search &= self.class.has_ferret?
48
+ @keyword_search = truefalse(params[:keyword], false) && @@have_ferret
59
49
 
60
- @reasoning = params[:reasoning] || false
61
- @subprops = {} if @reasoning
50
+ # turn off filesystem synchronisation for speed
51
+ @db.synchronous = 'off'
52
+
53
+ # drop the table if a new datastore is requested
54
+ @db.execute('drop table if exists triple') if truefalse(params[:new],false)
55
+
56
+ # create triples table. ignores duplicated triples
57
+ @db.execute('create table if not exists triple(s,p,o,c, unique(s,p,o,c) on conflict ignore)')
58
+
59
+ create_indices(params)
60
+ @db
62
61
 
63
62
  if keyword_search?
64
63
  # we initialise the ferret index, either as a file or in memory
@@ -74,15 +73,6 @@ class RDFLite < ActiveRdfAdapter
74
73
  Ferret::I.new(:field_infos => infos)
75
74
  end
76
75
  end
77
-
78
- # turn off filesystem synchronisation for speed
79
- @db.synchronous = 'off'
80
-
81
- # create triples table. ignores duplicated triples
82
- @db.execute('create table if not exists triple(s,p,o,c, unique(s,p,o,c) on conflict ignore)')
83
-
84
- create_indices(params)
85
- @db
86
76
  end
87
77
 
88
78
  # returns the number of triples in the datastore (incl. possible duplicates)
@@ -110,19 +100,16 @@ class RDFLite < ActiveRdfAdapter
110
100
 
111
101
  # deletes triple(s,p,o,c) from datastore
112
102
  # symbol parameters match anything: delete(:s,:p,:o) will delete all triples
113
- # you can specify a context to limit deletion to that context:
103
+ # you can specify a context to limit deletion to that context:
114
104
  # delete(:s,:p,:o, 'http://context') will delete all triples with that context
115
- def delete(s, p, o, c=nil)
116
- # convert non-nil input to internal format
117
- quad = [s,p,o,c].collect {|r| r.nil? ? nil : internalise(r) }
118
-
119
- # construct where clause for deletion (for all non-nil input)
105
+ def delete(s, p, o=nil, c=nil)
120
106
  where_clauses = []
121
- conditions = []
122
- quad.each_with_index do |r,i|
123
- unless r.nil?
124
- conditions << r
107
+ conditions = []
108
+
109
+ [s,p,o,c].each_with_index do |r,i|
110
+ unless r.nil? or check_type(r, Symbol)
125
111
  where_clauses << "#{SPOC[i]} = ?"
112
+ conditions << r.to_literal_s
126
113
  end
127
114
  end
128
115
 
@@ -130,7 +117,7 @@ class RDFLite < ActiveRdfAdapter
130
117
  ds = 'delete from triple'
131
118
  ds << " where #{where_clauses.join(' and ')}" unless where_clauses.empty?
132
119
 
133
- # execute delete string with possible deletion conditions (for each
120
+ # execute delete string with possible deletion conditions (for each
134
121
  # non-empty where clause)
135
122
  ActiveRdfLogger::log_debug(self) { "Deleting #{[s,p,o,c].join(' ')}" }
136
123
  @db.execute(ds, *conditions)
@@ -150,14 +137,10 @@ class RDFLite < ActiveRdfAdapter
150
137
  raise(ActiveRdfError, "adding non-resource #{s} while adding (#{s},#{p},#{o},#{c})") unless s.respond_to?(:uri)
151
138
  raise(ActiveRdfError, "adding non-resource #{p} while adding (#{s},#{p},#{o},#{c})") unless p.respond_to?(:uri)
152
139
 
153
- triple = [s, p, o].collect{|r| serialise(r) }
154
- add_ntriples(triple.join(' ') + " .\n", serialise(c) )
155
-
156
- ## get internal representation (array)
157
- #quad = [s,p,o,c].collect {|r| internalise(r) }
140
+ c = c.to_literal_s unless c.nil?
158
141
 
159
- ## insert the triple into the datastore
160
- #@db.execute('insert into triple values (?,?,?,?)', *quad)
142
+ # insert triple into database
143
+ @db.execute('insert into triple values (?,?,?,?);',s.to_literal_s,p.to_literal_s,o.to_literal_s,c)
161
144
 
162
145
  ## if keyword-search available, insert the object into keyword search
163
146
  #@ferret << {:subject => s, :object => o} if keyword_search?
@@ -165,28 +148,31 @@ class RDFLite < ActiveRdfAdapter
165
148
 
166
149
  # flushes openstanding changes to underlying sqlite3
167
150
  def flush
168
- # since we always write changes into sqlite3 immediately, we don't do
151
+ # since we always write changes into sqlite3 immediately, we don't do
169
152
  # anything here
170
153
  true
171
154
  end
172
155
 
173
156
  # loads triples from file in ntriples format
174
- def load(location)
175
- context = if URI.parse(location).host
176
- location
157
+ def load(location, syntax = nil)
158
+ context = if @contexts
159
+ if URI.parse(location).host
160
+ RDFS::Resource.new(location)
161
+ else
162
+ RDFS::Resource.new("file:#{location}")
163
+ end
177
164
  else
178
- internalise(RDFS::Resource.new("file:#{location}"))
165
+ nil
179
166
  end
180
167
 
181
- case MIME::Types.of(location)
182
- when MIME::Types['application/rdf+xml']
168
+ if MIME::Types.of(location) == MIME::Types['application/rdf+xml'] or syntax == 'rdfxml'
183
169
  # check if rapper available
184
- begin
170
+ begin
185
171
  # can only parse rdf/xml with redland
186
172
  # die otherwise
187
173
  require 'rdf/redland'
188
174
  model = Redland::Model.new
189
- Redland::Parser.new.parse_into_model(model, location)
175
+ Redland::Parser.new(syntax).parse_into_model(model, location)
190
176
  add_ntriples(model.to_string('ntriples'), location)
191
177
  rescue LoadError
192
178
  raise ActiveRdfError, "cannot parse remote rdf/xml file without Redland: please install Redland (librdf.org) and its Ruby bindings"
@@ -198,21 +184,16 @@ class RDFLite < ActiveRdfAdapter
198
184
  end
199
185
 
200
186
  # adds ntriples from given context into datastore
201
- def add_ntriples(ntriples, context)
187
+ def add_ntriples(ntriples, context = nil)
202
188
  # add each triple to db
203
189
  @db.transaction
204
- insert = @db.prepare('insert into triple values (?,?,?,?);')
205
190
 
206
191
  ntriples = NTriplesParser.parse(ntriples)
207
192
  ntriples.each do |s,p,o|
208
- # convert triples into internal db format
209
- subject, predicate, object = [s,p,o].collect {|r| internalise(r) }
210
-
211
- # insert triple into database
212
- insert.execute(subject, predicate, object, context)
193
+ add(s,p,o,context)
213
194
 
214
195
  # if keyword-search available, insert the object into keyword search
215
- @ferret << {:subject => subject, :object => object} if keyword_search?
196
+ @ferret << {:subject => s.to_s, :object => o.to_s} if keyword_search?
216
197
  end
217
198
 
218
199
  @db.commit
@@ -220,11 +201,11 @@ class RDFLite < ActiveRdfAdapter
220
201
  end
221
202
 
222
203
  # executes ActiveRDF query on datastore
223
- def query(query)
204
+ def execute(query)
224
205
  # construct query clauses
225
206
  sql, conditions = translate(query)
226
207
 
227
- # executing query, passing all where-clause values as parameters (so that
208
+ # executing query, passing all where-clause values as parameters (so that
228
209
  # sqlite will encode quotes correctly)
229
210
  results = @db.execute(sql, *conditions)
230
211
 
@@ -288,91 +269,68 @@ class RDFLite < ActiveRdfAdapter
288
269
  if not query.sort_clauses.empty?
289
270
  sort = query.sort_clauses.collect { |term| variable_name(query, term) }
290
271
  " order by (#{sort.join(',')})"
291
- elsif not query.reverse_sort_clauses.empty?
292
- sort = query.reverse_sort_clauses.collect { |term| variable_name(query, term) }
293
- " order by (#{sort.join(',')}) DESC"
294
272
  else
295
273
  ""
296
274
  end
297
275
  end
298
276
 
299
277
  # construct join clause
300
- # TODO: joins don't work this way, they have to be linear (in one direction
301
- # only, and we should only alias tables we didnt alias yet)
302
- # we should only look for one join clause in each where-clause: when we find
303
- # one, we skip the rest of the variables in this clause.
278
+ # begins with first element of construct traverses tables referenced by
304
279
  def construct_join(query)
305
- join_stmt = ''
306
-
307
- # no join necessary if only one where clause given
308
- return ' from triple as t0 ' if query.where_clauses.size == 1
309
-
310
- where_clauses = query.where_clauses.flatten
311
- considering = where_clauses.uniq.select{|w| w.is_a?(Symbol)}
280
+ join_stmt = []
281
+ seen_aliases = ['t0'] # first table already seen
282
+ seen_vars = []
283
+
284
+ # get a hash with indices for for all terms
285
+ # if the term doesnt have a join-buddy, we can skip it
286
+ occurances = term_occurances(query).reject{|var,terms| terms.size < 2}
287
+
288
+ # start with variables in first where clause
289
+ var_queue = query.where_clauses[0].find_all{|obj| check_type(obj, Symbol) and occurances.include?(obj)}
290
+
291
+ while !var_queue.empty? do
292
+ var = var_queue.shift
293
+ seen_vars << var
294
+ terms = occurances[var]
295
+
296
+ # look for a term in a table that has already been seen
297
+ table,field = terms.find{|table,field| seen_aliases.include?(table)}
298
+ raise ActiveRdfError, "query is confused. haven't seen #{table} for :#{var} yet." unless table
299
+ terms -= [[table,field]] # ignore this previously seen buddy term table
300
+ first_term = "#{table}.#{field}"
301
+
302
+ terms.each do |table,field|
303
+ join = ''
304
+ unless seen_aliases.include?(table)
305
+ seen_aliases << table
306
+ join << "join triple as #{table} on "
307
+ else
308
+ join << "and "
309
+ end
310
+ join << "#{first_term}=#{table}.#{field} "
311
+ join_stmt << join
312
312
 
313
- # constructing hash with indices for all terms
314
- # e.g. {?s => [1,3,5], ?p => [2], ... }
315
- term_occurrences = Hash.new()
316
- where_clauses.each_with_index do |term, index|
317
- ary = (term_occurrences[term] ||= [])
318
- ary << index
313
+ # add any terms from this table that haven't been seen yet to the queue
314
+ var_queue.concat query.where_clauses[table[1..-1].to_i].find_all{|obj| check_type(obj, Symbol) and !seen_vars.include?(obj) and occurances.include?(obj)}
315
+ end
319
316
  end
320
317
 
321
- aliases = {}
322
-
323
- where_clauses.each_with_index do |term, index|
324
- # if the term has been joined with his buddy already, we can skip it
325
- next unless considering.include?(term)
326
-
327
- # we find all (other) occurrences of this term
328
- indices = term_occurrences[term]
329
-
330
- # if the term doesnt have a join-buddy, we can skip it
331
- next if indices.size == 1
332
-
333
- # construct t0,t1,... as aliases for term
334
- # and construct join condition, e.g. t0.s
335
- termalias = "t#{index / 4}"
336
- termjoin = "#{termalias}.#{SPOC[index % 4]}"
337
-
338
- join = if join_stmt.include?(termalias)
339
- ""
340
- else
341
- "triple as #{termalias}"
342
- end
318
+ join = ' from triple as t0 '
319
+ join_stmt.empty? ? join : join + "#{join_stmt.join(' ')} "
320
+ end
343
321
 
344
- indices.each do |i|
345
- # skip the current term itself
346
- next if i==index
347
-
348
- # construct t0,t1, etc. as aliases for buddy,
349
- # and construct join condition, e.g. t0.s = t1.p
350
- buddyalias = "t#{i/4}"
351
- buddyjoin = "#{buddyalias}.#{SPOC[i%4]}"
352
-
353
- # TODO: fix reuse of same table names as aliases, e.g.
354
- # "from triple as t1 join triple as t2 on ... join t1 on ..."
355
- # is not allowed as such by sqlite
356
- # but on the other hand, restating the aliases gives ambiguity:
357
- # "from triple as t1 join triple as t2 on ... join triple as t1 ..."
358
- # is ambiguous
359
- if join_stmt.include?(buddyalias)
360
- join << "and #{termjoin} = #{buddyjoin}"
361
- else
362
- join << " join triple as #{buddyalias} on #{termjoin} = #{buddyjoin} "
322
+ # Returns a hash of arrays, keyed by the term symbol. Each element of the array is nested array of a pair of values: [table alias, field('s'|'p'|'o'|'c')]
323
+ # {:s => [[table,field],['t0','s'],...], :p => [['t0','p'],['t1','p']]}
324
+ def term_occurances(query)
325
+ term_occurances = {}
326
+ query.where_clauses.each_with_index do |clause, table_index|
327
+ clause.zip(SPOC).each do |obj,field|
328
+ if check_type(obj, Symbol)
329
+ (term_occurances[obj] ||= []) << ["t#{table_index}",field]
363
330
  end
364
331
  end
365
- join_stmt << join
366
-
367
- # remove term from 'todo' list of still-considered terms
368
- considering.delete(term)
369
- end
370
-
371
- if join_stmt == ''
372
- return " from triple as t0 "
373
- else
374
- return " from #{join_stmt} "
375
332
  end
333
+ term_occurances
376
334
  end
377
335
 
378
336
  # construct where clause
@@ -380,25 +338,45 @@ class RDFLite < ActiveRdfAdapter
380
338
  # collecting where clauses, these will be added to the sql string later
381
339
  where = []
382
340
 
383
- # collecting all the right-hand sides of where clauses (e.g. where name =
384
- # 'abc'), to add to query string later using ?-notation, because then
341
+ # collecting all the right-hand sides of where clauses (e.g. where name =
342
+ # 'abc'), to add to query string later using ?-notation, because then
385
343
  # sqlite will automatically encode quoted literals correctly
386
344
  right_hand_sides = []
387
345
 
388
- # convert each where clause to SQL:
389
- # add where clause for each subclause, except if it's a variable
390
- query.where_clauses.each_with_index do |clause,level|
391
- raise ActiveRdfError, "where clause #{clause} is not a triple" unless clause.is_a?(Array)
392
- clause.each_with_index do |subclause, i|
393
- # dont add where clause for variables
394
- unless subclause.is_a?(Symbol) || subclause.nil?
395
- conditions = compute_where_condition(i, subclause, query.reasoning? && reasoning?)
396
- if conditions.size == 1
397
- where << "t#{level}.#{SPOC[i]} = ?"
398
- right_hand_sides << conditions.first
346
+ query.where_clauses.each_with_index do |clause, table_index|
347
+ clause.zip(SPOC).each do |clause_elem,field|
348
+ if !(check_type(clause_elem, Symbol) or clause_elem.nil?)
349
+ # include querying on subproperty fields
350
+ if field == 'p' and query.reasoning? and $activerdf_internal_reasoning
351
+ predicate = clause_elem
352
+ predicates = [predicate] + predicate.sub_predicates
353
+ if predicates.size > 1
354
+ where << "t#{table_index}.#{field} in ('#{predicates.collect{|res| res.to_literal_s}.join("','")}')"
355
+ else
356
+ where << "t#{table_index}.#{field} = ?"
357
+ right_hand_sides << predicates[0].to_literal_s
358
+ end
399
359
  else
400
- conditions = conditions.collect {|c| "'#{c}'"}
401
- where << "t#{level}.#{SPOC[i]} in (#{conditions.join(',')})"
360
+ # match plain strings to all strings
361
+ if query.all_types? and !clause_elem.respond_to?(:uri) # dont wildcard resources
362
+ where << "t#{table_index}.#{field} like ?"
363
+ right_hand_sides << "\"#{clause_elem.to_s}\"%"
364
+ else
365
+ where << "t#{table_index}.#{field} = ?"
366
+ right_hand_sides << clause_elem.to_literal_s
367
+ end
368
+ end
369
+ # process filters
370
+ elsif field == 'o' and check_type(clause_elem, Symbol) and (filter = query.filter_clauses[clause_elem])
371
+ operator, operand = filter
372
+ case operator
373
+ when :datatype
374
+ where << "t#{table_index}.o like ?"
375
+ right_hand_sides << "%\"^^<#{operand}>"
376
+ when :lang
377
+ lang, exact = operand
378
+ where << "t#{table_index}.o like ?"
379
+ right_hand_sides << (exact ? "%\"@#{lang}" : "%\"@%#{lang}%")
402
380
  end
403
381
  end
404
382
  end
@@ -426,100 +404,34 @@ class RDFLite < ActiveRdfAdapter
426
404
  end
427
405
  end
428
406
 
429
- def compute_where_condition(index, subclause, reasoning)
430
- conditions = [subclause]
431
-
432
- # expand conditions with rdfs rules if reasoning enabled
433
- if reasoning
434
- case index
435
- when 0: ;
436
- # no rule for subjects
437
- when 1:
438
- # expand properties to include all subproperties
439
- conditions = subproperties(subclause) if subclause.respond_to?(:uri)
440
- when 2:
441
- # no rule for objects
442
- when 3:
443
- # no rule for contexts
444
- end
445
- end
446
-
447
- # convert conditions into internal format
448
- #conditions.collect { |c| c.respond_to?(:uri) ? "<#{c.uri}>" : c.to_s }
449
- conditions.collect { |c| internalise(c) }
450
- end
451
-
452
- def subproperties(resource)
453
- # compute and store subproperties of this resource
454
- # or use earlier computed value if available
455
- unless @subprops[resource]
456
- subproperty = Namespace.lookup(:rdfs,:subPropertyOf)
457
- children_query = Query.new.distinct(:sub).where(:sub, subproperty, resource)
458
- children_query.reasoning = false
459
- children = children_query.execute
460
-
461
- if children.empty?
462
- @subprops[resource] = [resource]
463
- else
464
- @subprops[resource] = [resource] + children.collect{|c| subproperties(c)}.flatten.compact
465
- end
407
+ # returns first sql variable name found for a queryterm
408
+ def variable_name(query,term)
409
+ if (indices = term_occurances(query)[term])
410
+ table, field = indices[0]
411
+ "#{table}.#{field}"
466
412
  end
467
- @subprops[resource]
468
413
  end
469
414
 
470
- # returns sql variable name for a queryterm
471
- def variable_name(query,term)
472
- # look up the first occurence of this term in the where clauses, and compute
473
- # the level and s/p/o position of it
474
- index = query.where_clauses.flatten.index(term)
475
-
476
- if index.nil?
477
- # term does not appear in where clause
478
- # but maybe it appears in a keyword clause
479
-
480
- # index would not be nil if we had:
481
- # select(:o).where(knud, knows, :o).where(:o, :keyword, 'eyal')
482
- #
483
- # the only possibility that index is nil is if we have:
484
- # select(:o).where(:o, :keyword, :eyal) (selecting subject)
485
- # or if we use a select clause that does not appear in any where clause
486
-
487
- # so we check if we find the term in the keyword clauses, otherwise we throw
488
- # an error
489
- if query.keywords.keys.include?(term)
490
- return "t0.s"
491
- else
492
- raise ActiveRdfError, "unbound variable :#{term.to_s} in select of #{query.to_sp}"
493
- end
415
+ # "safe" type checking that will not trigger additional queries
416
+ # on RDFS::Resource. This assumes that the klass is not a Resource
417
+ # type itself
418
+ def check_type(item, klass)
419
+ if(item.respond_to?(:uri))
420
+ item.class == klass
421
+ else
422
+ item.is_a?(klass)
494
423
  end
495
-
496
- termtable = "t#{index / 4}"
497
- termspo = SPOC[index % 4]
498
- return "#{termtable}.#{termspo}"
499
424
  end
500
425
 
501
- # wrap resources into ActiveRDF resources, literals into Strings. result_type
502
- # is the type that should be used for ActiveRDF resources
426
+ # wrap resources into ActiveRDF resources, literals into Strings
503
427
  def wrap(results, result_type)
504
428
  results.collect do |row|
505
429
  row.collect { |result| parse(result, result_type) }
506
430
  end
507
431
  end
508
432
 
509
- # Return the result as a correct type. result_type is the type that should
510
- # be used for "resouce" elements
511
- def parse(result, result_type = RDFS::Resource)
512
- NTriplesParser.parse_node(result, result_type) || result
513
- # case result
514
- # when Literal
515
- # # replace special characters to allow string interpolation for e.g. 'test\nbreak'
516
- # $1.double_quote
517
- # when Resource
518
- # result_type.new($1)
519
- # else
520
- # # when we do a count(*) query we get a number, not a resource/literal
521
- # result
522
- # end
433
+ def parse(result, result_type)
434
+ NTriplesParser.parse_node(result, result_type)
523
435
  end
524
436
 
525
437
  def create_indices(params)
@@ -532,7 +444,7 @@ class RDFLite < ActiveRdfAdapter
532
444
  opidx = params[:opidx] || false
533
445
 
534
446
  # creating lookup indices
535
- @db.transaction do
447
+ @db.transaction do
536
448
  @db.execute('create index if not exists sidx on triple(s)') if sidx
537
449
  @db.execute('create index if not exists pidx on triple(p)') if pidx
538
450
  @db.execute('create index if not exists oidx on triple(o)') if oidx
@@ -542,41 +454,5 @@ class RDFLite < ActiveRdfAdapter
542
454
  @db.execute('create index if not exists opidx on triple(o,p)') if opidx
543
455
  end
544
456
  end
545
-
546
- # transform triple into internal format <uri> and "literal"
547
- def internalise(r)
548
- if r.nil? or r.is_a? Symbol
549
- nil
550
- else
551
- r.to_ntriple
552
- end
553
- end
554
-
555
- # transform resource/literal into ntriples format
556
- def serialise(r)
557
- # Fixme: r is most likely NOT a resource
558
- if r.nil?
559
- nil
560
- else
561
- r.to_ntriple
562
- end
563
- end
564
-
565
- Resource = /<([^>]*)>/
566
- Literal = /"((?:\\"|[^"])*)"/
567
-
568
- public :subproperties
569
- end
570
-
571
- class String
572
- def double_quote
573
- Thread.new do
574
- $SAFE = 12
575
- begin
576
- eval('"%s"' % self)
577
- rescue Exception => e
578
- self
579
- end
580
- end.value
581
- end
582
457
  end
458
+ end
@@ -26,7 +26,7 @@ class SuggestingAdapter < FetchingAdapter
26
26
 
27
27
  # suggests additional predicates that might be applicable for the given resource
28
28
  def suggest(resource)
29
- ActiveRdfLogger::log_debug(self) { "Starting suggestions for #{size} triples" }
29
+ ActiveRdfLogger::log_debug(self) { "Starting suggestions for #{size} triples" }
30
30
  time = Time.now
31
31
 
32
32
  predicates = []
@@ -53,7 +53,7 @@ class SuggestingAdapter < FetchingAdapter
53
53
  end
54
54
  [candidate, score]
55
55
  end
56
- ActiveRdfLogger::log_debug(self) { "Suggestions for #{resource} took #{Time.now-time}s" }
56
+ ActiveRdfLogger::log_debug(self) { "Suggestions for #{resource} took #{Time.now-time}s" }
57
57
  suggestions
58
58
  end
59
59
 
@@ -2,32 +2,17 @@
2
2
  # Copyright:: (c) 2005-2006
3
3
  # License:: LGPL
4
4
 
5
- require 'rubygems'
6
5
  require 'test/unit'
7
6
  require 'active_rdf'
7
+ common_test_dir = File.dirname(File.expand_path(__FILE__)) + '/../../test'
8
+ require "#{common_test_dir}/common"
9
+ require "#{common_test_dir}/adapters/test_network_aware_adapter"
8
10
 
9
11
  class TestFetchingAdapter < Test::Unit::TestCase
10
- def setup
11
- ConnectionPool.clear
12
- @adapter = ConnectionPool.add(:type => :fetching)
13
- end
14
-
15
- def teardown
16
- end
12
+ include SetupAdapter
13
+ include TestNetworkAwareAdapter
17
14
 
18
- def test_parse_foaf
19
- @adapter.fetch("http://eyaloren.org/foaf.rdf#me")
20
- assert @adapter.size > 0
21
- end
22
-
23
- def test_sioc_schema
24
- @adapter.fetch("http://rdfs.org/sioc/ns#")
25
- assert_equal 560, @adapter.size
26
- end
27
-
28
- def test_foaf_schema
29
- @adapter.fetch("http://xmlns.com/foaf/0.1/")
30
- # foaf contains 563 triples but with two duplicates
31
- assert_equal 561, @adapter.size
15
+ def setup
16
+ super(:type => :fetching)
32
17
  end
33
18
  end