omf_rete 0.5 → 0.6.1
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.
- data/README.md +82 -74
- data/lib/omf_rete/abstract_tuple_set.rb +5 -1
- data/lib/omf_rete/indexed_tuple_set.rb +84 -17
- data/lib/omf_rete/join_op.rb +78 -18
- data/lib/omf_rete/planner/join_plan.rb +16 -15
- data/lib/omf_rete/planner/plan_builder.rb +52 -48
- data/lib/omf_rete/planner/source_plan.rb +14 -13
- data/lib/omf_rete/store/alpha/alpha_element.rb +3 -74
- data/lib/omf_rete/store/alpha/alpha_inner_element.rb +8 -58
- data/lib/omf_rete/store/alpha/alpha_leaf_element.rb +18 -3
- data/lib/omf_rete/store/alpha_store.rb +118 -0
- data/lib/omf_rete/store/named_alpha_store.rb +38 -0
- data/lib/omf_rete/store/object_store.rb +119 -0
- data/lib/omf_rete/store/predicate_store.rb +96 -0
- data/lib/omf_rete/store.rb +58 -13
- data/lib/omf_rete/tuple.rb +53 -0
- data/lib/omf_rete/tuple_stream.rb +89 -60
- data/lib/omf_rete/version.rb +1 -1
- data/lib/omf_rete.rb +14 -4
- data/omf_rete.gemspec +3 -0
- data/tests/test_filter.rb +1 -1
- data/tests/test_join_op.rb +30 -0
- data/tests/test_named_store.rb +36 -0
- data/tests/test_object_store.rb +150 -0
- data/tests/test_planner.rb +72 -40
- data/tests/test_predicate_store.rb +95 -0
- data/tests/test_readme.rb +66 -0
- data/tests/test_store.rb +56 -0
- metadata +30 -5
- data/lib/omf_rete/store/alpha/alpha_store.rb +0 -197
@@ -4,8 +4,8 @@
|
|
4
4
|
# Monkey patch symbol to allow consistent ordering of set keys
|
5
5
|
unless (:test).respond_to? '<=>'
|
6
6
|
class Symbol
|
7
|
-
def <=>(o)
|
8
|
-
self.to_s <=> o.to_s
|
7
|
+
def <=>(o)
|
8
|
+
self.to_s <=> o.to_s
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -13,20 +13,20 @@ end
|
|
13
13
|
|
14
14
|
module OMF::Rete
|
15
15
|
module Planner
|
16
|
-
|
17
|
-
# The base exception for all errors related
|
16
|
+
|
17
|
+
# The base exception for all errors related
|
18
18
|
class PlannerException < Exception; end
|
19
19
|
|
20
20
|
require 'omf_rete/planner/source_plan'
|
21
21
|
require 'omf_rete/planner/plan_level_builder'
|
22
22
|
require 'omf_rete/planner/plan_set'
|
23
|
-
require 'omf_rete/planner/filter_plan'
|
23
|
+
require 'omf_rete/planner/filter_plan'
|
24
24
|
|
25
|
-
# This class builds all the possible plans for a given
|
25
|
+
# This class builds all the possible plans for a given
|
26
26
|
# query
|
27
27
|
#
|
28
28
|
class PlanBuilder
|
29
|
-
|
29
|
+
|
30
30
|
attr_reader :plan, :store
|
31
31
|
#
|
32
32
|
# query -- query consists of an array of tuple paterns with binding declarations
|
@@ -35,17 +35,17 @@ module OMF::Rete
|
|
35
35
|
def initialize(query, store, opts = {})
|
36
36
|
@store = store
|
37
37
|
@opts = opts
|
38
|
-
|
38
|
+
|
39
39
|
_parse_query(query)
|
40
|
-
|
40
|
+
|
41
41
|
@complete_plans = []
|
42
42
|
if (@source_cnt == 1)
|
43
43
|
# only one source means a trivial plan, the source itself
|
44
44
|
@complete_plans = @sources.to_a
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
def build()
|
50
50
|
level = 0
|
51
51
|
maxLevels = @source_cnt + 10 # pull the emergency breaks sometimes
|
@@ -54,24 +54,24 @@ module OMF::Rete
|
|
54
54
|
level += 1
|
55
55
|
end
|
56
56
|
if (@complete_plans.empty?)
|
57
|
-
raise PlannerException.new("Can't create plan")
|
57
|
+
raise PlannerException.new("Can't create plan")
|
58
58
|
end
|
59
59
|
@complete_plans
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
def each_plan()
|
63
63
|
@complete_plans.each do |p| yield(p) end
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
# Return plan with lowest cost
|
67
67
|
#
|
68
68
|
def best_plan()
|
69
69
|
# best_plan = nil
|
70
70
|
# lowest_cost = 9999999999
|
71
|
-
#
|
71
|
+
#
|
72
72
|
# each_plan do |plan|
|
73
73
|
# cost = plan.cost
|
74
|
-
# if (cost < lowest_cost)
|
74
|
+
# if (cost < lowest_cost)
|
75
75
|
# lowest_cost = cost
|
76
76
|
# best_plan = plan
|
77
77
|
# end
|
@@ -81,8 +81,8 @@ module OMF::Rete
|
|
81
81
|
end
|
82
82
|
best_plan
|
83
83
|
end
|
84
|
-
|
85
|
-
|
84
|
+
|
85
|
+
|
86
86
|
# Materialize the plan. Create all the relevant operations and tuple sets
|
87
87
|
# to realize a configuration for the respective query. Returns the result
|
88
88
|
# set.
|
@@ -99,7 +99,7 @@ module OMF::Rete
|
|
99
99
|
_materialize_simple_plan(projectPattern, plan, opts, &block)
|
100
100
|
else
|
101
101
|
# this is the root of the plan
|
102
|
-
if projectPattern
|
102
|
+
if projectPattern
|
103
103
|
description = projectPattern
|
104
104
|
else
|
105
105
|
description = plan.result_set.to_a.sort
|
@@ -109,8 +109,8 @@ module OMF::Rete
|
|
109
109
|
endS
|
110
110
|
end
|
111
111
|
end
|
112
|
-
|
113
|
-
|
112
|
+
|
113
|
+
|
114
114
|
def describe(out = STDOUT, offset = 0, incr = 2, sep = "\n")
|
115
115
|
out << "\n=========\n"
|
116
116
|
@complete_plans.each do |p|
|
@@ -118,35 +118,40 @@ module OMF::Rete
|
|
118
118
|
p.describe(out, offset, incr, sep)
|
119
119
|
end
|
120
120
|
end
|
121
|
-
|
121
|
+
|
122
122
|
private
|
123
123
|
|
124
124
|
# Parse +query+ which is an array of query tuples or filters.
|
125
|
-
#
|
125
|
+
#
|
126
126
|
# This method create a new +SourcePlan+ (to be attached to a store)
|
127
127
|
# for every query tuple in the +query+ array.
|
128
|
-
#
|
128
|
+
#
|
129
129
|
def _parse_query(query)
|
130
130
|
@query = query
|
131
131
|
@sources = Set.new
|
132
132
|
@filters = []
|
133
133
|
@plans = PlanSet.new
|
134
|
+
if query.length == 1 && (query[0].is_a? Array)
|
135
|
+
# it's only rule, so we can allow source plans without binding variables
|
136
|
+
query[0] = SourcePlan.new(query[0], @store, false)
|
137
|
+
end
|
134
138
|
query.each do |sp|
|
135
|
-
|
139
|
+
|
136
140
|
if sp.is_a? FilterPlan
|
137
141
|
@filters << sp
|
138
142
|
elsif sp.is_a? SourcePlan
|
139
143
|
@sources << sp
|
140
144
|
@plans << sp
|
141
145
|
elsif sp.is_a? Array
|
142
|
-
unless
|
146
|
+
unless @store.confirmLength(sp)
|
143
147
|
raise PlannerException.new("SubPlan: Expected array of store size, but got '#{sp}'")
|
144
148
|
end
|
145
149
|
begin
|
146
150
|
p = SourcePlan.new(sp, @store)
|
147
151
|
@sources << p
|
148
152
|
@plans << p
|
149
|
-
rescue NoBindingException
|
153
|
+
rescue NoBindingException => nex
|
154
|
+
raise nex
|
150
155
|
# ignore sources with no bindings in them
|
151
156
|
end
|
152
157
|
else
|
@@ -158,7 +163,7 @@ module OMF::Rete
|
|
158
163
|
raise PlannerException.new("Query '#{query}' seems to be empty")
|
159
164
|
end
|
160
165
|
end
|
161
|
-
|
166
|
+
|
162
167
|
#
|
163
168
|
# Array of sources from lower levels to build new plans from
|
164
169
|
#
|
@@ -180,7 +185,7 @@ module OMF::Rete
|
|
180
185
|
end
|
181
186
|
@plans
|
182
187
|
end
|
183
|
-
|
188
|
+
|
184
189
|
# Compare +plan+ with all remaining plans and create
|
185
190
|
# a new plan if it can be combined. If no new plan
|
186
191
|
# is created for +plan+ elevated it to this level.
|
@@ -192,8 +197,8 @@ module OMF::Rete
|
|
192
197
|
end
|
193
198
|
end
|
194
199
|
end
|
195
|
-
|
196
|
-
|
200
|
+
|
201
|
+
|
197
202
|
def _build_for(left, right)
|
198
203
|
# STDOUT.puts "CHECKING"
|
199
204
|
# STDOUT.puts " LEFT"
|
@@ -208,22 +213,22 @@ module OMF::Rete
|
|
208
213
|
if (lcover.size == combinedSize || rcover.size == combinedSize)
|
209
214
|
return nil # doesn't get us closer to a solution
|
210
215
|
end
|
211
|
-
|
216
|
+
|
212
217
|
joinSet = left.result_set.intersection(right.result_set)
|
213
218
|
if (joinSet.empty?)
|
214
|
-
return nil # nothing to join
|
219
|
+
return nil # nothing to join
|
215
220
|
end
|
216
|
-
|
221
|
+
|
217
222
|
resultSet = left.result_set + right.result_set
|
218
223
|
left.used
|
219
224
|
right.used
|
220
225
|
jp = JoinPlan.new(left, right, joinSet, resultSet, combinedCover, self)
|
221
226
|
_add_plan(jp)
|
222
227
|
end
|
223
|
-
|
228
|
+
|
224
229
|
def _add_plan(plan)
|
225
230
|
action = 'DUPLICATE: '
|
226
|
-
if (@plans << plan)
|
231
|
+
if (@plans << plan)
|
227
232
|
action = 'ADDED: '
|
228
233
|
if (plan.cover_set.size == @source_cnt)
|
229
234
|
action = 'COMPLETE: '
|
@@ -234,13 +239,12 @@ module OMF::Rete
|
|
234
239
|
# STDOUT << action
|
235
240
|
# plan.describe
|
236
241
|
end
|
237
|
-
|
242
|
+
|
238
243
|
# The +plan+ consists only of a source plan. Create
|
239
244
|
# a processing stream and attach a block which extracts
|
240
245
|
# the 'bound' elements from the incoming tuple.
|
241
246
|
#
|
242
247
|
def _materialize_simple_plan(projectPattern, plan, opts, &block)
|
243
|
-
|
244
248
|
unless projectPattern
|
245
249
|
# create one from the binding varibales in plan.description
|
246
250
|
projectPattern = []
|
@@ -249,16 +253,16 @@ module OMF::Rete
|
|
249
253
|
projectPattern << name.to_sym
|
250
254
|
end
|
251
255
|
end
|
252
|
-
if (projectPattern.empty?)
|
253
|
-
raise NoBindingException.new("No binding declaration in source plan '#{plan.description.join(', ')}'")
|
254
|
-
end
|
256
|
+
# if (projectPattern.empty?)
|
257
|
+
# raise NoBindingException.new("No binding declaration in source plan '#{plan.description.join(', ')}'")
|
258
|
+
# end
|
255
259
|
end
|
256
260
|
description = projectPattern
|
257
|
-
|
261
|
+
|
258
262
|
#src = plan.materialize(nil, projectPattern, opts)
|
259
|
-
src = ProcessingTupleStream.new(projectPattern, projectPattern, plan.description)
|
263
|
+
src = ProcessingTupleStream.new(projectPattern, projectPattern, plan.description)
|
260
264
|
frontS, endS = _materialize_result_stream(plan, projectPattern, opts, &block)
|
261
|
-
|
265
|
+
|
262
266
|
src.receiver = frontS
|
263
267
|
frontS.source = src
|
264
268
|
|
@@ -266,7 +270,7 @@ module OMF::Rete
|
|
266
270
|
|
267
271
|
endS
|
268
272
|
end
|
269
|
-
|
273
|
+
|
270
274
|
# This creates the result stream and stacks all filters on top (if any)
|
271
275
|
# It returns the first and last element as an array.
|
272
276
|
#
|
@@ -274,9 +278,9 @@ module OMF::Rete
|
|
274
278
|
plan_description = plan.result_description
|
275
279
|
description = projectPattern || plan.result_description
|
276
280
|
rs = ResultTupleStream.new(description, &block)
|
277
|
-
|
281
|
+
|
278
282
|
# This is a very naive plan to add filters. It simple stacks them all at the end.
|
279
|
-
# It would be much better to put them right after each source or join which produces
|
283
|
+
# It would be much better to put them right after each source or join which produces
|
280
284
|
# the matching binding stream.
|
281
285
|
#
|
282
286
|
first_filter = nil
|
@@ -295,7 +299,7 @@ module OMF::Rete
|
|
295
299
|
end
|
296
300
|
[first_filter || rs, rs]
|
297
301
|
end
|
298
|
-
|
302
|
+
|
299
303
|
|
300
304
|
end # PlanBuilder
|
301
305
|
end # Planner
|
@@ -1,28 +1,29 @@
|
|
1
1
|
|
2
2
|
require 'omf_rete/planner/plan_builder'
|
3
3
|
require 'omf_rete/planner/abstract_plan'
|
4
|
+
require 'omf_rete/indexed_tuple_set'
|
4
5
|
require 'set'
|
5
6
|
|
6
7
|
module OMF::Rete
|
7
8
|
module Planner
|
8
|
-
|
9
|
+
|
9
10
|
# Thrown if the plan doesn't contain any bindings
|
10
11
|
class NoBindingException < PlannerException; end
|
11
12
|
|
12
13
|
# This class represents a planned join op.
|
13
|
-
#
|
14
|
+
#
|
14
15
|
#
|
15
16
|
class SourcePlan < AbstractPlan
|
16
17
|
attr_reader :description
|
17
18
|
attr_reader :source_set # tuple set created by this plan
|
18
|
-
|
19
|
+
|
19
20
|
#
|
20
21
|
# description - description of tuples contained in set
|
21
22
|
# store - store to attach +source_set+ to
|
22
23
|
#
|
23
|
-
def initialize(description, store = nil)
|
24
|
+
def initialize(description, store = nil, check_for_empty_result_set = true)
|
24
25
|
@description = description
|
25
|
-
# the result set consists of all the binding declarations
|
26
|
+
# the result set consists of all the binding declarations
|
26
27
|
# which are symbols with trailing '?'
|
27
28
|
resultSet = Set.new
|
28
29
|
description.each do |name|
|
@@ -30,29 +31,29 @@ module OMF::Rete
|
|
30
31
|
resultSet << name.to_sym
|
31
32
|
end
|
32
33
|
end
|
33
|
-
if (resultSet.empty?)
|
34
|
+
if (check_for_empty_result_set && resultSet.empty?)
|
34
35
|
raise NoBindingException.new("No binding declaration in sub plan '#{description.join(', ')}'")
|
35
36
|
end
|
36
37
|
coverSet = Set.new([self])
|
37
|
-
super coverSet, resultSet
|
38
|
-
|
38
|
+
super coverSet, resultSet
|
39
|
+
|
39
40
|
#raise Exception unless store.kind_of?(Moana::Filter::Store)
|
40
41
|
@store = store
|
41
42
|
end
|
42
|
-
|
43
|
+
|
43
44
|
# Materialize the plan. Returns a tuple set.
|
44
45
|
#
|
45
46
|
def materialize(indexPattern, projectPattern, opts)
|
46
47
|
unless indexPattern
|
47
48
|
# this plan only consists of a single source
|
48
49
|
projectPattern ||= result_description
|
49
|
-
@source_set = ProcessingTupleStream.new(projectPattern, projectPattern, @description)
|
50
|
+
@source_set = ProcessingTupleStream.new(projectPattern, projectPattern, @description)
|
50
51
|
else
|
51
52
|
@source_set = OMF::Rete::IndexedTupleSet.new(@description, indexPattern)
|
52
53
|
end
|
53
54
|
@store.registerTSet(@source_set, @description) if @store
|
54
55
|
end
|
55
|
-
|
56
|
+
|
56
57
|
# Return the cost of this plan.
|
57
58
|
#
|
58
59
|
# TODO: Some more meaningful heuristic will be nice
|
@@ -65,8 +66,8 @@ module OMF::Rete
|
|
65
66
|
end
|
66
67
|
@cost
|
67
68
|
end
|
68
|
-
|
69
|
-
|
69
|
+
|
70
|
+
|
70
71
|
def describe(out = STDOUT, offset = 0, incr = 2, sep = "\n")
|
71
72
|
out.write(" " * offset)
|
72
73
|
desc = @description.collect do |e| e || '*' end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
|
2
2
|
module OMF::Rete::Store::Alpha
|
3
|
-
|
3
|
+
|
4
4
|
# Module internal class, will only be instantiated by +Store+
|
5
5
|
#
|
6
6
|
class AlphaElement
|
7
|
-
|
7
|
+
|
8
8
|
def self.create(level, length)
|
9
9
|
rem = length - level
|
10
10
|
if (rem > 1)
|
@@ -13,83 +13,12 @@ module OMF::Rete::Store::Alpha
|
|
13
13
|
AlphaLeafElement.new(level)
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def initialize(level)
|
18
18
|
@level = level
|
19
19
|
end
|
20
20
|
|
21
21
|
end
|
22
22
|
|
23
|
-
class AlphaInnerElement < AlphaElement
|
24
|
-
|
25
|
-
def initialize(level, length)
|
26
|
-
super(level)
|
27
|
-
@length = length
|
28
|
-
@children = {}
|
29
|
-
if (level < length)
|
30
|
-
@wildChild = AlphaElement.create(level + 1, length)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# see Store
|
35
|
-
#
|
36
|
-
def registerTSet(tset, pattern)
|
37
|
-
pitem = pattern[@level]
|
38
|
-
if (pitem) # not nil
|
39
|
-
child = (@children[pitem] ||= AlphaElement.create(@level + 1, @length))
|
40
|
-
child.registerTSet(tset, pattern)
|
41
|
-
else # wildcard
|
42
|
-
@wildChild.registerTSet(tset, pattern)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
|
47
|
-
def addTuple(tarray)
|
48
|
-
el = tarray[@level]
|
49
|
-
if (child = @children[el])
|
50
|
-
child.addTuple(tarray)
|
51
|
-
end
|
52
|
-
@wildChild.addTuple(tarray) if (@wildChild)
|
53
|
-
end
|
54
|
-
end # AlphaInnerElement
|
55
|
-
|
56
|
-
# Module internal class, will only be instantiated by +Store+
|
57
|
-
#
|
58
|
-
class AlphaLeafElement < AlphaElement
|
59
|
-
|
60
|
-
def initialize(level)
|
61
|
-
super
|
62
|
-
@tsetIndex = {}
|
63
|
-
@tsetWildcards = []
|
64
|
-
end
|
65
|
-
|
66
|
-
# see Store
|
67
|
-
#
|
68
|
-
def registerTSet(tset, pattern)
|
69
|
-
pitem = pattern[@level]
|
70
|
-
leaf = (@level == @length)
|
71
|
-
if (pitem) # not nil
|
72
|
-
(@tsetIndex[pitem] ||= []) << tset
|
73
|
-
else # wildcard
|
74
|
-
@tsetWildcards << tset
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def addTuple(tarray)
|
79
|
-
# check if we have any matching tsets
|
80
|
-
item = tarray[@level]
|
81
|
-
if (arr = @tsetIndex[item])
|
82
|
-
arr.each do |s|
|
83
|
-
s.addTuple(tarray)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
@tsetWildcards.each do |s|
|
87
|
-
s.addTuple(tarray)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
|
92
23
|
|
93
|
-
end # AlphaLeafElement
|
94
|
-
|
95
24
|
end # Moana::Filter::Store::Alpha
|
@@ -1,28 +1,9 @@
|
|
1
1
|
require 'omf_rete/store/alpha/alpha_element'
|
2
2
|
|
3
3
|
module OMF::Rete::Store::Alpha
|
4
|
-
|
5
|
-
# Module internal class, will only be instantiated by +Store+
|
6
|
-
#
|
7
|
-
class AlphaElement
|
8
|
-
|
9
|
-
def self.create(level, length)
|
10
|
-
rem = length - level
|
11
|
-
if (rem > 1)
|
12
|
-
AlphaInnerElement.new(level, length)
|
13
|
-
else
|
14
|
-
AlphaLeafElement.new(level)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def initialize(level)
|
19
|
-
@level = level
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
4
|
|
24
5
|
class AlphaInnerElement < AlphaElement
|
25
|
-
|
6
|
+
|
26
7
|
def initialize(level, length)
|
27
8
|
super(level)
|
28
9
|
@length = length
|
@@ -31,7 +12,7 @@ module OMF::Rete::Store::Alpha
|
|
31
12
|
@wildChild = AlphaElement.create(level + 1, length)
|
32
13
|
end
|
33
14
|
end
|
34
|
-
|
15
|
+
|
35
16
|
# see Store
|
36
17
|
#
|
37
18
|
def registerTSet(tset, pattern)
|
@@ -44,7 +25,6 @@ module OMF::Rete::Store::Alpha
|
|
44
25
|
end
|
45
26
|
end
|
46
27
|
|
47
|
-
|
48
28
|
def addTuple(tarray)
|
49
29
|
el = tarray[@level]
|
50
30
|
if (child = @children[el])
|
@@ -52,45 +32,15 @@ module OMF::Rete::Store::Alpha
|
|
52
32
|
end
|
53
33
|
@wildChild.addTuple(tarray) if (@wildChild)
|
54
34
|
end
|
55
|
-
end # AlphaInnerElement
|
56
|
-
|
57
|
-
# Module internal class, will only be instantiated by +Store+
|
58
|
-
#
|
59
|
-
class AlphaLeafElement < AlphaElement
|
60
|
-
|
61
|
-
def initialize(level)
|
62
|
-
super
|
63
|
-
@tsetIndex = {}
|
64
|
-
@tsetWildcards = []
|
65
|
-
end
|
66
|
-
|
67
|
-
# see Store
|
68
|
-
#
|
69
|
-
def registerTSet(tset, pattern)
|
70
|
-
pitem = pattern[@level]
|
71
|
-
leaf = (@level == @length)
|
72
|
-
if (pitem) # not nil
|
73
|
-
(@tsetIndex[pitem] ||= []) << tset
|
74
|
-
else # wildcard
|
75
|
-
@tsetWildcards << tset
|
76
|
-
end
|
77
|
-
end
|
78
35
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
arr.each do |s|
|
84
|
-
s.addTuple(tarray)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
@tsetWildcards.each do |s|
|
88
|
-
s.addTuple(tarray)
|
36
|
+
def removeTuple(tarray)
|
37
|
+
el = tarray[@level]
|
38
|
+
if (child = @children[el])
|
39
|
+
child.removeTuple(tarray)
|
89
40
|
end
|
41
|
+
@wildChild.removeTuple(tarray) if (@wildChild)
|
90
42
|
end
|
91
|
-
|
92
43
|
|
44
|
+
end # AlphaInnerElement
|
93
45
|
|
94
|
-
end # AlphaLeafElement
|
95
|
-
|
96
46
|
end # Moana::Filter::Store::Alpha
|
@@ -2,7 +2,7 @@
|
|
2
2
|
require 'omf_rete/store/alpha/alpha_element'
|
3
3
|
|
4
4
|
module OMF::Rete::Store::Alpha
|
5
|
-
|
5
|
+
|
6
6
|
# Module internal class, will only be instantiated by +Store+
|
7
7
|
#
|
8
8
|
class AlphaLeafElement < AlphaElement
|
@@ -12,7 +12,7 @@ module OMF::Rete::Store::Alpha
|
|
12
12
|
@tsetIndex = {}
|
13
13
|
@tsetWildcards = []
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
# see Store
|
17
17
|
#
|
18
18
|
def registerTSet(tset, pattern)
|
@@ -37,5 +37,20 @@ module OMF::Rete::Store::Alpha
|
|
37
37
|
s.addTuple(tarray)
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
|
+
def removeTuple(tarray)
|
42
|
+
# check if we have any matching tsets
|
43
|
+
item = tarray[@level]
|
44
|
+
if (arr = @tsetIndex[item])
|
45
|
+
arr.each do |s|
|
46
|
+
s.removeTuple(tarray)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@tsetWildcards.each do |s|
|
50
|
+
s.removeTuple(tarray)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end # class
|
55
|
+
|
41
56
|
end # Moana::Filter::Store::Alpha
|