recommendable 2.0.4.20130313 → 2.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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