Files
danbooru/app/logical/recommender_service.rb
evazion fa985f08ab recommendations: filter out own uploads and favorites.
Filter out the user's own uploads and favorites from their
recommendations.

Note that in most cases a user's top-N recommendations will be things
they've already favorited. If a user has 10,000 favorites, most of their
top 10,000 recommendations will be their own favorites, so we have to
generate a little more than 10,000 recommendations to be sure they won't
all be filtered out.

In other words, the more favorites a user has, the more recommendations
we have to generate. The upper bound is clamped to 50,000 for
performance reasons. If a user has more favorites than this we may not
be able to find any recommendations for them.
2019-12-01 19:03:26 -06:00

63 lines
2.0 KiB
Ruby

module RecommenderService
module_function
MIN_POST_FAVS = 5
MIN_USER_FAVS = 50
CACHE_LIFETIME = 4.hours
def enabled?
Danbooru.config.recommender_server.present?
end
def available_for_post?(post)
enabled? && post.fav_count > MIN_POST_FAVS
end
def available_for_user?(user)
enabled? && user.favorite_count > MIN_USER_FAVS
end
def recommend_for_user(user_id, limit = 50)
body, status = HttpartyCache.get("#{Danbooru.config.recommender_server}/recommend/#{user_id}", params: { limit: limit }, expiry: CACHE_LIFETIME)
return [] if status != 200
process_recs(body, uploader_id: user_id, favoriter_id: user_id)
end
def recommend_for_post(post_id, limit = 50)
body, status = HttpartyCache.get("#{Danbooru.config.recommender_server}/similar/#{post_id}", params: { limit: limit }, expiry: CACHE_LIFETIME)
return [] if status != 200
process_recs(body, post_id: post_id)
end
def process_recs(recs, post_id: nil, uploader_id: nil, favoriter_id: nil)
recs = JSON.parse(recs)
posts = Post.where(id: recs.map(&:first))
posts = posts.where.not(id: post_id) if post_id
posts = posts.where.not(uploader_id: uploader_id) if uploader_id
posts = posts.where.not(id: Favorite.where(user_id: favoriter_id).select(:post_id)) if favoriter_id
id_to_score = recs.to_h
recs = posts.map { |post| { score: id_to_score[post.id], post: post } }
recs = recs.sort_by { |rec| -rec[:score] }
recs
end
def search(params)
if params[:user_id].present?
user = User.find(params[:user_id])
max_recommendations = params.fetch(:max_recommendations, user.favorite_count + 500).to_i.clamp(0, 50000)
recs = RecommenderService.recommend_for_user(params[:user_id], max_recommendations)
elsif params[:post_id].present?
max_recommendations = params.fetch(:max_recommendations, 50).to_i.clamp(0, 200)
recs = RecommenderService.recommend_for_post(params[:post_id], max_recommendations)
else
recs = []
end
recs
end
end