recommendable 2.0.4.20130313 → 2.1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b9fb8bf4282ccc0eeb6ef72f38ff05c007e50229
4
- data.tar.gz: 26d9e3d87b8d9059d9fa0002647d947f261f5224
3
+ metadata.gz: 91c45d80ed7ced8b477f8753ad6611b716e0395a
4
+ data.tar.gz: 7103dd3d7eb386ce8e019b45caade3139b7c8ea9
5
5
  SHA512:
6
- metadata.gz: 03456dbe5383f44f54160e332d2a2375109f1d94caa57b4878842777ede88f0e50714e13611bd6e0f3d3741efc8aa07c5951834d4e09076bf5e51154a2ba992f
7
- data.tar.gz: 58bda95c326eba0a1e3296d41ee4241f47a70f2773875c3bd0009ac1509b0c55db06e6185d222ad6a5b0e07e009619c7af6a248bab1aba82399895ef7fb753ad
6
+ metadata.gz: eca1215ebbdc0944e24a168ea6ee5a069300dbe7a7cf06051461cc4226eebcf4686ff321aacd07a932a96b08797d65c57084f52421b55ba292d4015095972c97
7
+ data.tar.gz: 50e043455fb3f7780727a54b7292e4c78045c3691c091ebb79af2204dc10df1e919a2255524b05210fed90186df812fe68cb20e2411cb5998e2de36f518436b5
data/lib/recommendable.rb CHANGED
@@ -11,7 +11,6 @@ require 'recommendable/workers/sidekiq'
11
11
  require 'recommendable/workers/resque'
12
12
  require 'recommendable/workers/delayed_job'
13
13
  require 'recommendable/workers/torque_box'
14
- require 'recommendable/workers/rails'
15
14
 
16
15
  module Recommendable
17
16
  class << self
@@ -5,43 +5,75 @@ module Recommendable
5
5
  # The ORM you are using. Currently supported: `:activerecord`, `:mongoid`, ':sequel', ':mongo-mapper' and `:datamapper`
6
6
  attr_accessor :orm
7
7
 
8
- # Recommendable's connection to Redis
8
+ # Recommendable's connection to Redis.
9
+ #
10
+ # Default: localhost:6379/0
9
11
  attr_accessor :redis
10
12
 
11
- # A prefix for all keys Recommendable uses
13
+ # A prefix for all keys Recommendable uses.
14
+ #
15
+ # Default: recommendable
12
16
  attr_accessor :redis_namespace
13
17
 
14
18
  # Whether or not to automatically enqueue users to have their recommendations
15
- # refreshed after they like/dislike an item
19
+ # refreshed after they like/dislike an item.
20
+ #
21
+ # Default: true
16
22
  attr_accessor :auto_enqueue
17
23
 
18
- # The name of the queue that background jobs will be placed in
19
- attr_accessor :queue_name
20
-
21
24
  # The number of nearest neighbors (k-NN) to check when updating
22
25
  # recommendations for a user. Set to `nil` if you want to check all
23
- # neighbors as opposed to a subset of the nearest ones.
26
+ # neighbors as opposed to a subset of the nearest ones. Set this to a lower
27
+ # number to improve Redis memory usage.
28
+ #
29
+ # Default: nil
24
30
  attr_accessor :nearest_neighbors
25
31
 
32
+ # Like kNN, but also uses some number of most dissimilar users when
33
+ # updating recommendations for a user. Because, hey, disagreements are
34
+ # just as important as agreements, right? If `nearest_neighbors` is set to
35
+ # `nil`, this configuration is ignored. Set this to a lower number
36
+ # to improve Redis memory usage.
37
+ #
38
+ # Default: nil
39
+ attr_accessor :furthest_neighbors
40
+
41
+ # The number of recommendations to store per user. Set this to a lower
42
+ # number to improve Redis memory usage.
43
+ #
44
+ # Default: 100
45
+ attr_accessor :recommendations_to_store
46
+
26
47
  attr_accessor :ratable_classes, :user_class
27
48
 
28
49
  # Default values
29
50
  def initialize
30
- @redis = Redis.new
31
- @redis_namespace = :recommendable
32
- @auto_enqueue = true
33
- @queue_name = :recommendable
34
- @ratable_classes = []
35
- @nearest_neighbors = nil
51
+ @redis = Redis.new
52
+ @redis_namespace = :recommendable
53
+ @auto_enqueue = true
54
+ @ratable_classes = []
55
+ @nearest_neighbors = nil
56
+ @furthest_neihbors = nil
57
+ @recommendations_to_store = 100
58
+ end
59
+
60
+ def queue_name
61
+ warn "Recommendable.config.queue_name has been deprecated. Jobs will always be placed in a queue named 'recommendable'."
62
+ end
63
+
64
+ def queue_name=(queue_name)
65
+ warn "Recommendable.config.queue_name has been deprecated. Jobs will always be placed in a queue named 'recommendable'."
36
66
  end
37
67
  end
38
68
 
39
69
  class << self
40
- attr_accessor :config
41
-
42
70
  def configure
43
71
  @config ||= Configuration.new
44
72
  yield @config
45
73
  end
74
+
75
+ def config
76
+ @config ||= Configuration.new
77
+ end
46
78
  end
47
79
  end
@@ -67,6 +67,13 @@ module Recommendable
67
67
  Recommendable.redis.zadd(similarity_set, similarity_between(user_id, id), id)
68
68
  end
69
69
 
70
+ if knn = Recommendable.config.nearest_neighbors
71
+ length = Recommendable.redis.zcard(similarity_set)
72
+ kfn = Recommendable.config.furthest_neighbors || 0
73
+
74
+ Recommendable.redis.zremrangebyrank(similarity_set, kfn, length - knn - 1)
75
+ end
76
+
70
77
  true
71
78
  end
72
79
 
@@ -110,6 +117,11 @@ module Recommendable
110
117
  end
111
118
 
112
119
  Recommendable.redis.del(temp_set)
120
+
121
+ if number_recommendations = Recommendable.config.recommendations_to_store
122
+ length = Recommendable.redis.zcard(recommended_set)
123
+ Recommendable.redis.zremrangebyrank(recommended_set, 0, length - number_recommendations - 1)
124
+ end
113
125
  end
114
126
 
115
127
  true
@@ -29,7 +29,6 @@ module Recommendable
29
29
  warn "Model #{self} is not using a supported ORM. You must handle removal from Redis manually when destroying instances."
30
30
  end
31
31
 
32
-
33
32
  # Whether or not items belonging to this class can be recommended.
34
33
  #
35
34
  # @return true if a user class `recommends :this`
@@ -58,26 +57,28 @@ module Recommendable
58
57
  # Completely removes this item from redis. Called from a before_destroy hook.
59
58
  # @private
60
59
  def remove_from_recommendable!
60
+ sets = [] # SREM needed
61
+ zsets = [] # ZREM needed
62
+ keys = [] # DEL needed
61
63
  # Remove this item from the score zset
62
- Recommendable.redis.zrem(Recommendable::Helpers::RedisKeyMapper.score_set_for(self.class), id)
64
+ zsets << Recommendable::Helpers::RedisKeyMapper.score_set_for(self.class)
63
65
 
64
66
  # Remove this item's liked_by/disliked_by sets
65
- %w[liked_by disliked_by].each do |action|
66
- set = Recommendable::Helpers::RedisKeyMapper.send("#{action}_set_for", self.class, id)
67
- Recommendable.redis.del(set)
68
- end
67
+ keys << Recommendable::Helpers::RedisKeyMapper.liked_by_set_for(self.class, id)
68
+ keys << Recommendable::Helpers::RedisKeyMapper.disliked_by_set_for(self.class, id)
69
69
 
70
70
  # Remove this item from any user's like/dislike/hidden/bookmark sets
71
71
  %w[liked disliked hidden bookmarked].each do |action|
72
- set = Recommendable::Helpers::RedisKeyMapper.send("#{action}_set_for", self.class, id)
73
- Recommendable.redis.keys(set).each do |set|
74
- Recommendable.redis.srem(set, id)
75
- end
72
+ sets += Recommendable.redis.keys(Recommendable::Helpers::RedisKeyMapper.send("#{action}_set_for", self.class, '*'))
76
73
  end
77
74
 
78
75
  # Remove this item from any user's recommendation zset
79
- Recommendable.redis.keys(Recommendable::Helpers::RedisKeyMapper.recommended_set_for(self.class, '*')).each do |zset|
80
- Recommendable.redis.zrem(zset, id)
76
+ zsets += Recommendable.redis.keys(Recommendable::Helpers::RedisKeyMapper.recommended_set_for(self.class, '*'))
77
+
78
+ Recommendable.redis.pipelined do |redis|
79
+ sets.each { |set| redis.srem(set, id) }
80
+ zsets.each { |zset| redis.zrem(zset, id) }
81
+ redis.del(*keys)
81
82
  end
82
83
  end
83
84
  end
@@ -36,31 +36,34 @@ module Recommendable
36
36
  # Removes a user from Recommendable. Called internally on a before_destroy hook.
37
37
  # @private
38
38
  def remove_from_recommendable!
39
- # Remove this user's similarity ZSET
40
- Recommendable.redis.del(Recommendable::Helpers::RedisKeyMapper.similarity_set_for(id))
39
+ sets = [] # SREM needed
40
+ zsets = [] # ZREM needed
41
+ keys = [] # DEL needed
41
42
 
42
43
  # Remove from other users' similarity ZSETs
43
- Recommendable.redis.keys(Recommendable::Helpers::RedisKeyMapper.similarity_set_for('*')).each do |zset|
44
- Recommendable.redis.zrem(zset, id)
45
- end
44
+ zsets += Recommendable.redis.keys(Recommendable::Helpers::RedisKeyMapper.similarity_set_for('*'))
45
+
46
+ # Remove this user's similarity ZSET
47
+ keys << Recommendable::Helpers::RedisKeyMapper.similarity_set_for(id)
46
48
 
47
49
  # For each ratable class...
48
50
  Recommendable.config.ratable_classes.each do |klass|
49
51
  # Remove this user from any class member's liked_by/disliked_by sets
50
- Recommendable.redis.keys(Recommendable::Helpers::RedisKeyMapper.liked_by_set_for(klass, '*')).each do |set|
51
- Recommendable.redis.srem(set, id)
52
- end
53
-
54
- Recommendable.redis.keys(Recommendable::Helpers::RedisKeyMapper.disliked_by_set_for(klass, '*')).each do |set|
55
- Recommendable.redis.srem(set, id)
56
- end
52
+ sets += Recommendable.redis.keys(Recommendable::Helpers::RedisKeyMapper.liked_by_set_for(klass, '*'))
53
+ sets += Recommendable.redis.keys(Recommendable::Helpers::RedisKeyMapper.disliked_by_set_for(klass, '*'))
57
54
 
58
55
  # Remove this user's liked/disliked/hidden/bookmarked/recommended sets for the class
59
- Recommendable.redis.del(Recommendable::Helpers::RedisKeyMapper.liked_set_for(klass, id))
60
- Recommendable.redis.del(Recommendable::Helpers::RedisKeyMapper.disliked_set_for(klass, id))
61
- Recommendable.redis.del(Recommendable::Helpers::RedisKeyMapper.hidden_set_for(klass, id))
62
- Recommendable.redis.del(Recommendable::Helpers::RedisKeyMapper.bookmarked_set_for(klass, id))
63
- Recommendable.redis.del(Recommendable::Helpers::RedisKeyMapper.recommended_set_for(klass, id))
56
+ keys << Recommendable::Helpers::RedisKeyMapper.liked_set_for(klass, id)
57
+ keys << Recommendable::Helpers::RedisKeyMapper.disliked_set_for(klass, id)
58
+ keys << Recommendable::Helpers::RedisKeyMapper.hidden_set_for(klass, id)
59
+ keys << Recommendable::Helpers::RedisKeyMapper.bookmarked_set_for(klass, id)
60
+ keys << Recommendable::Helpers::RedisKeyMapper.recommended_set_for(klass, id)
61
+ end
62
+
63
+ Recommendable.redis.pipelined do |redis|
64
+ sets.each { |set| redis.srem(set, id) }
65
+ zsets.each { |zset| redis.zrem(zset, id) }
66
+ redis.del(*keys)
64
67
  end
65
68
  end
66
69
  end
@@ -1,8 +1,8 @@
1
1
  module Recommendable
2
2
  MAJOR = 2
3
- MINOR = 0
4
- PATCH = 4
5
- PRE = 20130313
3
+ MINOR = 1
4
+ PATCH = 0
5
+ PRE = 1
6
6
 
7
7
  VERSION = [MAJOR, MINOR, PATCH, PRE].compact.join '.'
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recommendable
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.4.20130313
4
+ version: 2.1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Celis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-03-13 00:00:00.000000000 Z
11
+ date: 2013-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -165,7 +165,6 @@ files:
165
165
  - lib/recommendable/rater.rb
166
166
  - lib/recommendable/version.rb
167
167
  - lib/recommendable/workers/delayed_job.rb
168
- - lib/recommendable/workers/rails.rb
169
168
  - lib/recommendable/workers/resque.rb
170
169
  - lib/recommendable/workers/sidekiq.rb
171
170
  - lib/recommendable/workers/torque_box.rb
@@ -1,16 +0,0 @@
1
- module Recommendable
2
- module Workers
3
- class Rails
4
- attr_accessor :user_id
5
-
6
- def initialize(user_id)
7
- @user_id = user_id
8
- end
9
-
10
- def run
11
- Recommendable::Helpers::Calculations.update_similarities_for(user_id)
12
- Recommendable::Helpers::Calculations.update_recommendations_for(user_id)
13
- end
14
- end
15
- end
16
- end