Skip to content

Commit

Permalink
Add Book#similar_books
Browse files Browse the repository at this point in the history
  • Loading branch information
maccman committed Sep 11, 2008
1 parent e927612 commit f30d171
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 3 deletions.
4 changes: 4 additions & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ user = User.find(:first)
user.similar_users #=> [...]
user.recommended_books #=> [...]

book = Book.find(:first)
book.similar_books #=> [...]

Example 2
=========

Expand Down Expand Up @@ -110,6 +113,7 @@ user.recommended_books #=> [...]
#
# The advantage of using a dataset is that you don't need to load all the users & items into
# memory (which you do normally). The disadvantage is that you won't get as accurate results.
#

Contact
=======
Expand Down
49 changes: 46 additions & 3 deletions lib/acts_as_recommendable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,15 @@ def acts_as_recommendable(on, options = {})

class_inheritable_accessor :aar_options
self.aar_options = options

options[:on_class].class_eval do
define_method "similar_#{options[:on]}" do
Logic.similar_items(self, options)
end
end

define_method "similar_#{options[:class].name.underscore.pluralize}" do
Logic.similar(self, options)
Logic.similar_users(self, options)
end

define_method "recommended_#{options[:on_class].name.underscore.pluralize}" do
Expand Down Expand Up @@ -67,7 +73,7 @@ def item.aar_score=(d); @aar_score = d; end
}.compact.inject({}) {|h, item| h[item.id] = item; h }
end
end

end
end

Expand Down Expand Up @@ -144,7 +150,7 @@ def self.sim_pearson(prefs, items, person1, person2)
num / den
end

def self.similar(user, options)
def self.similar_users(user, options)
rankings = []
items, prefs = self.matrix(options)
prefs.each do |u, _|
Expand All @@ -169,6 +175,43 @@ def user.similar_score=(d); @similar_score = d; end
user
}
end

def self.similar_items(item, options)
if options[:use_dataset]
if options[:split_dataset]
rankings = Rails.cache.read("aar_#{options[:on]}_#{item.id}")
else
cached_dataset = Rails.cache.read("aar_#{options[:on]}_dataset")
logger.warn 'ActsRecommendable has an empty dataset - rebuild it' unless cached_dataset
rankings = cached_dataset && cached_dataset[self.id]
end
else
users, prefs = self.inverted_matrix(options)
rankings = []
prefs.each do |i, _|
next if i == item.id
rankings << [self.__send__(options[:algorithm], prefs, users, item.id, i), i]
end
end
return [] unless rankings

rankings = rankings.select {|score, _| score > 0.0 }
rankings = rankings.sort_by {|score, _| score }.reverse
rankings = rankings[0..(options[:limit] - 1)]

# Return the sorted list
ranking_ids = rankings.collect {|_, u| u }
ar_items = options[:on_class].find_some_without_failing(ranking_ids)
ar_items = ar_items.inject({}){ |h, item| h[item.id] = item; h }

rankings.collect {|score, item_id|
item = ar_items[item_id]
def item.similar_score; return @similar_score; end
def item.similar_score=(d); @similar_score = d; end
item.similar_score = score
item
}
end

def self.recommended(user, options)
totals = {}
Expand Down

0 comments on commit f30d171

Please sign in to comment.