commendo 1.2.4 → 2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/bin/commendo-create-mysql-db +3 -0
- data/bin/commendo-create.sql +99 -0
- data/bin/commendo-load-tsv +11 -5
- data/bin/commendo-load-tsv-mysql.rb +43 -0
- data/bin/commendo-time-mysql.rb +31 -0
- data/commendo.gemspec +4 -2
- data/lib/commendo.rb +24 -0
- data/lib/commendo/configuration.rb +25 -0
- data/lib/commendo/content_set.rb +13 -182
- data/lib/commendo/mysql-backed/content_set.rb +152 -0
- data/lib/commendo/mysql-backed/tag_set.rb +81 -0
- data/lib/commendo/mysql-backed/weighted_group.rb +40 -0
- data/lib/commendo/redis-backed/content_set.rb +194 -0
- data/lib/commendo/{pair_comparison.lua → redis-backed/pair_comparison.lua} +0 -0
- data/lib/commendo/{similarity.lua → redis-backed/similarity.lua} +0 -0
- data/lib/commendo/redis-backed/tag_set.rb +54 -0
- data/lib/commendo/redis-backed/weighted_group.rb +54 -0
- data/lib/commendo/tag_set.rb +6 -42
- data/lib/commendo/version.rb +1 -1
- data/lib/commendo/weighted_group.rb +7 -41
- data/lib/mysql2/client.rb +17 -0
- data/model 2.mwb +0 -0
- data/sql_model.mwb +0 -0
- data/test/configuration_test.rb +71 -0
- data/test/mysql_content_set_test.rb +40 -0
- data/test/mysql_tag_set_test.rb +34 -0
- data/test/mysql_weighted_group_test.rb +54 -0
- data/test/redis_content_set_test.rb +57 -0
- data/test/redis_tag_set_test.rb +31 -0
- data/test/redis_weighted_group_test.rb +49 -0
- data/test/tests_for_content_sets.rb +379 -0
- data/test/tests_for_tag_sets.rb +130 -0
- data/test/tests_for_weighted_groups.rb +106 -0
- metadata +72 -12
- data/test/content_set_test.rb +0 -408
- data/test/tag_set_test.rb +0 -128
- data/test/weighted_group_test.rb +0 -191
@@ -0,0 +1,57 @@
|
|
1
|
+
require_relative 'tests_for_content_sets.rb'
|
2
|
+
gem 'minitest'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'minitest/pride'
|
5
|
+
require 'minitest/mock'
|
6
|
+
require 'mocha/setup'
|
7
|
+
require 'commendo'
|
8
|
+
|
9
|
+
module Commendo
|
10
|
+
|
11
|
+
class RedisContentSetTest < Minitest::Test
|
12
|
+
|
13
|
+
def setup
|
14
|
+
Commendo.config do |config|
|
15
|
+
config.backend = :redis
|
16
|
+
config.host = 'localhost'
|
17
|
+
config.port = 6379
|
18
|
+
config.database = 15
|
19
|
+
end
|
20
|
+
Redis.new(host: Commendo.config.host, port: Commendo.config.port, db: Commendo.config.database).flushdb
|
21
|
+
@key_base = 'CommendoTests'
|
22
|
+
@cs = ContentSet.new(key_base: @key_base)
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_tag_set(kb)
|
26
|
+
Commendo::TagSet.new(key_base: kb)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_content_set(key_base, ts = nil)
|
30
|
+
Commendo::ContentSet.new(key_base: key_base, tag_set: ts)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_gives_similarity_key_for_resource
|
34
|
+
key_base = 'CommendoTestsFooBarBaz'
|
35
|
+
cs = create_content_set(key_base)
|
36
|
+
assert_equal 'CommendoTestsFooBarBaz:similar:resource-1', cs.similarity_key('resource-1')
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_calculate_yields_after_each
|
40
|
+
(3..23).each do |group|
|
41
|
+
(3..23).each do |res|
|
42
|
+
@cs.add_by_group(group, res) if res % group == 0
|
43
|
+
end
|
44
|
+
end
|
45
|
+
expected_keys = ['CommendoTests:resources:3', 'CommendoTests:resources:4', 'CommendoTests:resources:5', 'CommendoTests:resources:6', 'CommendoTests:resources:7', 'CommendoTests:resources:8', 'CommendoTests:resources:9', 'CommendoTests:resources:10', 'CommendoTests:resources:11', 'CommendoTests:resources:12', 'CommendoTests:resources:13', 'CommendoTests:resources:14', 'CommendoTests:resources:15', 'CommendoTests:resources:16', 'CommendoTests:resources:17', 'CommendoTests:resources:18', 'CommendoTests:resources:19', 'CommendoTests:resources:20', 'CommendoTests:resources:21', 'CommendoTests:resources:22', 'CommendoTests:resources:23']
|
46
|
+
actual_keys = []
|
47
|
+
@cs.calculate_similarity { |key, index, total|
|
48
|
+
actual_keys << key
|
49
|
+
}
|
50
|
+
assert_equal expected_keys.sort, actual_keys.sort
|
51
|
+
end
|
52
|
+
|
53
|
+
include TestsForContentSets
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'tests_for_tag_sets'
|
2
|
+
gem 'minitest'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'minitest/pride'
|
5
|
+
require 'minitest/mock'
|
6
|
+
require 'mocha/setup'
|
7
|
+
require 'commendo'
|
8
|
+
|
9
|
+
module Commendo
|
10
|
+
|
11
|
+
class RedisTagSetTest < Minitest::Test
|
12
|
+
|
13
|
+
def setup
|
14
|
+
Commendo.config do |config|
|
15
|
+
config.backend = :redis
|
16
|
+
config.host = 'localhost'
|
17
|
+
config.port = 6379
|
18
|
+
config.database = 15
|
19
|
+
end
|
20
|
+
Redis.new(host: Commendo.config.host, port: Commendo.config.port, db: Commendo.config.database).flushdb
|
21
|
+
@ts = TagSet.new(key_base: 'TagSetTest')
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_tag_set(kb)
|
25
|
+
Commendo::TagSet.new(key_base: kb)
|
26
|
+
end
|
27
|
+
|
28
|
+
include TestsForTagSets
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative 'tests_for_weighted_groups'
|
2
|
+
gem 'minitest'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'minitest/pride'
|
5
|
+
require 'minitest/mock'
|
6
|
+
require 'mocha/setup'
|
7
|
+
require 'commendo'
|
8
|
+
|
9
|
+
module Commendo
|
10
|
+
|
11
|
+
class RedisWeightedGroupTest < Minitest::Test
|
12
|
+
|
13
|
+
def setup
|
14
|
+
super
|
15
|
+
Commendo.config do |config|
|
16
|
+
config.backend = :redis
|
17
|
+
config.host = 'localhost'
|
18
|
+
config.port = 6379
|
19
|
+
config.database = 15
|
20
|
+
end
|
21
|
+
Redis.new(host: Commendo.config.host, port: Commendo.config.port, db: Commendo.config.database).flushdb
|
22
|
+
@tag_set = TagSet.new(key_base: 'CommendoTests:Tags')
|
23
|
+
@cs1 = ContentSet.new(key_base: 'CommendoTests:ContentSet1', tag_set: @tag_set)
|
24
|
+
@cs2 = ContentSet.new(key_base: 'CommendoTests:ContentSet2', tag_set: @tag_set)
|
25
|
+
@cs3 = ContentSet.new(key_base: 'CommendoTests:ContentSet3', tag_set: @tag_set)
|
26
|
+
(3..23).each do |group|
|
27
|
+
(3..23).each do |res|
|
28
|
+
@cs1.add_by_group(group, res) if res.modulo(group).zero? && res.modulo(2).zero?
|
29
|
+
@cs2.add_by_group(group, res) if res.modulo(group).zero? && res.modulo(3).zero?
|
30
|
+
@cs3.add_by_group(group, res) if res.modulo(group).zero? && res.modulo(6).zero?
|
31
|
+
@tag_set.add(res, 'mod3') if res.modulo(3).zero?
|
32
|
+
@tag_set.add(res, 'mod4') if res.modulo(4).zero?
|
33
|
+
@tag_set.add(res, 'mod5') if res.modulo(5).zero?
|
34
|
+
@tag_set.add(res, 'mod7') if res.modulo(7).zero?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
[@cs1, @cs2, @cs3].each { |cs| cs.calculate_similarity }
|
38
|
+
@weighted_group = Commendo::WeightedGroup.new(key_base: 'CommendoTests:WeightedGroup',
|
39
|
+
content_sets: [{cs: @cs1, weight: 1.0},
|
40
|
+
{cs: @cs2, weight: 10.0},
|
41
|
+
{cs: @cs3, weight: 100.0}]
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
include TestsForWeightedGroups
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,379 @@
|
|
1
|
+
module TestsForContentSets
|
2
|
+
|
3
|
+
def contains_resource(resource, similarities)
|
4
|
+
!similarities.select { |sim| sim[:resource] == "#{resource}" }.empty?
|
5
|
+
end
|
6
|
+
|
7
|
+
def similar_to(cs, resource, similar)
|
8
|
+
contains_resource(similar, cs.similar_to(resource))
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_recommends_for_many_applies_filters
|
12
|
+
ts = create_tag_set("#{@key_base}:tags")
|
13
|
+
@cs = create_content_set(@key_base, ts)
|
14
|
+
(3..23).each do |group|
|
15
|
+
(3..23).each do |res|
|
16
|
+
@cs.add(res, group) if res % group == 0
|
17
|
+
ts.add(res, 'mod3') if res.modulo(3).zero?
|
18
|
+
ts.add(res, 'mod4') if res.modulo(4).zero?
|
19
|
+
ts.add(res, 'mod5') if res.modulo(5).zero?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
@cs.calculate_similarity
|
23
|
+
actual = @cs.filtered_similar_to([12, 6, 9], include: ['mod4'], exclude: ['mod3', 'mod5'])
|
24
|
+
refute contains_resource('6', actual)
|
25
|
+
refute contains_resource('18', actual)
|
26
|
+
assert contains_resource('4', actual)
|
27
|
+
refute contains_resource('3', actual)
|
28
|
+
refute contains_resource('9', actual)
|
29
|
+
assert contains_resource('8', actual)
|
30
|
+
refute contains_resource('21', actual)
|
31
|
+
assert contains_resource('16', actual)
|
32
|
+
refute contains_resource('15', actual)
|
33
|
+
refute contains_resource('20', actual)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_recommends_for_many
|
37
|
+
ts = create_tag_set("#{@key_base}:tags")
|
38
|
+
@cs = create_content_set(@key_base, ts)
|
39
|
+
(3..23).each do |group|
|
40
|
+
(3..23).each do |res|
|
41
|
+
@cs.add(res, group) if res % group == 0
|
42
|
+
ts.add(res, 'mod3') if res.modulo(3).zero?
|
43
|
+
ts.add(res, 'mod4') if res.modulo(4).zero?
|
44
|
+
ts.add(res, 'mod5') if res.modulo(5).zero?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
@cs.calculate_similarity
|
48
|
+
expected = [
|
49
|
+
{resource: '18', similarity: 1.834},
|
50
|
+
{resource: '3', similarity: 1.734},
|
51
|
+
{resource: '6', similarity: 1.167},
|
52
|
+
{resource: '21', similarity: 1.086},
|
53
|
+
{resource: '15', similarity: 1.086},
|
54
|
+
{resource: '12', similarity: 1.0},
|
55
|
+
{resource: '9', similarity: 0.833},
|
56
|
+
{resource: '4', similarity: 0.4},
|
57
|
+
{resource: '8', similarity: 0.333},
|
58
|
+
{resource: '16', similarity: 0.286},
|
59
|
+
{resource: '20', similarity: 0.25}
|
60
|
+
]
|
61
|
+
actual = @cs.similar_to([12, 6, 9])
|
62
|
+
assert_equal expected, actual
|
63
|
+
#, include: ['mod4'], exclude: ['mod3', 'mod5']
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_filters_includes_and_exclude_by_tag_collection
|
67
|
+
ts = create_tag_set("#{@key_base}:tags")
|
68
|
+
@cs = create_content_set(@key_base, ts)
|
69
|
+
#Build some test data
|
70
|
+
(3..23).each do |group|
|
71
|
+
(3..23).each do |res|
|
72
|
+
@cs.add(res, group) if res % group == 0
|
73
|
+
ts.add(res, 'mod3') if res.modulo(3).zero?
|
74
|
+
ts.add(res, 'mod4') if res.modulo(4).zero?
|
75
|
+
ts.add(res, 'mod5') if res.modulo(5).zero?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
@cs.calculate_similarity
|
79
|
+
|
80
|
+
actual = @cs.filtered_similar_to(12, include: ['mod4'], exclude: ['mod3', 'mod5'])
|
81
|
+
assert_equal 3, actual.length
|
82
|
+
|
83
|
+
refute contains_resource('6', actual)
|
84
|
+
refute contains_resource('18', actual)
|
85
|
+
assert contains_resource('4', actual)
|
86
|
+
refute contains_resource('3', actual)
|
87
|
+
refute contains_resource('9', actual)
|
88
|
+
assert contains_resource('8', actual)
|
89
|
+
refute contains_resource('21', actual)
|
90
|
+
assert contains_resource('16', actual)
|
91
|
+
refute contains_resource('15', actual)
|
92
|
+
refute contains_resource('20', actual)
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_filters_exclude_by_tag_collection
|
96
|
+
ts = create_tag_set("#{@key_base}:tags")
|
97
|
+
@cs = create_content_set(@key_base, ts)
|
98
|
+
(3..23).each do |group|
|
99
|
+
(3..23).each do |res|
|
100
|
+
@cs.add(res, group) if res % group == 0
|
101
|
+
ts.add(res, 'mod3') if res.modulo(3).zero?
|
102
|
+
ts.add(res, 'mod4') if res.modulo(4).zero?
|
103
|
+
ts.add(res, 'mod5') if res.modulo(5).zero?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
@cs.calculate_similarity
|
107
|
+
|
108
|
+
actual = @cs.filtered_similar_to(10, exclude: ['mod3'])
|
109
|
+
assert_equal 2, actual.length
|
110
|
+
assert contains_resource('5', actual)
|
111
|
+
assert contains_resource('20', actual)
|
112
|
+
refute contains_resource('15', actual)
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_filters_include_by_tag_collection_and_limit
|
116
|
+
ts = create_tag_set("#{@key_base}:tags")
|
117
|
+
@cs = create_content_set(@key_base, ts)
|
118
|
+
(3..23).each do |group|
|
119
|
+
(3..23).each do |res|
|
120
|
+
@cs.add(res, group) if res % group == 0
|
121
|
+
ts.add(res, 'mod3') if res.modulo(3).zero?
|
122
|
+
ts.add(res, 'mod4') if res.modulo(4).zero?
|
123
|
+
ts.add(res, 'mod5') if res.modulo(5).zero?
|
124
|
+
end
|
125
|
+
end
|
126
|
+
@cs.calculate_similarity
|
127
|
+
|
128
|
+
actual = @cs.filtered_similar_to(10, include: ['mod5'], limit: 2)
|
129
|
+
assert_equal 2, actual.length
|
130
|
+
assert contains_resource('5', actual)
|
131
|
+
#assert contains_resource('15', actual)
|
132
|
+
assert contains_resource('20', actual)
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_filters_include_by_tag_collection
|
137
|
+
ts = create_tag_set("#{@key_base}:tags")
|
138
|
+
@cs = create_content_set(@key_base, ts)
|
139
|
+
(3..23).each do |group|
|
140
|
+
(3..23).each do |res|
|
141
|
+
@cs.add(res, group) if res % group == 0
|
142
|
+
ts.add(res, 'mod3') if res.modulo(3).zero?
|
143
|
+
ts.add(res, 'mod4') if res.modulo(4).zero?
|
144
|
+
ts.add(res, 'mod5') if res.modulo(5).zero?
|
145
|
+
end
|
146
|
+
end
|
147
|
+
@cs.calculate_similarity
|
148
|
+
|
149
|
+
actual = @cs.filtered_similar_to(10, include: ['mod5'])
|
150
|
+
assert_equal 3, actual.length
|
151
|
+
assert contains_resource('5', actual)
|
152
|
+
assert contains_resource('15', actual)
|
153
|
+
assert contains_resource('20', actual)
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_remove_and_calculate
|
158
|
+
(3..23).each do |group|
|
159
|
+
(3..23).each do |res|
|
160
|
+
@cs.add(res, group) if res % group == 0
|
161
|
+
end
|
162
|
+
end
|
163
|
+
@cs.calculate_similarity
|
164
|
+
assert similar_to(@cs, 18, 12)
|
165
|
+
@cs.remove_from_groups_and_calculate(18, 6, 3)
|
166
|
+
refute similar_to(@cs, 18, 12)
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_accepts_incremental_updates
|
170
|
+
(3..23).each do |group|
|
171
|
+
(3..23).each do |res|
|
172
|
+
@cs.add(res, group) if res % group == 0
|
173
|
+
end
|
174
|
+
end
|
175
|
+
@cs.calculate_similarity
|
176
|
+
assert similar_to(@cs, 18, 12)
|
177
|
+
refute similar_to(@cs, 10, 12)
|
178
|
+
|
179
|
+
@cs.add_and_calculate(12, 'foo', true)
|
180
|
+
@cs.add_and_calculate(10, 'foo', true)
|
181
|
+
assert similar_to(@cs, 10, 12)
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_remove_causes_similarity_to_change_when_recalculated
|
185
|
+
(3..23).each do |group|
|
186
|
+
(3..23).each do |res|
|
187
|
+
@cs.add(res, group) if res % group == 0
|
188
|
+
end
|
189
|
+
end
|
190
|
+
@cs.calculate_similarity
|
191
|
+
assert similar_to(@cs, 18, 12)
|
192
|
+
@cs.remove_from_groups(18, 6, 3)
|
193
|
+
@cs.calculate_similarity
|
194
|
+
refute similar_to(@cs, 18, 12)
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_remove_from_groups
|
198
|
+
(3..23).each do |group|
|
199
|
+
(3..23).each do |res|
|
200
|
+
@cs.add(res, group) if res % group == 0
|
201
|
+
end
|
202
|
+
end
|
203
|
+
resource = 20
|
204
|
+
assert_equal ['4', '5', '10', '20'].sort!, @cs.groups(resource).sort!
|
205
|
+
@cs.remove_from_groups(resource, 10)
|
206
|
+
assert_equal ['4', '5', '20'].sort!, @cs.groups(resource).sort!
|
207
|
+
@cs.remove_from_groups(resource, 4)
|
208
|
+
assert_equal ['5', '20'].sort!, @cs.groups(resource).sort!
|
209
|
+
end
|
210
|
+
|
211
|
+
def test_deletes_resource_from_everywhere
|
212
|
+
(3..23).each do |group|
|
213
|
+
(3..23).each do |res|
|
214
|
+
@cs.add_by_group(group, res) if res % group == 0
|
215
|
+
end
|
216
|
+
end
|
217
|
+
@cs.calculate_similarity
|
218
|
+
assert similar_to(@cs, 18, 12)
|
219
|
+
|
220
|
+
@cs.delete(12)
|
221
|
+
assert_equal [], @cs.similar_to(12)
|
222
|
+
refute similar_to(@cs, 18, 12)
|
223
|
+
|
224
|
+
@cs.calculate_similarity
|
225
|
+
assert_equal [], @cs.similar_to(12)
|
226
|
+
refute similar_to(@cs, 18, 12)
|
227
|
+
end
|
228
|
+
|
229
|
+
def test_calculate_copes_with_missing_resource
|
230
|
+
@cs.calculate_similarity_for_resource('999999999999', 0.1)
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_calculates_with_threshold
|
234
|
+
(3..23).each do |group|
|
235
|
+
(3..23).each do |res|
|
236
|
+
@cs.add_by_group(group, res) if res % group == 0
|
237
|
+
end
|
238
|
+
end
|
239
|
+
@cs.calculate_similarity(0.4)
|
240
|
+
expected = [
|
241
|
+
{resource: '9', similarity: 0.667},
|
242
|
+
{resource: '6', similarity: 0.667},
|
243
|
+
{resource: '12', similarity: 0.5}
|
244
|
+
]
|
245
|
+
assert_equal expected, @cs.similar_to(18)
|
246
|
+
end
|
247
|
+
|
248
|
+
def test_calculates_similarity_scores
|
249
|
+
(3..23).each do |group|
|
250
|
+
(3..23).each do |res|
|
251
|
+
@cs.add_by_group(group, res) if res % group == 0
|
252
|
+
end
|
253
|
+
end
|
254
|
+
@cs.calculate_similarity
|
255
|
+
expected = [
|
256
|
+
{resource: '9', similarity: 0.667},
|
257
|
+
{resource: '6', similarity: 0.667},
|
258
|
+
{resource: '12', similarity: 0.5},
|
259
|
+
{resource: '3', similarity: 0.4},
|
260
|
+
{resource: '21', similarity: 0.286},
|
261
|
+
{resource: '15', similarity: 0.286}
|
262
|
+
]
|
263
|
+
assert_equal expected, @cs.similar_to(18)
|
264
|
+
end
|
265
|
+
|
266
|
+
def test_recommendations_are_isolated_by_key_base
|
267
|
+
cs1 = create_content_set('ContentSetOne')
|
268
|
+
cs2 = create_content_set('ContentSetTwo')
|
269
|
+
cs1.add('1', 'a group')
|
270
|
+
cs2.add('2', 'a group')
|
271
|
+
cs1.add('3', 'a group')
|
272
|
+
cs2.add('4', 'a group')
|
273
|
+
cs1.add('5', 'a group')
|
274
|
+
cs2.add('6', 'a group')
|
275
|
+
cs1.calculate_similarity
|
276
|
+
cs2.calculate_similarity
|
277
|
+
assert_equal [{resource: '5', similarity: 1.0}, {resource: '3', similarity: 1.0}], cs1.similar_to('1')
|
278
|
+
assert_equal [{resource: '6', similarity: 1.0}, {resource: '4', similarity: 1.0}], cs2.similar_to('2')
|
279
|
+
end
|
280
|
+
|
281
|
+
def test_recommends_when_added_by_group_with_scores
|
282
|
+
@cs.add_by_group('group-1', ['resource-1', 2], ['resource-2', 3], ['resource-3', 7])
|
283
|
+
@cs.add_by_group('group-2', ['resource-1', 2], ['resource-3', 3], ['resource-4', 5])
|
284
|
+
@cs.calculate_similarity
|
285
|
+
expected = [
|
286
|
+
{resource: 'resource-3', similarity: 1.0},
|
287
|
+
{resource: 'resource-4', similarity: 0.778},
|
288
|
+
{resource: 'resource-2', similarity: 0.714}
|
289
|
+
]
|
290
|
+
assert_equal expected, @cs.similar_to('resource-1')
|
291
|
+
end
|
292
|
+
|
293
|
+
def test_recommends_when_added_by_group
|
294
|
+
@cs.add_by_group('group-1', 'resource-1', 'resource-2', 'resource-3')
|
295
|
+
@cs.add_by_group('group-2', 'resource-1', 'resource-3', 'resource-4')
|
296
|
+
@cs.calculate_similarity
|
297
|
+
expected = [
|
298
|
+
{resource: 'resource-3', similarity: 1.0},
|
299
|
+
{resource: 'resource-4', similarity: 0.667},
|
300
|
+
{resource: 'resource-2', similarity: 0.667}
|
301
|
+
]
|
302
|
+
assert_equal expected, @cs.similar_to('resource-1')
|
303
|
+
end
|
304
|
+
|
305
|
+
def test_recommends_when_extra_scores_added
|
306
|
+
test_recommends_when_added_with_scores #sets up the content set
|
307
|
+
@cs.add('resource-3', ['group-1', 1], ['group-3', 2])
|
308
|
+
@cs.add('resource-4', ['group-2', 1])
|
309
|
+
@cs.add_by_group('group-1', ['newource-9', 100], 'resource-2', 'resource-3')
|
310
|
+
@cs.add_by_group('group-2', 'resource-1', 'resource-3', 'resource-4')
|
311
|
+
@cs.calculate_similarity
|
312
|
+
expected = [
|
313
|
+
{resource: 'newource-9', similarity: 1.0},
|
314
|
+
{resource: 'resource-1', similarity: 0.769},
|
315
|
+
{resource: 'resource-3', similarity: 0.706}
|
316
|
+
]
|
317
|
+
actual = @cs.similar_to('resource-2')
|
318
|
+
assert_equal expected, actual
|
319
|
+
end
|
320
|
+
|
321
|
+
def test_recommends_with_large_number_of_groups
|
322
|
+
(0..3000).each do |i|
|
323
|
+
@cs.add('resource-1', ["group-#{i}", i/100.0], ["group-#{i+1}", i/20.0])
|
324
|
+
@cs.add('resource-9', ["group-#{i}", i/100.0], ["group-#{i+1}", i/20.0])
|
325
|
+
end
|
326
|
+
@cs.calculate_similarity
|
327
|
+
expected = [
|
328
|
+
{resource: 'resource-9', similarity: 1.0}
|
329
|
+
]
|
330
|
+
assert_equal expected, @cs.similar_to('resource-1')
|
331
|
+
end
|
332
|
+
|
333
|
+
def test_recommends_when_added_with_scores
|
334
|
+
@cs.add('resource-1', ['group-1', 2], ['group-2', 2])
|
335
|
+
@cs.add('resource-2', ['group-1', 7])
|
336
|
+
@cs.add('resource-3', ['group-1', 2], ['group-2', 2])
|
337
|
+
@cs.add('resource-4', ['group-2', 3])
|
338
|
+
@cs.calculate_similarity
|
339
|
+
expected = [
|
340
|
+
{resource: 'resource-3', similarity: 1.0},
|
341
|
+
{resource: 'resource-2', similarity: 0.818},
|
342
|
+
{resource: 'resource-4', similarity: 0.714}
|
343
|
+
]
|
344
|
+
actual = @cs.similar_to('resource-1')
|
345
|
+
assert_equal expected, actual
|
346
|
+
end
|
347
|
+
|
348
|
+
def test_recommends_limited_by_number
|
349
|
+
@cs.add('resource-1', 'group-1', 'group-2')
|
350
|
+
@cs.add('resource-2', 'group-1')
|
351
|
+
@cs.add('resource-3', 'group-1', 'group-2')
|
352
|
+
@cs.add('resource-4', 'group-2')
|
353
|
+
@cs.calculate_similarity
|
354
|
+
expected = [
|
355
|
+
{resource: 'resource-3', similarity: 1.0},
|
356
|
+
{resource: 'resource-4', similarity: 0.667},
|
357
|
+
{resource: 'resource-2', similarity: 0.667}
|
358
|
+
]
|
359
|
+
assert_equal expected[0..0], @cs.similar_to('resource-1', 1)
|
360
|
+
assert_equal expected[0..1], @cs.similar_to('resource-1', 2)
|
361
|
+
assert_equal expected, @cs.similar_to('resource-1', 3)
|
362
|
+
assert_equal expected, @cs.similar_to('resource-1', 99)
|
363
|
+
end
|
364
|
+
|
365
|
+
def test_recommends_when_added
|
366
|
+
@cs.add('resource-1', 'group-1', 'group-2')
|
367
|
+
@cs.add('resource-2', 'group-1')
|
368
|
+
@cs.add('resource-3', 'group-1', 'group-2')
|
369
|
+
@cs.add('resource-4', 'group-2')
|
370
|
+
@cs.calculate_similarity
|
371
|
+
expected = [
|
372
|
+
{resource: 'resource-3', similarity: 1.0},
|
373
|
+
{resource: 'resource-4', similarity: 0.667},
|
374
|
+
{resource: 'resource-2', similarity: 0.667}
|
375
|
+
]
|
376
|
+
assert_equal expected, @cs.similar_to('resource-1')
|
377
|
+
end
|
378
|
+
|
379
|
+
end
|