-
-
Save ricardobeat/1502809 to your computer and use it in GitHub Desktop.
Fake Recommender class for CoffeeScript/tame discussion - using async (http://github.com/caolan/async)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async = require 'async' | |
class Recommender | |
getRecommendations: (search_params, respond) -> | |
# Do 2 things at once: | |
# - check if we have a logged in user, get their info | |
# - fire off distributed requests for search queries | |
# ------------------------------------------------------------------------ | |
interests = search_params.interests.slice() | |
interest_meta = {} | |
logged_in = user_info = null | |
step_one = (next) => | |
async.parallel [ | |
(fw) => | |
@isLoggedIn -> | |
[logged_in, user_info] = arguments | |
fw null | |
(fw) => | |
q = async.queue ((text, cb) => @getMetaInfo text, cb), 20 | |
q.drain = fw | |
for interest in interests | |
q.push interest.text, (info) -> interest_meta[interest.text] = info | |
], next | |
# | |
# Do more things at once: | |
# - get a taste profile for the user (only if logged in) | |
# - get taste profiles for each legit search interest | |
# ------------------------------------------------------------------------ | |
user_taste_profile = null | |
taste_profiles = {} | |
step_two = (next) => | |
async.parallel [ | |
(fw) => | |
return fw null unless logged_in | |
@getUserTasteProfile user_info.id, (p) -> | |
user_taste_profile = p | |
fw null | |
(fw) => | |
q = async.queue ((id, cb) => @getTasteProfile id, cb), 20 | |
q.drain = fw | |
for own key, interest of interests | |
q.push interest.id, (err, p) -> taste_profiles[interest.id] = p | |
], next | |
# | |
# We have recs, but just [id, score] pairs. Let's: | |
# - look up info on each interest | |
# - save that this user got these recs (if logged in) | |
# ------------------------------------------------------------------------ | |
full_recommendations = [] | |
step_three = => | |
@getRecsFromTasteProfiles taste_profiles, user_taste_profile, (recommendations) => | |
async.waterfall [ | |
(fw) => | |
q = async.queue ((info, cb) => @getInfo info, cb), 20 | |
q.drain = fw | |
for id in recommendations | |
q.push id, (err, info) -> full_recommendations.push info | |
(fw) => | |
return fw null unless logged_in | |
@rememberRecommendations user_info.id, recommendations, fw | |
] | |
async.series [step_one, step_two, step_three] | |
# -------------------------------------------------------------------------- | |
# -------------------------------------------------------------------------- | |
# Fake functions, for those who want to test getRecommendations() | |
# I added some management of what's concurrent | |
# -------------------------------------------------------------------------- | |
isLoggedIn: (cb) -> | |
@_fakeRpc "logged_in", => | |
if Math.random() < 0.5 | |
cb true, | |
id: "user_id_#{Math.random()}" | |
age: Math.floor(18 + 30 * Math.random()) | |
else | |
cb false, null | |
getRecsFromTasteProfiles: (tp, utp, cb) -> | |
@_fakeRpc "get_recs", => | |
res = [] | |
for i in [0..10] | |
res.push ["interest_id_#{Math.random()}", Math.random()] | |
cb res | |
getMetaInfo: (search_str, cb) -> | |
@_fakeRpc "get_meta", -> | |
cb id: "interest_id_#{Math.random()}" | |
getUserTasteProfile: (uid, cb) -> | |
@_fakeRpc "get_user_taste", -> | |
cb Math.random() | |
getTasteProfile: (uid, cb) -> | |
@_fakeRpc "get_taste", -> | |
cb Math.random() | |
getInfo: (rec_id, cb) -> | |
@_fakeRpc "get_info", -> | |
cb | |
title: "Bangin'" | |
avg_age: Math.floor(18 + 30 * Math.random()) | |
id: rec_id | |
rememberRecommendations: (id, recommendations, cb) -> | |
@_fakeRpc "rememberRecommendations", cb | |
# -------------------------------------------------------------------------- | |
_fakeRpc: (name, cb) -> | |
@_openRpcCount = {} if not @_openRpcCount | |
@_openRpcCount[name] = 0 if not @_openRpcCount[name] | |
@_openRpcCount[name]++ | |
@_printFakeRpcData() | |
setTimeout((()=> | |
@_openRpcCount[name]-- | |
@_printFakeRpcData() | |
cb()),Math.random()*1000) | |
_printFakeRpcData: -> | |
console.log "open remote calls: " + ("#{k} (#{v})" for k,v of @_openRpcCount).join(" ") | |
R = new Recommender() | |
search_params = | |
interests: [ { text: "football", opinion: 1.23 }, { text: "basketball", opinion: 13.23 } ] | |
start_time = Date.now() | |
R.getRecommendations search_params, (res) -> | |
console.log "-------------" | |
console.log "Done. Got #{res.length} results in #{Date.now() - start_time}ms" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
could use
async.waterfall
instead ofseries
to avoid the top-level variables.