odba 1.0.0

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.
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env ruby
2
+ #-- CacheEntry -- odba -- 29.04.2004 -- hwyss@ywesee.com mwalder@ywesee.com
3
+
4
+ module ODBA
5
+ class CacheEntry # :nodoc: all
6
+ @@id_table = {}
7
+ @@finalizer = proc { |object_id|
8
+ if(odba_id = @@id_table.delete(object_id))
9
+ ODBA.cache.invalidate odba_id
10
+ end
11
+ }
12
+ attr_accessor :last_access
13
+ attr_reader :accessed_by, :odba_id, :odba_object_id
14
+ def initialize(obj)
15
+ @odba_id = obj.odba_id
16
+ update obj
17
+ @accessed_by = {}
18
+ @odba_observers = obj.odba_observers
19
+ end
20
+ def update obj
21
+ @last_access = Time.now
22
+ @odba_object = obj
23
+ @odba_class = obj.class
24
+ @odba_id = obj.odba_id
25
+ unless @odba_object_id == obj.object_id
26
+ @@id_table.delete @odba_object_id
27
+ @odba_object_id = obj.object_id
28
+ @@id_table.store @odba_object_id, @odba_id
29
+ ObjectSpace.define_finalizer obj, @@finalizer
30
+ end
31
+ end
32
+ def object_id2ref(object_id, odba_id)
33
+ if (obj = ObjectSpace._id2ref(object_id)) \
34
+ && obj.is_a?(Persistable) && !obj.odba_unsaved? \
35
+ && obj.odba_id == odba_id
36
+ obj
37
+ end
38
+ rescue RangeError, NoMethodError => e
39
+ nil
40
+ end
41
+ def odba_id2ref(odba_id)
42
+ odba_id && ODBA.cache.include?(odba_id) && ODBA.cache.fetch(odba_id)
43
+ end
44
+ def odba_add_reference(object)
45
+ @accessed_by.store(object.object_id, object.odba_id)
46
+ object
47
+ end
48
+ def odba_cut_connections!
49
+ @accessed_by.each { |object_id, odba_id|
50
+ if((item = odba_id2ref(odba_id) || object_id2ref(object_id, odba_id)) \
51
+ && item.respond_to?(:odba_cut_connection))
52
+ item.odba_cut_connection(_odba_object)
53
+ end
54
+ }
55
+ end
56
+ def odba_notify_observers(*args)
57
+ @odba_observers.each { |obs| obs.odba_update(*args) }
58
+ end
59
+ def odba_object
60
+ @last_access = Time.now
61
+ @odba_object = _odba_object
62
+ @odba_object || ODBA.cache.fetch(@odba_id)
63
+ end
64
+ def _odba_object
65
+ @odba_object || object_id2ref(@odba_object_id, @odba_id)
66
+ end
67
+ def odba_old?(retire_horizon = Time.now - ODBA.cache.retire_age)
68
+ !_odba_object.odba_unsaved? \
69
+ && (retire_horizon > @last_access)
70
+ end
71
+ def odba_retire opts={}
72
+ # replace with stubs in accessed_by
73
+ instance = _odba_object
74
+ if opts[:force]
75
+ @accessed_by.each do |object_id, odba_id|
76
+ if item = odba_id2ref(odba_id)
77
+ item.odba_stubize instance, opts
78
+ elsif(item = object_id2ref(object_id, odba_id))
79
+ if item.is_a?(Persistable) && !item.is_a?(Stub)
80
+ item.odba_stubize instance, opts
81
+ end
82
+ end
83
+ end
84
+ @accessed_by.clear
85
+ @odba_object = nil
86
+ else
87
+ @accessed_by.delete_if { |object_id, odba_id|
88
+ if(item = odba_id2ref(odba_id))
89
+ item.odba_stubize instance
90
+ elsif(item = object_id2ref(object_id, odba_id))
91
+ case item
92
+ when Stub
93
+ true
94
+ when Array, Hash
95
+ false
96
+ when Persistable
97
+ item.odba_stubize instance
98
+ else
99
+ true
100
+ end
101
+ else
102
+ true
103
+ end
104
+ }
105
+ if @accessed_by.empty?
106
+ @odba_object = nil
107
+ end
108
+ end
109
+ end
110
+ def odba_replace!(obj)
111
+ oldhash = _odba_object.hash
112
+ _odba_object.odba_replace!(obj)
113
+ if(_odba_object.hash != oldhash)
114
+ @accessed_by.each { |object_id, odba_id|
115
+ if(item = odba_id2ref(odba_id) || object_id2ref(object_id, odba_id))
116
+ item.rehash if(item.respond_to? :rehash)
117
+ end
118
+ }
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env ruby
2
+ #-- ConnectionPool -- ODBA -- 08.03.2005 -- hwyss@ywesee.com
3
+
4
+ require 'dbi'
5
+ require 'thread'
6
+
7
+ module ODBA
8
+ class ConnectionPool
9
+ POOL_SIZE = 5
10
+ SETUP_RETRIES = 3
11
+ attr_reader :connections
12
+ # All connections are delegated to DBI. The constructor simply records
13
+ # the DBI-arguments and reuses them to setup connections when needed.
14
+ def initialize(*dbi_args)
15
+ @dbi_args = dbi_args
16
+ @opts = @dbi_args.last.is_a?(Hash) ? @dbi_args.pop : Hash.new
17
+ @connections = []
18
+ @mutex = Mutex.new
19
+ connect
20
+ end
21
+ def next_connection # :nodoc:
22
+ conn = nil
23
+ @mutex.synchronize {
24
+ conn = @connections.shift
25
+ }
26
+ yield(conn)
27
+ ensure
28
+ @mutex.synchronize {
29
+ @connections.push(conn)
30
+ }
31
+ end
32
+ def method_missing(method, *args, &block) # :nodoc:
33
+ tries = SETUP_RETRIES
34
+ begin
35
+ next_connection { |conn|
36
+ conn.send(method, *args, &block)
37
+ }
38
+ rescue NoMethodError, DBI::Error => e
39
+ warn e
40
+ if(tries > 0 && (!e.is_a?(DBI::ProgrammingError) \
41
+ || e.message == 'no connection to the server'))
42
+ sleep(SETUP_RETRIES - tries)
43
+ tries -= 1
44
+ reconnect
45
+ retry
46
+ else
47
+ raise
48
+ end
49
+ end
50
+ end
51
+ def size
52
+ @connections.size
53
+ end
54
+ alias :pool_size :size
55
+ def connect # :nodoc:
56
+ @mutex.synchronize { _connect }
57
+ end
58
+ def _connect # :nodoc:
59
+ POOL_SIZE.times {
60
+ conn = DBI.connect(*@dbi_args)
61
+ if encoding = @opts[:client_encoding]
62
+ conn.execute "SET CLIENT_ENCODING TO '#{encoding}'"
63
+ end
64
+ @connections.push(conn)
65
+ }
66
+ end
67
+ def disconnect # :nodoc:
68
+ @mutex.synchronize { _disconnect }
69
+ end
70
+ def _disconnect # :nodoc:
71
+ while(conn = @connections.shift)
72
+ begin
73
+ conn.disconnect
74
+ rescue DBI::InterfaceError, Exception
75
+ ## we're not interested, since we are disconnecting anyway
76
+ nil
77
+ end
78
+ end
79
+ end
80
+ def reconnect # :nodoc:
81
+ @mutex.synchronize {
82
+ _disconnect
83
+ _connect
84
+ }
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env ruby
2
+ # DRbWrapper -- ydim -- 11.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'drb'
5
+ require 'odba/persistable'
6
+ require 'odba/stub'
7
+ require 'odba/odba'
8
+ require 'drb/timeridconv'
9
+
10
+ module ODBA
11
+ class DRbWrapper
12
+ instance_methods.each { |m|
13
+ undef_method(m) unless m =~ /^(__)|(respond_to\?|object_id$)/ }
14
+ include DRb::DRbUndumped
15
+ def initialize(obj)
16
+ @obj = obj
17
+ end
18
+ def respond_to?(sym, *args)
19
+ super || @obj.respond_to?(sym, *args)
20
+ end
21
+ def method_missing(sym, *args)
22
+ if(block_given?)
23
+ res = @obj.__send__(sym, *args) { |*block_args|
24
+ yield *block_args.collect { |arg| __wrap(arg) }
25
+ }
26
+ __wrap(res)
27
+ else
28
+ res = @obj.__send__(sym, *args)
29
+ if(res.is_a?(Array))
30
+ res.collect { |item| __wrap(item) }
31
+ elsif(res.is_a?(Hash))
32
+ res.inject({}) { |memo, (key, value)|
33
+ memo.store(__wrap(key), __wrap(value))
34
+ memo
35
+ }
36
+ else
37
+ __wrap(res)
38
+ end
39
+ end
40
+ end
41
+ def __wrap(obj)
42
+ if(obj.is_a?(ODBA::Persistable))
43
+ DRbWrapper.new(obj.odba_instance)
44
+ else
45
+ obj
46
+ end
47
+ end
48
+ def __wrappee
49
+ @obj
50
+ end
51
+ end
52
+ class DRbIdConv < DRb::DRbIdConv
53
+ def initialize(*args)
54
+ super
55
+ @unsaved = {}
56
+ end
57
+ def odba_update(key, odba_id, object_id)
58
+ case key
59
+ when :store
60
+ @unsaved.store(object_id, odba_id)
61
+ when :clean, :delete
62
+ @unsaved.delete(object_id)
63
+ end
64
+ end
65
+ def to_obj(ref)
66
+ test = ref
67
+ if(test.is_a?(String) || (test = @unsaved[ref]))
68
+ DRbWrapper.new(ODBA.cache.fetch(test.to_i))
69
+ else
70
+ super
71
+ end
72
+ rescue RuntimeError => e
73
+ raise RangeError, e.message
74
+ end
75
+ def to_id(obj)
76
+ if(obj.is_a?(ODBA::Persistable))
77
+ if(obj.odba_unsaved?)
78
+ obj.odba_add_observer(self)
79
+ super
80
+ else
81
+ obj.odba_id.to_s
82
+ end
83
+ else
84
+ super
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ #-- IdServer -- odba -- 10.11.2004 -- hwyss@ywesee.com
3
+
4
+ require 'odba/persistable'
5
+ require 'thread'
6
+
7
+ module ODBA
8
+ class IdServer
9
+ include Persistable
10
+ ODBA_SERIALIZABLE = ['@ids']
11
+ ODBA_EXCLUDE_VARS = ['@mutex']
12
+ def initialize
13
+ @ids = {}
14
+ end
15
+ def next_id(key, startval=1)
16
+ @mutex ||= Mutex.new
17
+ res = nil
18
+ @mutex.synchronize {
19
+ @ids[key] ||= (startval - 1)
20
+ res = @ids[key] += 1
21
+ }
22
+ odba_store
23
+ res
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,395 @@
1
+ #!/usr/bin/env ruby
2
+ #-- Index -- odba -- 13.05.2004 -- rwaltert@ywesee.com
3
+
4
+ require 'odba/persistable'
5
+
6
+ module ODBA
7
+ # Indices in ODBA are defined by origin and target (which may be identical)
8
+ # Any time a Persistable of class _target_klass_ or _origin_klass_ is stored,
9
+ # all corresponding indices are updated. To make this possible, we have to tell
10
+ # Index, how to navigate from _origin_ to _target_ and vice versa.
11
+ # This entails the Limitation that these paths must not change without
12
+ # _origin_ and/or _target_ being stored.
13
+ # Further, _search_term_ must be resolved in relation to _origin_.
14
+ class IndexCommon # :nodoc: all
15
+ include Persistable
16
+ if RUBY_VERSION >= '1.9'
17
+ ODBA_EXCLUDE_VARS = [
18
+ :@proc_origin, :@proc_target, :@proc_resolve_search_term
19
+ ]
20
+ else
21
+ ODBA_EXCLUDE_VARS = [
22
+ '@proc_origin', '@proc_target', '@proc_resolve_search_term'
23
+ ]
24
+ end
25
+ attr_accessor :origin_klass, :target_klass, :resolve_origin, :resolve_target,
26
+ :resolve_search_term, :index_name, :dictionary, :class_filter
27
+ def initialize(index_definition, origin_module)
28
+ @origin_klass = origin_module.instance_eval(index_definition.origin_klass.to_s)
29
+ @target_klass = origin_module.instance_eval(index_definition.target_klass.to_s)
30
+ @resolve_origin = index_definition.resolve_origin
31
+ @resolve_target = index_definition.resolve_target
32
+ @index_name = index_definition.index_name
33
+ @resolve_search_term = index_definition.resolve_search_term
34
+ @dictionary = index_definition.dictionary
35
+ @class_filter = index_definition.class_filter
36
+ end
37
+ def current_origin_ids(target_id) # :nodoc:
38
+ ODBA.storage.index_origin_ids(@index_name, target_id)
39
+ end
40
+ def current_target_ids(origin_id) # :nodoc:
41
+ ODBA.storage.index_target_ids(@index_name, origin_id)
42
+ end
43
+ def delete(object) # :nodoc:
44
+ if(object.is_a?(@origin_klass))
45
+ ODBA.storage.delete_index_element(@index_name, object.odba_id,
46
+ 'origin_id')
47
+ end
48
+ if(object.is_a?(@target_klass))
49
+ ODBA.storage.delete_index_element(@index_name, object.odba_id,
50
+ 'target_id')
51
+ end
52
+ end
53
+ def delete_origin(origin_id, term) # :nodoc:
54
+ ODBA.storage.index_delete_origin(@index_name, origin_id, term)
55
+ end
56
+ def delete_target(origin_id, old_term, target_id) # :nodoc:
57
+ ODBA.storage.index_delete_target(@index_name, origin_id,
58
+ old_term, target_id)
59
+ end
60
+ def do_update_index(origin_id, term, target_id=nil) # :nodoc:
61
+ ODBA.storage.update_index(@index_name, origin_id, term, target_id)
62
+ end
63
+ def fill(targets)
64
+ @proc_origin = nil
65
+ rows = []
66
+ targets.flatten.each { |target|
67
+ target_id = target.odba_id
68
+ origins = proc_instance_origin.call(target)
69
+ origins.each { |origin|
70
+ search_terms(origin).each { |term|
71
+ do_update_index( origin.odba_id, term, target_id)
72
+ }
73
+ }
74
+ }
75
+ end
76
+ def keys(length=nil)
77
+ ODBA.storage.index_fetch_keys(@index_name, length).delete_if { |k|
78
+ k.empty? }
79
+ end
80
+ def matches(substring, limit=nil, offset=0)
81
+ ODBA.storage.index_matches @index_name, substring, limit, offset
82
+ end
83
+ def origin_class?(klass)
84
+ (@origin_klass == klass)
85
+ end
86
+ def proc_instance_origin # :nodoc:
87
+ if(@proc_origin.nil?)
88
+ if(@resolve_origin.to_s.empty?)
89
+ @proc_origin = Proc.new { |odba_item| [odba_item] }
90
+ else
91
+ src = <<-EOS
92
+ Proc.new { |odba_item|
93
+ res = [odba_item.#{@resolve_origin}]
94
+ res.flatten!
95
+ res.compact!
96
+ res
97
+ }
98
+ EOS
99
+ @proc_origin = eval(src)
100
+ end
101
+ end
102
+ @proc_origin
103
+ end
104
+ def proc_instance_target # :nodoc:
105
+ if(@proc_target.nil?)
106
+ if(@resolve_target.to_s.empty?)
107
+ @proc_target = Proc.new { |odba_item| [odba_item] }
108
+ #elsif(@resolve_target == :odba_skip)
109
+ # @proc_target = Proc.new { [] }
110
+ else
111
+ src = <<-EOS
112
+ Proc.new { |odba_item|
113
+ res = [odba_item.#{@resolve_target}]
114
+ res.flatten!
115
+ res.compact!
116
+ res
117
+ }
118
+ EOS
119
+ @proc_target = eval(src)
120
+ end
121
+ end
122
+ @proc_target
123
+ end
124
+ def proc_resolve_search_term # :nodoc:
125
+ if(@proc_resolve_search_term.nil?)
126
+ if(@resolve_search_term.to_s.empty?)
127
+ @proc_resolve_search_term = Proc.new { |origin|
128
+ origin.to_s.downcase
129
+ }
130
+ else
131
+ src = <<-EOS
132
+ Proc.new { |origin|
133
+ begin
134
+ origin.#{@resolve_search_term}
135
+ rescue NameError => e
136
+ nil
137
+ end
138
+ }
139
+ EOS
140
+ @proc_resolve_search_term = eval(src)
141
+ end
142
+ end
143
+ @proc_resolve_search_term
144
+ end
145
+ def search_term(origin) # :nodoc:
146
+ proc_resolve_search_term.call(origin)
147
+ end
148
+ def search_terms(origin)
149
+ [search_term(origin)].flatten.compact.uniq
150
+ end
151
+ def set_relevance(meta, rows) # :nodoc:
152
+ if(meta.respond_to?(:set_relevance))
153
+ rows.each { |row|
154
+ meta.set_relevance(row.at(0), row.at(1))
155
+ }
156
+ end
157
+ end
158
+ def update(object)
159
+ @class_filter ||= :is_a?
160
+ if(object.send(@class_filter, @target_klass))
161
+ update_target(object)
162
+ elsif(object.send(@class_filter, @origin_klass))
163
+ update_origin(object)
164
+ end
165
+ rescue StandardError => err
166
+ warn <<-EOS
167
+ #{err.class}: #{err.message} when updating index '#{@index_name}' with a #{object.class}
168
+ #{err.backtrace[0,4]}
169
+ [...]
170
+ EOS
171
+ end
172
+ def update_origin(object) # :nodoc:
173
+ origin_id = object.odba_id
174
+ search_terms = search_terms(object)
175
+ current = current_target_ids(origin_id)
176
+ target_ids = []
177
+ current_terms = []
178
+ current.each { |row|
179
+ target_ids.push(row[0])
180
+ current_terms.push(row[1])
181
+ }
182
+ current_terms.uniq!
183
+ target_ids.uniq!
184
+ (current_terms - search_terms).each { |term|
185
+ delete_origin(origin_id, term)
186
+ }
187
+ new_terms = search_terms - current_terms
188
+ unless(new_terms.empty?)
189
+ target_ids.each { |target_id|
190
+ new_terms.each { |term|
191
+ do_update_index(origin_id, term, target_id)
192
+ }
193
+ }
194
+ end
195
+ end
196
+ def update_target(target) # :nodoc:
197
+ target_id = target.odba_id
198
+ current = current_origin_ids(target_id)
199
+ old_terms = current.collect { |row|
200
+ [row[0], row[1]]
201
+ }
202
+ origins = proc_instance_origin.call(target)
203
+ new_terms = []
204
+ origins.each { |origin|
205
+ origin_id = origin.odba_id
206
+ search_terms(origin).each { |term|
207
+ new_terms.push([origin_id, term])
208
+ }
209
+ }
210
+ (old_terms - new_terms).each { |origin_id, terms|
211
+ delete_target(origin_id, terms, target_id)
212
+ }
213
+ (new_terms - old_terms).each { |origin_id, terms|
214
+ do_update_index(origin_id, terms, target_id)
215
+ }
216
+ end
217
+ end
218
+ # Currently there are 3 predefined Index-classes
219
+ # For Sample Code see
220
+ # http://dev.ywesee.com/wiki.php/ODBA/SimpleIndex
221
+ # http://dev.ywesee.com/wiki.php/ODBA/ConditionIndex
222
+ # http://dev.ywesee.com/wiki.php/ODBA/FulltextIndex
223
+ class Index < IndexCommon # :nodoc: all
224
+ def initialize(index_definition, origin_module) # :nodoc:
225
+ super(index_definition, origin_module)
226
+ ODBA.storage.create_index(index_definition.index_name)
227
+ end
228
+ def fetch_ids(search_term, meta=nil) # :nodoc:
229
+ exact = meta.respond_to?(:exact) && meta.exact
230
+ limit = meta.respond_to?(:limit) && meta.limit
231
+ rows = ODBA.storage.retrieve_from_index(@index_name,
232
+ search_term.to_s.downcase,
233
+ exact, limit)
234
+ set_relevance(meta, rows)
235
+ rows.collect { |row| row.at(0) }
236
+ end
237
+ def update_origin(object) # :nodoc:
238
+ # Possible changes:
239
+ # - search_terms of origin have changed
240
+ # - targets have changed, except if @resolve_target == :none
241
+ # => we need a matrix of all current [term, target_id]
242
+ # and of all new [term, target_id]
243
+ origin_id = object.odba_id
244
+ search_terms = search_terms(object)
245
+ current = current_target_ids(origin_id)
246
+ target_ids = if @resolve_target == :none
247
+ current.dup
248
+ else
249
+ proc_instance_target.call(object).collect { |obj|
250
+ obj.odba_id }
251
+ end
252
+ target_ids.compact!
253
+ target_ids.uniq!
254
+ current_ids = []
255
+ current_terms = []
256
+ current.each { |row|
257
+ current_ids.push(row[0])
258
+ current_terms.push(row[1])
259
+ }
260
+ current_ids.uniq!
261
+ current_terms.uniq!
262
+ current_combinations = current_ids.inject([]) { |memo, id|
263
+ current_terms.each { |term| memo.push [term, id] }
264
+ memo
265
+ }
266
+ combinations = target_ids.inject([]) { |memo, id|
267
+ search_terms.each { |term| memo.push [term, id] }
268
+ memo
269
+ }
270
+ (current_combinations - combinations).each { |pair|
271
+ delete_target(origin_id, *pair)
272
+ }
273
+ (combinations - current_combinations).each { |pair|
274
+ do_update_index(origin_id, *pair)
275
+ }
276
+ end
277
+ def search_terms(origin)
278
+ super.collect { |term| term.to_s.downcase }.uniq
279
+ end
280
+ end
281
+ class ConditionIndex < IndexCommon # :nodoc: all
282
+ def initialize(index_definition, origin_module) # :nodoc:
283
+ super(index_definition, origin_module)
284
+ definition = {}
285
+ @resolve_search_term = {}
286
+ index_definition.resolve_search_term.each { |name, info|
287
+ if(info.is_a?(String))
288
+ info = { 'resolve' => info }
289
+ end
290
+ if(info['type'].nil?)
291
+ info['type'] = 'text'
292
+ end
293
+ @resolve_search_term.store(name, info)
294
+ definition.store(name, info['type'])
295
+ }
296
+ ODBA.storage.create_condition_index(@index_name, definition)
297
+ end
298
+ def current_ids(rows, id_name)
299
+ rows.collect { |row|
300
+ [
301
+ row[id_name],
302
+ @resolve_search_term.keys.collect { |key|
303
+ [key.to_s, row[key]] }.sort,
304
+ ]
305
+ }
306
+ end
307
+ def current_origin_ids(target_id)
308
+ current_ids(ODBA.storage.condition_index_ids(@index_name,
309
+ target_id,
310
+ 'target_id'),
311
+ 'origin_id')
312
+ end
313
+ def current_target_ids(origin_id)
314
+ current_ids(ODBA.storage.condition_index_ids(@index_name,
315
+ origin_id,
316
+ 'origin_id'),
317
+ 'target_id')
318
+ end
319
+ def delete_origin(origin_id, search_terms)
320
+ ODBA.storage.condition_index_delete(@index_name, origin_id,
321
+ search_terms)
322
+ end
323
+ def delete_target(origin_id, search_terms, target_id)
324
+ ODBA.storage.condition_index_delete(@index_name, origin_id,
325
+ search_terms, target_id)
326
+ end
327
+ def do_update_index(origin_id, search_terms, target_id=nil) # :nodoc:
328
+ ODBA.storage.update_condition_index(@index_name, origin_id,
329
+ search_terms, target_id)
330
+ end
331
+ def fetch_ids(conditions, meta=nil) # :nodoc:
332
+ limit = meta.respond_to?(:limit) && meta.limit
333
+ rows = ODBA.storage.retrieve_from_condition_index(@index_name,
334
+ conditions,
335
+ limit)
336
+ set_relevance(meta, rows)
337
+ rows.collect { |row| row.at(0) }
338
+ end
339
+ def proc_resolve_search_term # :nodoc:
340
+ if(@proc_resolve_search_term.nil?)
341
+ src = <<-EOS
342
+ Proc.new { |origin|
343
+ values = {}
344
+ EOS
345
+ @resolve_search_term.each { |name, info|
346
+ src << <<-EOS
347
+ begin
348
+ values.store('#{name}', origin.#{info['resolve']})
349
+ rescue NameError
350
+ end
351
+ EOS
352
+ }
353
+ src << <<-EOS
354
+ values
355
+ }
356
+ EOS
357
+ @proc_resolve_search_term = eval(src)
358
+ end
359
+ @proc_resolve_search_term
360
+ end
361
+ def search_terms(origin)
362
+ super.collect { |data| data.to_a.sort }
363
+ end
364
+ end
365
+ class FulltextIndex < IndexCommon # :nodoc: all
366
+ def initialize(index_definition, origin_module) # :nodoc:
367
+ super(index_definition, origin_module)
368
+ ODBA.storage.create_fulltext_index(@index_name)
369
+ end
370
+ def current_origin_ids(target_id)
371
+ ODBA.storage.fulltext_index_delete(@index_name, target_id,
372
+ 'target_id')
373
+ []
374
+ end
375
+ def current_target_ids(origin_id)
376
+ ODBA.storage.fulltext_index_target_ids(@index_name, origin_id)
377
+ end
378
+ def delete_origin(origin_id, term)
379
+ ODBA.storage.fulltext_index_delete(@index_name, origin_id,
380
+ 'origin_id')
381
+ end
382
+ def fetch_ids(search_term, meta=nil) # :nodoc:
383
+ limit = meta.respond_to?(:limit) && meta.limit
384
+ rows = ODBA.storage.retrieve_from_fulltext_index(@index_name,
385
+ search_term, @dictionary, limit)
386
+ set_relevance(meta, rows)
387
+ rows.collect { |row| row.at(0) }
388
+ end
389
+ def do_update_index(origin_id, search_text, target_id=nil) # :nodoc:
390
+ ODBA.storage.update_fulltext_index(@index_name, origin_id,
391
+ search_text, target_id,
392
+ @dictionary)
393
+ end
394
+ end
395
+ end