commendo 1.2.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/bin/commendo-create-mysql-db +3 -0
  4. data/bin/commendo-create.sql +99 -0
  5. data/bin/commendo-load-tsv +11 -5
  6. data/bin/commendo-load-tsv-mysql.rb +43 -0
  7. data/bin/commendo-time-mysql.rb +31 -0
  8. data/commendo.gemspec +4 -2
  9. data/lib/commendo.rb +24 -0
  10. data/lib/commendo/configuration.rb +25 -0
  11. data/lib/commendo/content_set.rb +13 -182
  12. data/lib/commendo/mysql-backed/content_set.rb +152 -0
  13. data/lib/commendo/mysql-backed/tag_set.rb +81 -0
  14. data/lib/commendo/mysql-backed/weighted_group.rb +40 -0
  15. data/lib/commendo/redis-backed/content_set.rb +194 -0
  16. data/lib/commendo/{pair_comparison.lua → redis-backed/pair_comparison.lua} +0 -0
  17. data/lib/commendo/{similarity.lua → redis-backed/similarity.lua} +0 -0
  18. data/lib/commendo/redis-backed/tag_set.rb +54 -0
  19. data/lib/commendo/redis-backed/weighted_group.rb +54 -0
  20. data/lib/commendo/tag_set.rb +6 -42
  21. data/lib/commendo/version.rb +1 -1
  22. data/lib/commendo/weighted_group.rb +7 -41
  23. data/lib/mysql2/client.rb +17 -0
  24. data/model 2.mwb +0 -0
  25. data/sql_model.mwb +0 -0
  26. data/test/configuration_test.rb +71 -0
  27. data/test/mysql_content_set_test.rb +40 -0
  28. data/test/mysql_tag_set_test.rb +34 -0
  29. data/test/mysql_weighted_group_test.rb +54 -0
  30. data/test/redis_content_set_test.rb +57 -0
  31. data/test/redis_tag_set_test.rb +31 -0
  32. data/test/redis_weighted_group_test.rb +49 -0
  33. data/test/tests_for_content_sets.rb +379 -0
  34. data/test/tests_for_tag_sets.rb +130 -0
  35. data/test/tests_for_weighted_groups.rb +106 -0
  36. metadata +72 -12
  37. data/test/content_set_test.rb +0 -408
  38. data/test/tag_set_test.rb +0 -128
  39. data/test/weighted_group_test.rb +0 -191
@@ -0,0 +1,130 @@
1
+ module TestsForTagSets
2
+
3
+ def test_adds_tags_for_resource
4
+ assert_equal [], @ts.get(1)
5
+ @ts.add(1, 'foo', 'bar', 'baz')
6
+ expected = @ts.get(1)
7
+ assert_equal %w(bar baz foo).sort, expected.sort
8
+ @ts.add(1, 'qux', 'qip')
9
+ expected = @ts.get(1)
10
+ assert_equal %w(bar baz foo qip qux).sort, expected.sort
11
+ end
12
+
13
+ def test_isolates_by_key_base
14
+ ts1 = create_tag_set('TagSetOne')
15
+ ts2 = create_tag_set('TagSetTwo')
16
+ assert_equal [], ts1.get(1)
17
+ assert_equal [], ts2.get(1)
18
+
19
+ ts1.add(1, 'foo', 'bar', 'baz')
20
+
21
+ assert_equal %w(bar baz foo).sort, ts1.get(1).sort
22
+ assert_equal [], ts2.get(1)
23
+
24
+ ts2.add(1, 'qux', 'qip')
25
+ assert_equal %w(bar baz foo).sort, ts1.get(1).sort
26
+ assert_equal %w(qux qip).sort, ts2.get(1).sort
27
+ end
28
+
29
+ def test_empty?
30
+ assert @ts.respond_to? :empty?
31
+ assert @ts.empty?
32
+ @ts.add(1, 'qux', 'qip')
33
+ refute @ts.empty?
34
+ @ts.delete(1)
35
+ assert @ts.empty?
36
+ end
37
+
38
+ def test_sets_tags_for_resource
39
+ assert_equal [], @ts.get(1)
40
+ @ts.set(1, 'foo', 'bar', 'baz')
41
+ assert_equal %w(bar baz foo).sort, @ts.get(1).sort
42
+ @ts.set(1, 'qux', 'qip')
43
+ assert_equal %w(qip qux).sort, @ts.get(1).sort
44
+ end
45
+
46
+ def test_sets_tags_when_empty
47
+ @ts.set(1, 'foo', 'bar', 'baz')
48
+ @ts.set(2, 'qux', 'qip')
49
+ assert_equal %w(bar baz foo).sort, @ts.get(1).sort
50
+ assert_equal %w(qip qux).sort, @ts.get(2).sort
51
+ @ts.set(1, *[])
52
+ assert_equal [], @ts.get(1)
53
+ assert_equal %w(qip qux).sort, @ts.get(2).sort
54
+ end
55
+
56
+ def test_deletes_all_tags_for_resource
57
+ @ts.set(1, 'foo', 'bar', 'baz')
58
+ @ts.set(2, 'qux', 'qip')
59
+ assert_equal %w(bar baz foo).sort, @ts.get(1).sort
60
+ assert_equal %w(qip qux).sort, @ts.get(2).sort
61
+ @ts.delete(1)
62
+ assert_equal [], @ts.get(1)
63
+ assert_equal %w(qip qux).sort, @ts.get(2).sort
64
+ end
65
+
66
+ def test_deletes_given_tags_for_resource
67
+ assert_equal [], @ts.get(1)
68
+ @ts.set(1, 'foo', 'bar', 'baz', 'qux', 'qip')
69
+ @ts.delete(1, 'qux', 'qip')
70
+ assert_equal %w(bar baz foo).sort, @ts.get(1).sort
71
+ end
72
+
73
+ def test_matches_tags
74
+ @ts.set(1, 'foo', 'bar', 'baz')
75
+ @ts.set(2, 'qux', 'qip')
76
+
77
+ assert @ts.matches(1, ['foo'])
78
+ assert @ts.matches(1, %w(bar baz))
79
+ assert @ts.matches(1, %w(bar baz foo))
80
+ refute @ts.matches(1, ['qux'])
81
+ refute @ts.matches(1, ['qip'])
82
+
83
+ refute @ts.matches(2, ['foo'])
84
+ refute @ts.matches(2, %w(bar baz))
85
+ refute @ts.matches(2, %w(bar baz foo))
86
+ assert @ts.matches(2, %w(qux qip))
87
+ assert @ts.matches(2, ['qux'])
88
+ assert @ts.matches(2, ['qip'])
89
+ end
90
+
91
+ def test_matches_exclude_tags
92
+ @ts.set(1, 'foo', 'bar', 'baz')
93
+ @ts.set(2, 'qux', 'qip')
94
+
95
+ refute @ts.matches(1, nil, ['foo'])
96
+ refute @ts.matches(1, [], ['foo'])
97
+ refute @ts.matches(1, [], %w(bar baz))
98
+ refute @ts.matches(1, [], %w(bar baz foo))
99
+ assert @ts.matches(1, [], ['qux'])
100
+ assert @ts.matches(1, [], ['qip'])
101
+
102
+ assert @ts.matches(2, nil, ['foo'])
103
+ assert @ts.matches(2, [], ['foo'])
104
+ assert @ts.matches(2, [], %w(bar baz))
105
+ assert @ts.matches(2, [], %w(bar baz foo))
106
+ refute @ts.matches(2, [], %w(qux qip))
107
+ refute @ts.matches(2, [], ['qux'])
108
+ refute @ts.matches(2, [], ['qip'])
109
+ end
110
+
111
+ def test_matches_include_and_exclude_tags
112
+ @ts.set(1, 'foo', 'bar', 'baz')
113
+ @ts.set(2, 'qux', 'qip')
114
+
115
+ refute @ts.matches(1, ['foo'], ['bar'])
116
+ refute @ts.matches(1, ['bar'], ['foo'])
117
+
118
+ assert @ts.matches(1, ['foo'], [])
119
+ assert @ts.matches(1, ['foo'], nil)
120
+ assert @ts.matches(1, ['foo'], ['qux'])
121
+
122
+ assert @ts.matches(2, ['qip'], ['foo'])
123
+ assert @ts.matches(2, ['qux'], %w(bar baz))
124
+ assert @ts.matches(2, ['qip'], %w(bar baz foo))
125
+ refute @ts.matches(2, ['qip'], %w(qux qip))
126
+ refute @ts.matches(2, ['qip'], ['qux'])
127
+ refute @ts.matches(2, ['qux'], ['qip'])
128
+ end
129
+
130
+ end
@@ -0,0 +1,106 @@
1
+ module TestsForWeightedGroups
2
+
3
+ def test_calls_each_content_set
4
+ expected = [
5
+ {resource: '6', similarity: 74.037},
6
+ {resource: '12', similarity: 55.5},
7
+ {resource: '9', similarity: 6.67},
8
+ {resource: '3', similarity: 4.0},
9
+ {resource: '21', similarity: 2.86},
10
+ {resource: '15', similarity: 2.86}
11
+ ]
12
+ assert_equal expected, @weighted_group.similar_to(18)
13
+ end
14
+
15
+ def test_calls_each_content_set_with_limits
16
+ expected = [
17
+ {resource: '6', similarity: 74.037},
18
+ {resource: '12', similarity: 55.5},
19
+ {resource: '9', similarity: 6.67},
20
+ {resource: '3', similarity: 4.0},
21
+ {resource: '21', similarity: 2.86},
22
+ {resource: '15', similarity: 2.86}
23
+ ]
24
+ assert_equal expected[0..0], @weighted_group.similar_to(18, 1)
25
+ assert_equal expected[0..2], @weighted_group.similar_to(18, 3)
26
+ assert_equal expected, @weighted_group.similar_to(18, 6)
27
+ assert_equal expected, @weighted_group.similar_to(18, 99)
28
+ end
29
+
30
+ def test_filters_include_recommendations
31
+ expected = [{resource: '15', similarity: 2.86}]
32
+ @weighted_group.tag_set = @tag_set
33
+ assert_equal expected, @weighted_group.filtered_similar_to(18, include: ['mod5'])
34
+ end
35
+
36
+ def test_filters_exclude_recommendations
37
+ expected = [
38
+ {resource: '6', similarity: 74.037},
39
+ {resource: '12', similarity: 55.5},
40
+ {resource: '9', similarity: 6.67},
41
+ {resource: '3', similarity: 4.0}
42
+ ]
43
+ @weighted_group.tag_set = @tag_set
44
+ assert_equal expected, @weighted_group.filtered_similar_to(18, exclude: ['mod5', 'mod7'])
45
+ end
46
+
47
+ def test_filters_include_and_exclude_recommendations
48
+ expected = [
49
+ {resource: '16', similarity: 0.8},
50
+ {resource: '4', similarity: 0.667},
51
+ {resource: '12', similarity: 0.333}
52
+ ]
53
+ @weighted_group.tag_set = @tag_set
54
+ assert_equal expected, @weighted_group.filtered_similar_to(8, include: ['mod4'], exclude: ['mod5'])
55
+ end
56
+
57
+ def test_filters_include_and_exclude_recommendations_and_limits
58
+ expected = [
59
+ {resource: '16', similarity: 0.8},
60
+ {resource: '4', similarity: 0.667},
61
+ {resource: '12', similarity: 0.333}
62
+ ]
63
+ @weighted_group.tag_set = @tag_set
64
+ assert_equal expected[0..0], @weighted_group.filtered_similar_to(8, include: ['mod4'], exclude: ['mod5'], limit: 1)
65
+ assert_equal expected[0..1], @weighted_group.filtered_similar_to(8, include: ['mod4'], exclude: ['mod5'], limit: 2)
66
+ assert_equal expected, @weighted_group.filtered_similar_to(8, include: ['mod4'], exclude: ['mod5'], limit: 3)
67
+ assert_equal expected, @weighted_group.filtered_similar_to(8, include: ['mod4'], exclude: ['mod5'], limit: 99)
68
+ end
69
+
70
+ def test_similar_to_mutliple_items
71
+ expected = [
72
+ {resource: '12', similarity: 78.437},
73
+ {resource: '18', similarity: 78.037},
74
+ {resource: '9', similarity: 11.67},
75
+ {resource: '21', similarity: 9.0},
76
+ {resource: '15', similarity: 9.0},
77
+ {resource: '6', similarity: 6.67},
78
+ {resource: '3', similarity: 6.67},
79
+ {resource: '8', similarity: 0.667},
80
+ {resource: '16', similarity: 0.5},
81
+ {resource: '20', similarity: 0.4}
82
+ ]
83
+ @weighted_group.tag_set = @tag_set
84
+ assert_equal expected, @weighted_group.similar_to([3, 4, 5, 6, 7])
85
+ end
86
+
87
+ def test_filtered_similar_to_mutliple_items
88
+ expected = [
89
+ {resource: '12', similarity: 78.437},
90
+ {resource: '8', similarity: 0.667},
91
+ {resource: '16', similarity: 0.5},
92
+ ]
93
+
94
+ @weighted_group.tag_set = @tag_set
95
+ assert_equal expected, @weighted_group.filtered_similar_to([3, 4, 5, 6, 7], include: ['mod4'], exclude: ['mod5'])
96
+ end
97
+
98
+ def test_weightings_affect_scores
99
+ skip
100
+ end
101
+
102
+ def test_precalculates
103
+ skip
104
+ end
105
+
106
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: commendo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Styles
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-09 00:00:00.000000000 Z
11
+ date: 2015-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mysql2
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: progressbar
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +52,20 @@ dependencies:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: slop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: bundler
43
71
  requirement: !ruby/object:Gem::Requirement
@@ -108,16 +136,20 @@ dependencies:
108
136
  - - "~>"
109
137
  - !ruby/object:Gem::Version
110
138
  version: 5.0.8
111
- description: A Jaccard-similarity recommender using Redis sets
139
+ description: A Jaccard-similarity recommender using Redis sets or MySQL
112
140
  email:
113
141
  - rob.styles@dynamicorange.com
114
142
  executables:
143
+ - commendo-create-mysql-db
144
+ - commendo-create.sql
115
145
  - commendo-find-identical-pairs
116
146
  - commendo-load
117
147
  - commendo-load-tags-tsv
118
148
  - commendo-load-tsv
149
+ - commendo-load-tsv-mysql.rb
119
150
  - commendo-recommendations-distribution
120
151
  - commendo-similarity-distribution
152
+ - commendo-time-mysql.rb
121
153
  extensions: []
122
154
  extra_rdoc_files: []
123
155
  files:
@@ -127,23 +159,44 @@ files:
127
159
  - LICENSE.txt
128
160
  - README.md
129
161
  - Rakefile
162
+ - bin/commendo-create-mysql-db
163
+ - bin/commendo-create.sql
130
164
  - bin/commendo-find-identical-pairs
131
165
  - bin/commendo-load
132
166
  - bin/commendo-load-tags-tsv
133
167
  - bin/commendo-load-tsv
168
+ - bin/commendo-load-tsv-mysql.rb
134
169
  - bin/commendo-recommendations-distribution
135
170
  - bin/commendo-similarity-distribution
171
+ - bin/commendo-time-mysql.rb
136
172
  - commendo.gemspec
137
173
  - lib/commendo.rb
174
+ - lib/commendo/configuration.rb
138
175
  - lib/commendo/content_set.rb
139
- - lib/commendo/pair_comparison.lua
140
- - lib/commendo/similarity.lua
176
+ - lib/commendo/mysql-backed/content_set.rb
177
+ - lib/commendo/mysql-backed/tag_set.rb
178
+ - lib/commendo/mysql-backed/weighted_group.rb
179
+ - lib/commendo/redis-backed/content_set.rb
180
+ - lib/commendo/redis-backed/pair_comparison.lua
181
+ - lib/commendo/redis-backed/similarity.lua
182
+ - lib/commendo/redis-backed/tag_set.rb
183
+ - lib/commendo/redis-backed/weighted_group.rb
141
184
  - lib/commendo/tag_set.rb
142
185
  - lib/commendo/version.rb
143
186
  - lib/commendo/weighted_group.rb
144
- - test/content_set_test.rb
145
- - test/tag_set_test.rb
146
- - test/weighted_group_test.rb
187
+ - lib/mysql2/client.rb
188
+ - model 2.mwb
189
+ - sql_model.mwb
190
+ - test/configuration_test.rb
191
+ - test/mysql_content_set_test.rb
192
+ - test/mysql_tag_set_test.rb
193
+ - test/mysql_weighted_group_test.rb
194
+ - test/redis_content_set_test.rb
195
+ - test/redis_tag_set_test.rb
196
+ - test/redis_weighted_group_test.rb
197
+ - test/tests_for_content_sets.rb
198
+ - test/tests_for_tag_sets.rb
199
+ - test/tests_for_weighted_groups.rb
147
200
  homepage: ''
148
201
  licenses:
149
202
  - MIT
@@ -167,9 +220,16 @@ rubyforge_project:
167
220
  rubygems_version: 2.5.0
168
221
  signing_key:
169
222
  specification_version: 4
170
- summary: A Jaccard-similarity recommender using Redis sets
223
+ summary: A Jaccard-similarity recommender using Redis sets or MySQL
171
224
  test_files:
172
- - test/content_set_test.rb
173
- - test/tag_set_test.rb
174
- - test/weighted_group_test.rb
225
+ - test/configuration_test.rb
226
+ - test/mysql_content_set_test.rb
227
+ - test/mysql_tag_set_test.rb
228
+ - test/mysql_weighted_group_test.rb
229
+ - test/redis_content_set_test.rb
230
+ - test/redis_tag_set_test.rb
231
+ - test/redis_weighted_group_test.rb
232
+ - test/tests_for_content_sets.rb
233
+ - test/tests_for_tag_sets.rb
234
+ - test/tests_for_weighted_groups.rb
175
235
  has_rdoc:
@@ -1,408 +0,0 @@
1
- gem 'minitest'
2
- require 'minitest/autorun'
3
- require 'minitest/pride'
4
- require 'minitest/mock'
5
- require 'mocha/setup'
6
- require 'commendo'
7
-
8
- module Commendo
9
-
10
- class ContentSetTest < Minitest::Test
11
-
12
- def setup
13
- @redis = Redis.new(db: 15)
14
- @redis.flushdb
15
- @key_base = 'CommendoTests'
16
- @cs = ContentSet.new(@redis, @key_base)
17
- end
18
-
19
- def test_gives_similarity_key_for_resource
20
- key_base = 'CommendoTestsFooBarBaz'
21
- cs = ContentSet.new(@redis, key_base)
22
- assert_equal 'CommendoTestsFooBarBaz:similar:resource-1', cs.similarity_key('resource-1')
23
- end
24
-
25
- def test_recommends_when_added
26
- @cs.add('resource-1', 'group-1', 'group-2')
27
- @cs.add('resource-2', 'group-1')
28
- @cs.add('resource-3', 'group-1', 'group-2')
29
- @cs.add('resource-4', 'group-2')
30
- @cs.calculate_similarity
31
- expected = [
32
- {resource: 'resource-3', similarity: 1.0},
33
- {resource: 'resource-4', similarity: 0.667},
34
- {resource: 'resource-2', similarity: 0.667}
35
- ]
36
- assert_equal expected, @cs.similar_to('resource-1')
37
- end
38
-
39
- def test_recommends_limited_by_number
40
- @cs.add('resource-1', 'group-1', 'group-2')
41
- @cs.add('resource-2', 'group-1')
42
- @cs.add('resource-3', 'group-1', 'group-2')
43
- @cs.add('resource-4', 'group-2')
44
- @cs.calculate_similarity
45
- expected = [
46
- {resource: 'resource-3', similarity: 1.0},
47
- {resource: 'resource-4', similarity: 0.667},
48
- {resource: 'resource-2', similarity: 0.667}
49
- ]
50
- assert_equal expected[0..0], @cs.similar_to('resource-1', 1)
51
- assert_equal expected[0..1], @cs.similar_to('resource-1', 2)
52
- assert_equal expected, @cs.similar_to('resource-1', 3)
53
- assert_equal expected, @cs.similar_to('resource-1', 99)
54
- end
55
-
56
- def test_recommends_when_added_with_scores
57
- @cs.add('resource-1', ['group-1', 2], ['group-2', 2])
58
- @cs.add('resource-2', ['group-1', 7])
59
- @cs.add('resource-3', ['group-1', 2], ['group-2', 2])
60
- @cs.add('resource-4', ['group-2', 3])
61
- @cs.calculate_similarity
62
- expected = [
63
- {resource: 'resource-3', similarity: 1.0},
64
- {resource: 'resource-2', similarity: 0.818},
65
- {resource: 'resource-4', similarity: 0.714}
66
- ]
67
- assert_equal expected, @cs.similar_to('resource-1')
68
- end
69
-
70
- def test_recommends_with_large_number_of_groups
71
- (0..3000).each do |i|
72
- @cs.add('resource-1', ["group-#{i}", i/100.0], ["group-#{i+1}", i/20.0])
73
- @cs.add('resource-9', ["group-#{i}", i/100.0], ["group-#{i+1}", i/20.0])
74
- end
75
- @cs.calculate_similarity
76
- expected = [
77
- {resource: 'resource-9', similarity: 1.0}
78
- ]
79
- assert_equal expected, @cs.similar_to('resource-1')
80
- end
81
-
82
- def test_recommends_when_extra_scores_added
83
- test_recommends_when_added_with_scores #sets up the content set
84
- @cs.add('resource-3', ['group-1', 1], ['group-3', 2])
85
- @cs.add('resource-4', ['group-2', 1])
86
- @cs.add_by_group('group-1', ['newource-9', 100], 'resource-2', 'resource-3')
87
- @cs.add_by_group('group-2', 'resource-1', 'resource-3', 'resource-4')
88
- @cs.calculate_similarity
89
- expected = [
90
- {resource: 'newource-9', similarity: 1.0},
91
- {resource: 'resource-1', similarity: 0.769},
92
- {resource: 'resource-3', similarity: 0.706}
93
- ]
94
- assert_equal expected, @cs.similar_to('resource-2')
95
- end
96
-
97
- def test_recommends_when_added_by_group
98
- @cs.add_by_group('group-1', 'resource-1', 'resource-2', 'resource-3')
99
- @cs.add_by_group('group-2', 'resource-1', 'resource-3', 'resource-4')
100
- @cs.calculate_similarity
101
- expected = [
102
- {resource: 'resource-3', similarity: 1.0},
103
- {resource: 'resource-4', similarity: 0.667},
104
- {resource: 'resource-2', similarity: 0.667}
105
- ]
106
- assert_equal expected, @cs.similar_to('resource-1')
107
- end
108
-
109
- def test_recommends_when_added_by_group_with_scores
110
- @cs.add_by_group('group-1', ['resource-1', 2], ['resource-2', 3], ['resource-3', 7])
111
- @cs.add_by_group('group-2', ['resource-1', 2], ['resource-3', 3], ['resource-4', 5])
112
- @cs.calculate_similarity
113
- expected = [
114
- {resource: 'resource-3', similarity: 1.0},
115
- {resource: 'resource-4', similarity: 0.778},
116
- {resource: 'resource-2', similarity: 0.714}
117
- ]
118
- assert_equal expected, @cs.similar_to('resource-1')
119
- end
120
-
121
- def test_recommendations_are_isolated_by_key_base
122
- skip
123
- end
124
-
125
- def test_recommendations_are_isolated_by_redis_db
126
- skip
127
- end
128
-
129
- def test_calculates_similarity_scores
130
- (3..23).each do |group|
131
- (3..23).each do |res|
132
- @cs.add_by_group(group, res) if res % group == 0
133
- end
134
- end
135
- @cs.calculate_similarity
136
- expected = [
137
- {resource: '9', similarity: 0.667},
138
- {resource: '6', similarity: 0.667},
139
- {resource: '12', similarity: 0.5},
140
- {resource: '3', similarity: 0.4},
141
- {resource: '21', similarity: 0.286},
142
- {resource: '15', similarity: 0.286}
143
- ]
144
- assert_equal expected, @cs.similar_to(18)
145
- end
146
-
147
- def test_calculates_with_threshold
148
- (3..23).each do |group|
149
- (3..23).each do |res|
150
- @cs.add_by_group(group, res) if res % group == 0
151
- end
152
- end
153
- @cs.calculate_similarity(0.4)
154
- expected = [
155
- {resource: '9', similarity: 0.667},
156
- {resource: '6', similarity: 0.667},
157
- {resource: '12', similarity: 0.5}
158
- ]
159
- assert_equal expected, @cs.similar_to(18)
160
- end
161
-
162
- def test_calculate_copes_with_missing_resource
163
- @cs.calculate_similarity_for_resource('999999999999', 0.1)
164
- end
165
-
166
- def test_calculate_yields_after_each
167
- (3..23).each do |group|
168
- (3..23).each do |res|
169
- @cs.add_by_group(group, res) if res % group == 0
170
- end
171
- end
172
- 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']
173
- actual_keys = []
174
- @cs.calculate_similarity { |key, index, total|
175
- actual_keys << key
176
- }
177
- assert_equal expected_keys.sort, actual_keys.sort
178
- end
179
-
180
- def test_deletes_resource_from_everywhere
181
- (3..23).each do |group|
182
- (3..23).each do |res|
183
- @cs.add_by_group(group, res) if res % group == 0
184
- end
185
- end
186
- @cs.calculate_similarity
187
- assert similar_to(@cs, 18, 12)
188
-
189
- @cs.delete(12)
190
- assert_equal [], @cs.similar_to(12)
191
- refute similar_to(@cs, 18, 12)
192
-
193
- @cs.calculate_similarity
194
- assert_equal [], @cs.similar_to(12)
195
- refute similar_to(@cs, 18, 12)
196
- end
197
-
198
- def test_remove_from_groups
199
- (3..23).each do |group|
200
- (3..23).each do |res|
201
- @cs.add(res, group) if res % group == 0
202
- end
203
- end
204
- resource = 20
205
- assert_equal ['4','5','10','20'].sort!, @cs.groups(resource).sort!
206
- @cs.remove_from_groups(resource, 10)
207
- assert_equal ['4','5','20'].sort!, @cs.groups(resource).sort!
208
- @cs.remove_from_groups(resource, 4)
209
- assert_equal ['5','20'].sort!, @cs.groups(resource).sort!
210
- end
211
-
212
- def test_remove_causes_similarity_to_change_when_recalculated
213
- (3..23).each do |group|
214
- (3..23).each do |res|
215
- @cs.add(res, group) if res % group == 0
216
- end
217
- end
218
- @cs.calculate_similarity
219
- assert similar_to(@cs, 18, 12)
220
- @cs.remove_from_groups(18, 6, 3)
221
- @cs.calculate_similarity
222
- refute similar_to(@cs, 18, 12)
223
- end
224
-
225
- def test_accepts_incremental_updates
226
- (3..23).each do |group|
227
- (3..23).each do |res|
228
- @cs.add(res, group) if res % group == 0
229
- end
230
- end
231
- @cs.calculate_similarity
232
- assert similar_to(@cs, 18, 12)
233
- refute similar_to(@cs, 10, 12)
234
-
235
- @cs.add_and_calculate(12, 'foo', true)
236
- @cs.add_and_calculate(10, 'foo', true)
237
- assert similar_to(@cs, 10, 12)
238
- end
239
-
240
- def test_remove_and_calculate
241
- (3..23).each do |group|
242
- (3..23).each do |res|
243
- @cs.add(res, group) if res % group == 0
244
- end
245
- end
246
- @cs.calculate_similarity
247
- assert similar_to(@cs, 18, 12)
248
- @cs.remove_from_groups_and_calculate(18, 6, 3)
249
- refute similar_to(@cs, 18, 12)
250
- end
251
-
252
- def test_filters_include_by_tag_collection
253
- ts = TagSet.new(@redis, "#{@key_base}:tags")
254
- @cs = ContentSet.new(@redis, @key_base, ts)
255
- (3..23).each do |group|
256
- (3..23).each do |res|
257
- @cs.add(res, group) if res % group == 0
258
- ts.add(res, 'mod3') if res.modulo(3).zero?
259
- ts.add(res, 'mod4') if res.modulo(4).zero?
260
- ts.add(res, 'mod5') if res.modulo(5).zero?
261
- end
262
- end
263
- @cs.calculate_similarity
264
-
265
- actual = @cs.filtered_similar_to(10, include: ['mod5'])
266
- assert_equal 3, actual.length
267
- assert contains_resource('5', actual)
268
- assert contains_resource('15', actual)
269
- assert contains_resource('20', actual)
270
-
271
- end
272
-
273
- def test_filters_include_by_tag_collection_and_limit
274
- ts = TagSet.new(@redis, "#{@key_base}:tags")
275
- @cs = ContentSet.new(@redis, @key_base, ts)
276
- (3..23).each do |group|
277
- (3..23).each do |res|
278
- @cs.add(res, group) if res % group == 0
279
- ts.add(res, 'mod3') if res.modulo(3).zero?
280
- ts.add(res, 'mod4') if res.modulo(4).zero?
281
- ts.add(res, 'mod5') if res.modulo(5).zero?
282
- end
283
- end
284
- @cs.calculate_similarity
285
-
286
- actual = @cs.filtered_similar_to(10, include: ['mod5'], limit: 2)
287
- assert_equal 2, actual.length
288
- assert contains_resource('5', actual)
289
- #assert contains_resource('15', actual)
290
- assert contains_resource('20', actual)
291
-
292
- end
293
-
294
- def test_filters_exclude_by_tag_collection
295
- ts = TagSet.new(@redis, "#{@key_base}:tags")
296
- @cs = ContentSet.new(@redis, @key_base, ts)
297
- (3..23).each do |group|
298
- (3..23).each do |res|
299
- @cs.add(res, group) if res % group == 0
300
- ts.add(res, 'mod3') if res.modulo(3).zero?
301
- ts.add(res, 'mod4') if res.modulo(4).zero?
302
- ts.add(res, 'mod5') if res.modulo(5).zero?
303
- end
304
- end
305
- @cs.calculate_similarity
306
-
307
- actual = @cs.filtered_similar_to(10, exclude: ['mod3'])
308
- assert_equal 2, actual.length
309
- assert contains_resource('5', actual)
310
- assert contains_resource('20', actual)
311
- refute contains_resource('15', actual)
312
- end
313
-
314
- def test_filters_includes_and_exclude_by_tag_collection
315
- ts = TagSet.new(@redis, "#{@key_base}:tags")
316
- @cs = ContentSet.new(@redis, @key_base, ts)
317
- #Build some test data
318
- (3..23).each do |group|
319
- (3..23).each do |res|
320
- @cs.add(res, group) if res % group == 0
321
- ts.add(res, 'mod3') if res.modulo(3).zero?
322
- ts.add(res, 'mod4') if res.modulo(4).zero?
323
- ts.add(res, 'mod5') if res.modulo(5).zero?
324
- end
325
- end
326
- @cs.calculate_similarity
327
-
328
- actual = @cs.filtered_similar_to(12, include: ['mod4'], exclude: ['mod3', 'mod5'])
329
- assert_equal 3, actual.length
330
-
331
- refute contains_resource('6', actual)
332
- refute contains_resource('18', actual)
333
- assert contains_resource('4', actual)
334
- refute contains_resource('3', actual)
335
- refute contains_resource('9', actual)
336
- assert contains_resource('8', actual)
337
- refute contains_resource('21', actual)
338
- assert contains_resource('16', actual)
339
- refute contains_resource('15', actual)
340
- refute contains_resource('20', actual)
341
- end
342
-
343
- def test_recommends_for_many
344
- ts = TagSet.new(@redis, "#{@key_base}:tags")
345
- @cs = ContentSet.new(@redis, @key_base, ts)
346
- (3..23).each do |group|
347
- (3..23).each do |res|
348
- @cs.add(res, group) if res % group == 0
349
- ts.add(res, 'mod3') if res.modulo(3).zero?
350
- ts.add(res, 'mod4') if res.modulo(4).zero?
351
- ts.add(res, 'mod5') if res.modulo(5).zero?
352
- end
353
- end
354
- @cs.calculate_similarity
355
- expected = [
356
- {resource: '18', similarity: 1.834},
357
- {resource: '3', similarity: 1.734},
358
- {resource: '6', similarity: 1.167},
359
- {resource: '21', similarity: 1.086},
360
- {resource: '15', similarity: 1.086},
361
- {resource: '12', similarity: 1.0},
362
- {resource: '9', similarity: 0.833},
363
- {resource: '4', similarity: 0.4},
364
- {resource: '8', similarity: 0.333},
365
- {resource: '16', similarity: 0.286},
366
- {resource: '20', similarity: 0.25}
367
- ]
368
- actual = @cs.similar_to([12, 6, 9])
369
- assert_equal expected, actual
370
- #, include: ['mod4'], exclude: ['mod3', 'mod5']
371
- end
372
-
373
- def test_recommends_for_many_applies_filters
374
- ts = TagSet.new(@redis, "#{@key_base}:tags")
375
- @cs = ContentSet.new(@redis, @key_base, ts)
376
- (3..23).each do |group|
377
- (3..23).each do |res|
378
- @cs.add(res, group) if res % group == 0
379
- ts.add(res, 'mod3') if res.modulo(3).zero?
380
- ts.add(res, 'mod4') if res.modulo(4).zero?
381
- ts.add(res, 'mod5') if res.modulo(5).zero?
382
- end
383
- end
384
- @cs.calculate_similarity
385
- actual = @cs.filtered_similar_to([12, 6, 9], include: ['mod4'], exclude: ['mod3', 'mod5'])
386
- refute contains_resource('6', actual)
387
- refute contains_resource('18', actual)
388
- assert contains_resource('4', actual)
389
- refute contains_resource('3', actual)
390
- refute contains_resource('9', actual)
391
- assert contains_resource('8', actual)
392
- refute contains_resource('21', actual)
393
- assert contains_resource('16', actual)
394
- refute contains_resource('15', actual)
395
- refute contains_resource('20', actual)
396
- end
397
-
398
- def similar_to(cs, resource, similar)
399
- contains_resource(similar, cs.similar_to(resource))
400
- end
401
-
402
- def contains_resource(resource, similarities)
403
- !similarities.select { |sim| sim[:resource] == "#{resource}" }.empty?
404
- end
405
-
406
- end
407
-
408
- end