predictor 1.0.0 → 2.0.0.rc1
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.
- checksums.yaml +4 -4
- data/Changelog.md +13 -0
- data/Gemfile +1 -1
- data/README.md +75 -32
- data/docs/READMEv1.md +206 -0
- data/lib/predictor/base.rb +128 -60
- data/lib/predictor/input_matrix.rb +29 -82
- data/lib/predictor/version.rb +1 -1
- data/spec/base_spec.rb +160 -94
- data/spec/input_matrix_spec.rb +30 -160
- data/spec/predictor_spec.rb +1 -1
- metadata +6 -4
@@ -3,6 +3,10 @@ class Predictor::InputMatrix
|
|
3
3
|
@opts = opts
|
4
4
|
end
|
5
5
|
|
6
|
+
def parent_redis_key(*append)
|
7
|
+
([@opts.fetch(:redis_prefix)] + append).flatten.compact.join(":")
|
8
|
+
end
|
9
|
+
|
6
10
|
def redis_key(*append)
|
7
11
|
([@opts.fetch(:redis_prefix), @opts.fetch(:key)] + append).flatten.compact.join(":")
|
8
12
|
end
|
@@ -11,30 +15,19 @@ class Predictor::InputMatrix
|
|
11
15
|
(@opts[:weight] || 1).to_f
|
12
16
|
end
|
13
17
|
|
14
|
-
def
|
15
|
-
|
16
|
-
item_ids.each { |item| add_single_nomulti(set_id, item) }
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def add_set!(set_id, item_ids)
|
21
|
-
add_set(set_id, item_ids)
|
22
|
-
item_ids.each { |item_id| process_item!(item_id) }
|
23
|
-
end
|
24
|
-
|
25
|
-
def add_single(set_id, item_id)
|
18
|
+
def add_to_set(set, *items)
|
19
|
+
items = items.flatten if items.count == 1 && items[0].is_a?(Array)
|
26
20
|
Predictor.redis.multi do
|
27
|
-
add_single_nomulti(
|
21
|
+
items.each { |item| add_single_nomulti(set, item) }
|
28
22
|
end
|
29
23
|
end
|
30
24
|
|
31
|
-
def
|
32
|
-
|
33
|
-
process_item!(item_id)
|
25
|
+
def add_set(set, items)
|
26
|
+
add_to_set(set, *items)
|
34
27
|
end
|
35
28
|
|
36
|
-
def
|
37
|
-
|
29
|
+
def add_single(set, item)
|
30
|
+
add_to_set(set, item)
|
38
31
|
end
|
39
32
|
|
40
33
|
def items_for(set)
|
@@ -45,81 +38,26 @@ class Predictor::InputMatrix
|
|
45
38
|
Predictor.redis.sunion redis_key(:sets, item)
|
46
39
|
end
|
47
40
|
|
48
|
-
def related_items(
|
49
|
-
sets = Predictor.redis.smembers(redis_key(:sets,
|
41
|
+
def related_items(item)
|
42
|
+
sets = Predictor.redis.smembers(redis_key(:sets, item))
|
50
43
|
keys = sets.map { |set| redis_key(:items, set) }
|
51
|
-
|
52
|
-
Predictor.redis.sunion(keys) - [item_id]
|
53
|
-
else
|
54
|
-
[]
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def similarity(item1, item2)
|
59
|
-
Predictor.redis.zscore(redis_key(:similarities, item1), item2)
|
60
|
-
end
|
61
|
-
|
62
|
-
# calculate all similarities to other items in the matrix for item1
|
63
|
-
def similarities_for(item1, with_scores: false, offset: 0, limit: -1)
|
64
|
-
Predictor.redis.zrevrange(redis_key(:similarities, item1), offset, limit == -1 ? limit : offset + (limit - 1), with_scores: with_scores)
|
65
|
-
end
|
66
|
-
|
67
|
-
def process_item!(item)
|
68
|
-
cache_similarities_for(item)
|
69
|
-
end
|
70
|
-
|
71
|
-
def process!
|
72
|
-
all_items.each do |item|
|
73
|
-
process_item!(item)
|
74
|
-
end
|
44
|
+
keys.length > 0 ? Predictor.redis.sunion(keys) - [item] : []
|
75
45
|
end
|
76
46
|
|
77
|
-
# delete
|
78
|
-
def delete_item
|
79
|
-
Predictor.redis.
|
80
|
-
|
81
|
-
sets = Predictor.redis.smembers(redis_key(:sets, item_id))
|
82
|
-
items = Predictor.redis.zrange(redis_key(:similarities, item_id), 0, -1)
|
47
|
+
# delete item from the matrix
|
48
|
+
def delete_item(item)
|
49
|
+
Predictor.redis.watch(redis_key(:sets, item)) do
|
50
|
+
sets = Predictor.redis.smembers(redis_key(:sets, item))
|
83
51
|
Predictor.redis.multi do |multi|
|
84
52
|
sets.each do |set|
|
85
|
-
multi.srem(redis_key(:items, set),
|
53
|
+
multi.srem(redis_key(:items, set), item)
|
86
54
|
end
|
87
55
|
|
88
|
-
|
89
|
-
multi.zrem(redis_key(:similarities, item), item_id)
|
90
|
-
end
|
91
|
-
|
92
|
-
multi.del redis_key(:sets, item_id), redis_key(:similarities, item_id)
|
56
|
+
multi.del redis_key(:sets, item)
|
93
57
|
end
|
94
58
|
end
|
95
59
|
end
|
96
60
|
|
97
|
-
private
|
98
|
-
|
99
|
-
def add_single_nomulti(set_id, item_id)
|
100
|
-
Predictor.redis.sadd(redis_key(:all_items), item_id)
|
101
|
-
Predictor.redis.sadd(redis_key(:items, set_id), item_id)
|
102
|
-
# add the set_id to the item_id's set--inverting the sets
|
103
|
-
Predictor.redis.sadd(redis_key(:sets, item_id), set_id)
|
104
|
-
end
|
105
|
-
|
106
|
-
def cache_similarity(item1, item2)
|
107
|
-
score = calculate_jaccard(item1, item2)
|
108
|
-
|
109
|
-
if score > 0
|
110
|
-
Predictor.redis.multi do |multi|
|
111
|
-
multi.zadd(redis_key(:similarities, item1), score, item2)
|
112
|
-
multi.zadd(redis_key(:similarities, item2), score, item1)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def cache_similarities_for(item)
|
118
|
-
related_items(item).each do |related_item|
|
119
|
-
cache_similarity(item, related_item)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
61
|
def calculate_jaccard(item1, item2)
|
124
62
|
x = nil
|
125
63
|
y = nil
|
@@ -135,4 +73,13 @@ class Predictor::InputMatrix
|
|
135
73
|
return 0.0
|
136
74
|
end
|
137
75
|
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def add_single_nomulti(set, item)
|
80
|
+
Predictor.redis.sadd(parent_redis_key(:all_items), item)
|
81
|
+
Predictor.redis.sadd(redis_key(:items, set), item)
|
82
|
+
# add the set to the item's set--inverting the sets
|
83
|
+
Predictor.redis.sadd(redis_key(:sets, item), set)
|
84
|
+
end
|
138
85
|
end
|
data/lib/predictor/version.rb
CHANGED
data/spec/base_spec.rb
CHANGED
@@ -8,6 +8,7 @@ describe Predictor::Base do
|
|
8
8
|
before(:each) do
|
9
9
|
flush_redis!
|
10
10
|
BaseRecommender.input_matrices = {}
|
11
|
+
BaseRecommender.limit_similarities_to(nil)
|
11
12
|
end
|
12
13
|
|
13
14
|
describe "configuration" do
|
@@ -16,6 +17,11 @@ describe Predictor::Base do
|
|
16
17
|
BaseRecommender.input_matrices.keys.should == [:myinput]
|
17
18
|
end
|
18
19
|
|
20
|
+
it "should allow a similarity limit" do
|
21
|
+
BaseRecommender.limit_similarities_to(100)
|
22
|
+
BaseRecommender.similarity_limit.should == 100
|
23
|
+
end
|
24
|
+
|
19
25
|
it "should retrieve an input_matrix on a new instance" do
|
20
26
|
BaseRecommender.input_matrix(:myinput)
|
21
27
|
sm = BaseRecommender.new
|
@@ -37,67 +43,56 @@ describe Predictor::Base do
|
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
40
|
-
describe "
|
41
|
-
it "
|
42
|
-
BaseRecommender.input_matrix(:
|
43
|
-
BaseRecommender.input_matrix(:
|
44
|
-
sm = BaseRecommender.new
|
45
|
-
sm.myfirstinput.should_receive(:process_item!).with("fnorditem").and_return([["fooitem",0.5]])
|
46
|
-
sm.mysecondinput.should_receive(:process_item!).with("fnorditem").and_return([["fooitem",0.5]])
|
47
|
-
sm.process_item!("fnorditem")
|
48
|
-
end
|
49
|
-
|
50
|
-
it "should call process_item! on each input_matrix and add all outputs to the similarity matrix" do
|
51
|
-
BaseRecommender.input_matrix(:myfirstinput)
|
52
|
-
BaseRecommender.input_matrix(:mysecondinput)
|
46
|
+
describe "all_items" do
|
47
|
+
it "returns all items across all matrices" do
|
48
|
+
BaseRecommender.input_matrix(:anotherinput)
|
49
|
+
BaseRecommender.input_matrix(:yetanotherinput)
|
53
50
|
sm = BaseRecommender.new
|
54
|
-
sm.
|
55
|
-
sm.
|
56
|
-
sm.
|
51
|
+
sm.add_to_matrix(:anotherinput, 'a', "foo", "bar")
|
52
|
+
sm.add_to_matrix(:yetanotherinput, 'b', "fnord", "shmoo", "bar")
|
53
|
+
sm.all_items.should include('foo', 'bar', 'fnord', 'shmoo')
|
54
|
+
sm.all_items.length.should == 4
|
57
55
|
end
|
56
|
+
end
|
58
57
|
|
59
|
-
|
60
|
-
|
61
|
-
BaseRecommender.input_matrix(:
|
58
|
+
describe "add_to_matrix" do
|
59
|
+
it "calls add_to_set on the given matrix" do
|
60
|
+
BaseRecommender.input_matrix(:anotherinput)
|
62
61
|
sm = BaseRecommender.new
|
63
|
-
sm.
|
64
|
-
sm.
|
65
|
-
sm.process_item!("fnorditem")
|
62
|
+
sm.anotherinput.should_receive(:add_to_set).with('a', 'foo', 'bar')
|
63
|
+
sm.add_to_matrix(:anotherinput, 'a', 'foo', 'bar')
|
66
64
|
end
|
67
|
-
end
|
68
65
|
|
69
|
-
|
70
|
-
it "should retrieve all items from all input matrices" do
|
66
|
+
it "adds the items to the all_items storage" do
|
71
67
|
BaseRecommender.input_matrix(:anotherinput)
|
72
|
-
BaseRecommender.input_matrix(:yetanotherinput)
|
73
68
|
sm = BaseRecommender.new
|
74
|
-
sm.anotherinput
|
75
|
-
sm.
|
76
|
-
sm.all_items.length.should == 4
|
77
|
-
sm.all_items.should include("foo", "bar", "fnord", "shmoo")
|
69
|
+
sm.add_to_matrix(:anotherinput, 'a', 'foo', 'bar')
|
70
|
+
sm.all_items.should include('foo', 'bar')
|
78
71
|
end
|
72
|
+
end
|
79
73
|
|
80
|
-
|
74
|
+
describe "add_to_matrix!" do
|
75
|
+
it "calls add_to_matrix and process_items! for the given items" do
|
81
76
|
BaseRecommender.input_matrix(:anotherinput)
|
82
|
-
BaseRecommender.input_matrix(:yetanotherinput)
|
83
77
|
sm = BaseRecommender.new
|
84
|
-
sm.
|
85
|
-
sm.
|
86
|
-
sm.
|
87
|
-
sm.all_items.should include("foo", "bar", "fnord")
|
78
|
+
sm.should_receive(:add_to_matrix).with(:anotherinput, 'a', 'foo')
|
79
|
+
sm.should_receive(:process_items!).with('foo')
|
80
|
+
sm.add_to_matrix!(:anotherinput, 'a', 'foo')
|
88
81
|
end
|
89
82
|
end
|
90
83
|
|
91
|
-
describe "
|
92
|
-
it "
|
84
|
+
describe "related_items" do
|
85
|
+
it "returns items in the sets across all matrices that the given item is also in" do
|
93
86
|
BaseRecommender.input_matrix(:anotherinput)
|
94
87
|
BaseRecommender.input_matrix(:yetanotherinput)
|
88
|
+
BaseRecommender.input_matrix(:finalinput)
|
95
89
|
sm = BaseRecommender.new
|
96
|
-
sm.anotherinput.
|
97
|
-
sm.yetanotherinput.
|
98
|
-
sm.
|
99
|
-
sm.yetanotherinput.should_receive(:process!).exactly(1).times
|
90
|
+
sm.anotherinput.add_to_set('a', "foo", "bar")
|
91
|
+
sm.yetanotherinput.add_to_set('b', "fnord", "shmoo", "bar")
|
92
|
+
sm.finalinput.add_to_set('c', "nada")
|
100
93
|
sm.process!
|
94
|
+
sm.related_items("bar").should include("foo", "fnord", "shmoo")
|
95
|
+
sm.related_items("bar").length.should == 3
|
101
96
|
end
|
102
97
|
end
|
103
98
|
|
@@ -106,13 +101,13 @@ describe Predictor::Base do
|
|
106
101
|
BaseRecommender.input_matrix(:users, weight: 4.0)
|
107
102
|
BaseRecommender.input_matrix(:tags, weight: 1.0)
|
108
103
|
sm = BaseRecommender.new
|
109
|
-
sm.users.
|
110
|
-
sm.users.
|
111
|
-
sm.users.
|
112
|
-
sm.users.
|
113
|
-
sm.tags.
|
114
|
-
sm.tags.
|
115
|
-
sm.tags.
|
104
|
+
sm.users.add_to_set('me', "foo", "bar", "fnord")
|
105
|
+
sm.users.add_to_set('not_me', "foo", "shmoo")
|
106
|
+
sm.users.add_to_set('another', "fnord", "other")
|
107
|
+
sm.users.add_to_set('another', "nada")
|
108
|
+
sm.tags.add_to_set('tag1', "foo", "fnord", "shmoo")
|
109
|
+
sm.tags.add_to_set('tag2', "bar", "shmoo")
|
110
|
+
sm.tags.add_to_set('tag3', "shmoo", "nada")
|
116
111
|
sm.process!
|
117
112
|
predictions = sm.predictions_for('me', matrix_label: :users)
|
118
113
|
predictions.should == ["shmoo", "other", "nada"]
|
@@ -123,39 +118,9 @@ describe Predictor::Base do
|
|
123
118
|
predictions = sm.predictions_for('me', matrix_label: :users, offset: 1)
|
124
119
|
predictions.should == ["other", "nada"]
|
125
120
|
end
|
126
|
-
|
127
|
-
it "correctly normalizes predictions" do
|
128
|
-
BaseRecommender.input_matrix(:users, weight: 1.0)
|
129
|
-
BaseRecommender.input_matrix(:tags, weight: 2.0)
|
130
|
-
BaseRecommender.input_matrix(:topics, weight: 4.0)
|
131
|
-
|
132
|
-
sm = BaseRecommender.new
|
133
|
-
|
134
|
-
sm.users.add_set('user1', ["c1", "c2", "c4"])
|
135
|
-
sm.users.add_set('user2', ["c3", "c4"])
|
136
|
-
sm.topics.add_set('topic1', ["c1", "c4"])
|
137
|
-
sm.topics.add_set('topic2', ["c2", "c3"])
|
138
|
-
sm.tags.add_set('tag1', ["c1", "c2", "c4"])
|
139
|
-
sm.tags.add_set('tag2', ["c1", "c4"])
|
140
|
-
|
141
|
-
sm.process!
|
142
|
-
|
143
|
-
predictions = sm.predictions_for('user1', matrix_label: :users, with_scores: true, normalize: false)
|
144
|
-
predictions.should eq([["c3", 4.5]])
|
145
|
-
predictions = sm.predictions_for('user2', matrix_label: :users, with_scores: true, normalize: false)
|
146
|
-
predictions.should eq([["c1", 6.5], ["c2", 5.5]])
|
147
|
-
predictions = sm.predictions_for('user1', matrix_label: :users, with_scores: true, normalize: true)
|
148
|
-
predictions[0][0].should eq("c3")
|
149
|
-
predictions[0][1].should be_within(0.001).of(0.592)
|
150
|
-
predictions = sm.predictions_for('user2', matrix_label: :users, with_scores: true, normalize: true)
|
151
|
-
predictions[0][0].should eq("c2")
|
152
|
-
predictions[0][1].should be_within(0.001).of(1.065)
|
153
|
-
predictions[1][0].should eq("c1")
|
154
|
-
predictions[1][1].should be_within(0.001).of(0.764)
|
155
|
-
end
|
156
121
|
end
|
157
122
|
|
158
|
-
describe "similarities_for
|
123
|
+
describe "similarities_for" do
|
159
124
|
it "should not throw exception for non existing items" do
|
160
125
|
sm = BaseRecommender.new
|
161
126
|
sm.similarities_for("not_existing_item").length.should == 0
|
@@ -168,12 +133,12 @@ describe Predictor::Base do
|
|
168
133
|
|
169
134
|
sm = BaseRecommender.new
|
170
135
|
|
171
|
-
sm.users.
|
172
|
-
sm.users.
|
173
|
-
sm.topics.
|
174
|
-
sm.topics.
|
175
|
-
sm.tags.
|
176
|
-
sm.tags.
|
136
|
+
sm.users.add_to_set('user1', "c1", "c2", "c4")
|
137
|
+
sm.users.add_to_set('user2', "c3", "c4")
|
138
|
+
sm.topics.add_to_set('topic1', "c1", "c4")
|
139
|
+
sm.topics.add_to_set('topic2', "c2", "c3")
|
140
|
+
sm.tags.add_to_set('tag1', "c1", "c2", "c4")
|
141
|
+
sm.tags.add_to_set('tag2', "c1", "c4")
|
177
142
|
|
178
143
|
sm.process!
|
179
144
|
sm.similarities_for("c1", with_scores: true).should eq([["c4", 6.5], ["c2", 2.0]])
|
@@ -188,24 +153,125 @@ describe Predictor::Base do
|
|
188
153
|
BaseRecommender.input_matrix(:set1)
|
189
154
|
BaseRecommender.input_matrix(:set2)
|
190
155
|
sm = BaseRecommender.new
|
191
|
-
sm.set1.
|
192
|
-
sm.set1.
|
193
|
-
sm.set2.
|
156
|
+
sm.set1.add_to_set "item1", "foo", "bar"
|
157
|
+
sm.set1.add_to_set "item2", "nada", "bar"
|
158
|
+
sm.set2.add_to_set "item3", "bar", "other"
|
194
159
|
sm.sets_for("bar").length.should == 3
|
195
160
|
sm.sets_for("bar").should include("item1", "item2", "item3")
|
196
161
|
sm.sets_for("other").should == ["item3"]
|
197
162
|
end
|
198
163
|
end
|
199
164
|
|
165
|
+
describe "process_items!" do
|
166
|
+
context "with no similarity_limit" do
|
167
|
+
it "calculates the similarity between the item and all related_items (other items in a set the given item is in)" do
|
168
|
+
BaseRecommender.input_matrix(:myfirstinput)
|
169
|
+
BaseRecommender.input_matrix(:mysecondinput)
|
170
|
+
BaseRecommender.input_matrix(:mythirdinput, weight: 3.0)
|
171
|
+
sm = BaseRecommender.new
|
172
|
+
sm.myfirstinput.add_to_set 'set1', 'item1', 'item2'
|
173
|
+
sm.mysecondinput.add_to_set 'set2', 'item2', 'item3'
|
174
|
+
sm.mythirdinput.add_to_set 'set3', 'item2', 'item3'
|
175
|
+
sm.mythirdinput.add_to_set 'set4', 'item1', 'item2', 'item3'
|
176
|
+
sm.similarities_for('item2').should be_empty
|
177
|
+
sm.process_items!('item2')
|
178
|
+
similarities = sm.similarities_for('item2', with_scores: true)
|
179
|
+
similarities.should include(["item3", 4.0], ["item1", 2.5])
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context "with a similarity_limit" do
|
184
|
+
it "calculates the similarity between the item and all related_items (other items in a set the given item is in), but obeys the similarity_limit" do
|
185
|
+
BaseRecommender.input_matrix(:myfirstinput)
|
186
|
+
BaseRecommender.input_matrix(:mysecondinput)
|
187
|
+
BaseRecommender.input_matrix(:mythirdinput, weight: 3.0)
|
188
|
+
BaseRecommender.limit_similarities_to(1)
|
189
|
+
sm = BaseRecommender.new
|
190
|
+
sm.myfirstinput.add_to_set 'set1', 'item1', 'item2'
|
191
|
+
sm.mysecondinput.add_to_set 'set2', 'item2', 'item3'
|
192
|
+
sm.mythirdinput.add_to_set 'set3', 'item2', 'item3'
|
193
|
+
sm.mythirdinput.add_to_set 'set4', 'item1', 'item2', 'item3'
|
194
|
+
sm.similarities_for('item2').should be_empty
|
195
|
+
sm.process_items!('item2')
|
196
|
+
similarities = sm.similarities_for('item2', with_scores: true)
|
197
|
+
similarities.should include(["item3", 4.0])
|
198
|
+
similarities.length.should == 1
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "process!" do
|
204
|
+
it "should call process_items for all_items's" do
|
205
|
+
BaseRecommender.input_matrix(:anotherinput)
|
206
|
+
BaseRecommender.input_matrix(:yetanotherinput)
|
207
|
+
sm = BaseRecommender.new
|
208
|
+
sm.anotherinput.add_to_set('a', "foo", "bar")
|
209
|
+
sm.yetanotherinput.add_to_set('b', "fnord", "shmoo")
|
210
|
+
sm.all_items.should include("foo", "bar", "fnord", "shmoo")
|
211
|
+
sm.should_receive(:process_items!).with(*sm.all_items)
|
212
|
+
sm.process!
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "delete_from_matrix!" do
|
217
|
+
it "calls delete_item on the matrix" do
|
218
|
+
BaseRecommender.input_matrix(:anotherinput)
|
219
|
+
BaseRecommender.input_matrix(:yetanotherinput)
|
220
|
+
sm = BaseRecommender.new
|
221
|
+
sm.anotherinput.add_to_set('a', "foo", "bar")
|
222
|
+
sm.yetanotherinput.add_to_set('b', "bar", "shmoo")
|
223
|
+
sm.process!
|
224
|
+
sm.similarities_for('bar').should include('foo', 'shmoo')
|
225
|
+
sm.anotherinput.should_receive(:delete_item).with('foo')
|
226
|
+
sm.delete_from_matrix!(:anotherinput, 'foo')
|
227
|
+
end
|
228
|
+
|
229
|
+
it "updates similarities" do
|
230
|
+
BaseRecommender.input_matrix(:anotherinput)
|
231
|
+
BaseRecommender.input_matrix(:yetanotherinput)
|
232
|
+
sm = BaseRecommender.new
|
233
|
+
sm.anotherinput.add_to_set('a', "foo", "bar")
|
234
|
+
sm.yetanotherinput.add_to_set('b', "bar", "shmoo")
|
235
|
+
sm.process!
|
236
|
+
sm.similarities_for('bar').should include('foo', 'shmoo')
|
237
|
+
sm.delete_from_matrix!(:anotherinput, 'foo')
|
238
|
+
sm.similarities_for('bar').should == ['shmoo']
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
200
242
|
describe "delete_item!" do
|
201
243
|
it "should call delete_item on each input_matrix" do
|
202
244
|
BaseRecommender.input_matrix(:myfirstinput)
|
203
245
|
BaseRecommender.input_matrix(:mysecondinput)
|
204
246
|
sm = BaseRecommender.new
|
205
|
-
sm.myfirstinput.should_receive(:delete_item
|
206
|
-
sm.mysecondinput.should_receive(:delete_item
|
247
|
+
sm.myfirstinput.should_receive(:delete_item).with("fnorditem")
|
248
|
+
sm.mysecondinput.should_receive(:delete_item).with("fnorditem")
|
207
249
|
sm.delete_item!("fnorditem")
|
208
250
|
end
|
251
|
+
|
252
|
+
it "should remove the item from all_items" do
|
253
|
+
BaseRecommender.input_matrix(:anotherinput)
|
254
|
+
sm = BaseRecommender.new
|
255
|
+
sm.anotherinput.add_to_set('a', "foo", "bar")
|
256
|
+
sm.process!
|
257
|
+
sm.all_items.should include('foo')
|
258
|
+
sm.delete_item!('foo')
|
259
|
+
sm.all_items.should_not include('foo')
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should remove the item's similarities and also remove the item from related_items' similarities" do
|
263
|
+
BaseRecommender.input_matrix(:anotherinput)
|
264
|
+
BaseRecommender.input_matrix(:yetanotherinput)
|
265
|
+
sm = BaseRecommender.new
|
266
|
+
sm.anotherinput.add_to_set('a', "foo", "bar")
|
267
|
+
sm.yetanotherinput.add_to_set('b', "bar", "shmoo")
|
268
|
+
sm.process!
|
269
|
+
sm.similarities_for('bar').should include('foo', 'shmoo')
|
270
|
+
sm.similarities_for('shmoo').should include('bar')
|
271
|
+
sm.delete_item!('shmoo')
|
272
|
+
sm.similarities_for('bar').should_not include('shmoo')
|
273
|
+
sm.similarities_for('shmoo').should be_empty
|
274
|
+
end
|
209
275
|
end
|
210
276
|
|
211
277
|
describe "clean!" do
|
@@ -213,9 +279,9 @@ describe Predictor::Base do
|
|
213
279
|
BaseRecommender.input_matrix(:set1)
|
214
280
|
BaseRecommender.input_matrix(:set2)
|
215
281
|
sm = BaseRecommender.new
|
216
|
-
sm.set1.
|
217
|
-
sm.set1.
|
218
|
-
sm.set2.
|
282
|
+
sm.set1.add_to_set "item1", "foo", "bar"
|
283
|
+
sm.set1.add_to_set "item2", "nada", "bar"
|
284
|
+
sm.set2.add_to_set "item3", "bar", "other"
|
219
285
|
Predictor.redis.keys("#{sm.redis_prefix}:*").should_not be_empty
|
220
286
|
sm.clean!
|
221
287
|
Predictor.redis.keys("#{sm.redis_prefix}:*").should be_empty
|