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.
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