2015-02-23 06:21:36 +00:00
module JamRuby
2015-05-07 12:55:13 +00:00
class MusicianSearch < BaseSearch
2015-02-23 06:21:36 +00:00
2015-05-18 04:00:12 +00:00
cattr_accessor :jschema , :search_meta
2015-05-07 12:55:13 +00:00
attr_accessor :user_counters
2015-04-02 14:52:16 +00:00
2015-03-06 06:33:38 +00:00
KEY_STUDIOS = 'studio_sessions'
2015-02-23 06:21:36 +00:00
KEY_AGES = 'ages'
2015-02-28 21:18:43 +00:00
KEY_INTERESTS = 'interests'
2015-02-23 06:21:36 +00:00
SORT_VALS = %W{ latency distance }
SORT_ORDERS = {
SORT_VALS [ 0 ] = > 'Latency to Me' ,
SORT_VALS [ 1 ] = > 'Distance to Me'
}
2015-03-05 05:51:26 +00:00
STUDIO_COUNTS = [ ANY_VAL_INT , 0 , 1 , 2 , 3 , 4 ]
2015-02-23 06:21:36 +00:00
STUDIOS_LABELS = {
STUDIO_COUNTS [ 0 ] = > 'Any' ,
2015-04-04 20:26:41 +00:00
STUDIO_COUNTS [ 1 ] = > 'under 10' ,
STUDIO_COUNTS [ 2 ] = > '10 to 50' ,
STUDIO_COUNTS [ 3 ] = > '50 to 100' ,
STUDIO_COUNTS [ 4 ] = > 'over 100'
2015-02-23 06:21:36 +00:00
}
2015-04-04 20:26:41 +00:00
AGE_COUNTS = [ 10 , 20 , 30 , 40 , 50 ]
2015-02-23 06:21:36 +00:00
AGES = {
2015-04-04 20:26:41 +00:00
AGE_COUNTS [ 0 ] = > 'Teens' ,
AGE_COUNTS [ 1 ] = > " 20's " ,
AGE_COUNTS [ 2 ] = > " 30's " ,
AGE_COUNTS [ 3 ] = > " 40's " ,
AGE_COUNTS [ 4 ] = > " 50+ "
2015-02-23 06:21:36 +00:00
}
INTEREST_VALS = [ ANY_VAL_STR ,
GenrePlayer :: VIRTUAL_BAND ,
GenrePlayer :: TRADITIONAL_BAND ,
GenrePlayer :: PAID_SESSION ,
GenrePlayer :: FREE_SESSION ,
GenrePlayer :: COWRITING ,
]
INTERESTS = {
INTEREST_VALS [ 0 ] = > 'Any' ,
INTEREST_VALS [ 1 ] = > 'Virtual Band' ,
INTEREST_VALS [ 2 ] = > 'Traditional Band' ,
INTEREST_VALS [ 3 ] = > 'Paid Sessions' ,
INTEREST_VALS [ 4 ] = > 'Free Sessions' ,
INTEREST_VALS [ 5 ] = > 'Co-Writing'
}
2015-05-18 04:00:12 +00:00
def self . json_schema
return @@jschema if @@jschema
@@jschema = BaseSearch . json_schema . merge ( {
KEY_INTERESTS = > INTEREST_VALS [ 0 ] ,
KEY_STUDIOS = > STUDIO_COUNTS [ 0 ] . to_s ,
2015-05-27 08:20:46 +00:00
KEY_AGES = > [ ] ,
KEY_SKILL = > self :: SKILL_VALS [ 0 ] . to_s ,
2015-05-18 04:00:12 +00:00
} )
end
2015-02-23 06:21:36 +00:00
2015-05-18 04:00:12 +00:00
def self . search_filter_meta
return @@search_meta if @@search_meta
@@search_meta = super . merge ( {
interests : { keys : INTEREST_VALS , map : INTERESTS } ,
ages : { keys : AGE_COUNTS , map : AGES }
} )
end
2015-02-23 06:21:36 +00:00
2015-05-07 12:55:13 +00:00
def self . search_target_class
User
2015-02-23 06:21:36 +00:00
end
def _ages ( rel )
unless ( vals = json [ KEY_AGES ] ) . blank?
return rel if vals . detect { | vv | ANY_VAL_INT == vv }
arels = [ ]
vals . each do | val |
today = Date . today
2015-04-04 14:55:41 +00:00
case val . to_i
2015-02-23 06:21:36 +00:00
when 10
arels << User . where ( " birth_date >= ? AND birth_date < ? " ,
today - 20 . years , today - 10 . years )
when 20
arels << User . where ( " birth_date >= ? AND birth_date < ? " ,
today - 30 . years , today - 20 . years )
when 30
arels << User . where ( " birth_date >= ? AND birth_date < ? " ,
today - 40 . years , today - 50 . years )
when 40
arels << User . where ( " birth_date >= ? AND birth_date < ? " ,
today - 50 . years , today - 40 . years )
when 50
arels << User . where ( " birth_date <= ? " , today - 50 . years )
end
end
rel = rel . where ( " birth_date IS NOT NULL " )
2015-02-24 06:06:40 +00:00
sql = " ( #{ arels . map ( & :where_values ) . flatten . join ( ') OR (' ) } ) "
rel = rel . where ( sql )
2015-02-23 06:21:36 +00:00
end
rel
end
def _studios ( rel )
2015-03-09 06:13:56 +00:00
ss = json [ KEY_STUDIOS ] . to_i
rel = rel . where ( 'studio_session_count = ?' , ss ) if 0 < = ss
2015-02-23 06:21:36 +00:00
rel
end
def _gigs ( rel )
2015-03-09 06:13:56 +00:00
gg = json [ KEY_GIGS ] . to_i
rel = rel . where ( 'concert_count = ?' , gg ) if 0 < = gg
2015-02-23 06:21:36 +00:00
rel
end
def _skills ( rel )
2015-03-26 05:56:43 +00:00
if 0 < ( val = json [ KEY_SKILL ] . to_i )
2015-05-27 04:10:05 +00:00
rel = rel . where ( skill_level : val )
2015-02-23 06:21:36 +00:00
end
rel
end
def _interests ( rel )
2015-03-26 05:56:43 +00:00
val = json [ KEY_INTERESTS ]
2015-02-23 06:21:36 +00:00
if val . present? && ANY_VAL_STR != val
2015-02-28 21:18:43 +00:00
rel = rel . where ( " #{ val } = ? " , true )
2015-02-23 06:21:36 +00:00
end
rel
end
2021-08-31 18:25:15 +00:00
def _active_within ( rel )
if 0 < = ( val = json [ KEY_ACTIVE_WITHIN ] . to_i )
2021-12-22 13:26:13 +00:00
rel = rel . where ( " users.id IN (SELECT users.id FROM users GROUP BY id HAVING GREATEST(updated_at, last_jam_updated_at) >= ?) " , val . days . ago . at_beginning_of_day )
2021-08-31 18:25:15 +00:00
end
rel
end
2015-05-27 08:20:46 +00:00
def _sort_order ( rel )
val = json [ self . class :: KEY_SORT_ORDER ]
if self . class :: SORT_VALS [ 1 ] == val
2021-02-01 14:56:47 +00:00
#locidispid = self.user.last_jam_locidispid || 0
#my_locid = locidispid / 1000000
#rel = rel.joins("LEFT JOIN geoiplocations AS my_geo ON my_geo.locid = #{my_locid}")
#rel = rel.joins("LEFT JOIN geoiplocations AS other_geo ON users.last_jam_locidispid/1000000 = other_geo.locid")
#rel = rel.group("users.id, my_geo.geog, other_geo.geog")
#rel = rel.order('st_distance(my_geo.geog, other_geo.geog)')
2015-05-27 08:20:46 +00:00
else
rel = rel . joins ( " LEFT JOIN current_scores ON current_scores.a_userid = users.id AND current_scores.b_userid = ' #{ self . user . id } ' " )
rel = rel . order ( 'current_scores.full_score ASC' )
end
rel
end
2021-12-22 13:26:13 +00:00
def _sort_by_ids_ordinality ( rel , ids )
return rel if ids . empty?
values = [ ]
ids . each_with_index do | id , index |
values << " (' #{ id } ', #{ index + 1 } ) "
end
rel = rel . joins ( " JOIN (VALUES #{ values . join ( " , " ) } ) as x (id, ordering) ON users.id = x.id " )
rel . order ( 'x.ordering' )
end
2022-02-08 15:25:00 +00:00
def do_filter ( user_ids )
2015-04-29 20:34:28 +00:00
rel = User . musicians . where ( 'users.id <> ?' , self . user . id )
2022-02-08 15:25:00 +00:00
2021-12-18 17:04:34 +00:00
#user_ids parameter is used to track users returned from neo4j user's latency data service. #if this variable is not null means we are calling this method with neo4j latency data (currently this call only comes from api_search_controller#filter)
2021-12-22 13:26:13 +00:00
#debugger
2021-12-18 14:41:28 +00:00
unless user_ids . nil?
2021-12-13 14:16:07 +00:00
if user_ids . empty?
2022-02-08 15:25:00 +00:00
rel = User . none # no user_ids have been passed from latency service. which means no results for the latency based filter conditions. So we return an empty data set
2021-12-13 14:16:07 +00:00
else
2022-02-08 15:25:00 +00:00
rel = rel . where ( id : user_ids )
2021-12-22 13:26:13 +00:00
#following line does not work in postgresql 9.3 - array_position function was intruduced in postgresql 9.4
#NOTE: we can change to this once we upgrade postgresql
#rel = rel.where(id: user_ids).where('users.id <> ?', self.user.id).order("array_position(ARRAY[#{user_ids.map { |i| "'#{i}'" }.join(',')}], id::TEXT)")
2022-10-18 16:37:15 +00:00
#rel = self._sort_by_ids_ordinality(rel, user_ids)
2021-12-13 14:16:07 +00:00
end
2021-08-09 14:18:10 +00:00
end
2022-10-18 16:37:15 +00:00
rel = rel . select ( " users.*, (SELECT count(friendships.id) > 0 AS is_friend FROM friendships WHERE friendships.user_id = users.id AND friendships.friend_id = ' #{ self . user . id } '), (SELECT count(follows.id) > 0 AS is_following FROM follows WHERE follows.user_id = users.id AND follows.followable_id = ' #{ self . user . id } ' AND follows.followable_type = 'JamRuby::User'), (SELECT count(friend_requests.id) > 0 as pending_friend_request from friend_requests WHERE (friend_requests.user_id = ' #{ self . user . id } ' AND friend_requests.friend_id = users.id) OR (friend_requests.user_id = users.id AND friend_requests.friend_id = ' #{ self . user . id } ') ) " )
2022-02-08 15:25:00 +00:00
rel
end
2021-12-13 14:16:07 +00:00
2022-02-08 15:25:00 +00:00
def do_search ( params = { } , user_ids )
rel = User . musicians . where ( 'users.id <> ?' , self . user . id )
2020-10-14 02:05:08 +00:00
rel = Search . scope_schools_together ( rel , self . user )
2015-02-23 06:21:36 +00:00
rel = self . _genres ( rel )
rel = self . _ages ( rel )
rel = self . _studios ( rel )
rel = self . _gigs ( rel )
rel = self . _skills ( rel )
rel = self . _instruments ( rel )
rel = self . _interests ( rel )
2021-12-07 16:05:19 +00:00
#rel = self._sort_order(rel)
2021-08-09 14:18:10 +00:00
rel = self . _joined_within ( rel )
2021-08-31 18:25:15 +00:00
rel = self . _active_within ( rel )
2015-02-23 06:21:36 +00:00
rel
end
2015-05-07 12:55:13 +00:00
def search_includes ( rel )
rel . includes ( [ :instruments , :followings , :friends ] )
2015-04-02 14:52:16 +00:00
end
2022-10-18 16:37:15 +00:00
def filter_includes ( rel )
rel . includes ( [ :instruments , :genres ] )
end
def process_results_page ( _results , skip_counters = false )
2015-04-02 14:52:16 +00:00
@results = _results
2022-10-18 16:37:15 +00:00
2015-04-02 14:52:16 +00:00
@user_counters = { } and return self unless user
2022-10-18 16:37:15 +00:00
unless skip_counters
@user_counters = @results . inject ( { } ) { | hh , val | hh [ val . id ] = [ ] ; hh }
mids = " ' #{ @results . map ( & :id ) . join ( " ',' " ) } ' "
# this gets counts for each search result on friends/follows/records/sessions
@results . each do | uu |
counters = { }
counters [ COUNT_FRIEND ] = Friendship . where ( :user_id = > uu . id ) . count
counters [ COUNT_FOLLOW ] = Follow . where ( :followable_id = > uu . id ) . count
counters [ COUNT_RECORD ] = ClaimedRecording . where ( :user_id = > uu . id ) . count
counters [ COUNT_SESSION ] = MusicSession . where ( :user_id = > uu . id ) . count
@user_counters [ uu . id ] << counters
end
2015-04-02 14:52:16 +00:00
2022-10-18 16:37:15 +00:00
# this section determines follow/like/friend status for each search result
# so that action links can be activated or not
rel = User . select ( " users.id AS uid " )
rel = rel . joins ( " LEFT JOIN follows ON follows.user_id = ' #{ user . id } ' " )
rel = rel . where ( [ " users.id IN ( #{ mids } ) AND follows.followable_id = users.id " ] )
rel . all . each { | val | @user_counters [ val . uid ] << RESULT_FOLLOW }
rel = User . select ( " users.id AS uid " )
rel = rel . joins ( " LEFT JOIN friendships AS friends ON friends.friend_id = ' #{ user . id } ' " )
rel = rel . where ( [ " users.id IN ( #{ mids } ) AND friends.user_id = users.id " ] )
rel . all . each { | val | @user_counters [ val . uid ] << RESULT_FRIEND }
end
2015-04-02 14:52:16 +00:00
self
end
private
def _count ( musician , key )
if mm = @user_counters [ musician . id ]
return mm . detect { | ii | ii . is_a? ( Hash ) } [ key ]
end if @user_counters
0
end
public
def follow_count ( musician )
_count ( musician , COUNT_FOLLOW )
end
def friend_count ( musician )
_count ( musician , COUNT_FRIEND )
end
def record_count ( musician )
_count ( musician , COUNT_RECORD )
end
def session_count ( musician )
_count ( musician , COUNT_SESSION )
end
def is_friend? ( musician )
if mm = @user_counters [ musician . id ]
return mm . include? ( RESULT_FRIEND )
end if @user_counters
false
end
def is_follower? ( musician )
if mm = @user_counters [ musician . id ]
return mm . include? ( RESULT_FOLLOW )
end if @user_counters
false
end
def search_type
self . class . to_s
2015-03-02 09:41:53 +00:00
end
2015-04-04 14:55:41 +00:00
def is_blank?
2015-05-18 04:00:12 +00:00
self . data_blob == self . class . json_schema
2015-04-04 14:55:41 +00:00
end
def description
if self . is_blank?
return 'Click search button to look for musicians with similar interests, skill levels, etc.'
end
jj = self . json
2015-07-15 15:04:45 +00:00
str = ''
if 0 < ( val = jj [ KEY_INSTRUMENTS ] ) . length
str += " , Instruments = "
instr_ids = val . collect { | stored_instrument | stored_instrument [ 'id' ] }
instrs = Instrument . where ( [ " id IN (?) " , instr_ids ] ) . order ( :description )
instrs . each_with_index do | ii , idx |
proficiency = val . detect { | stored_instrument | stored_instrument [ 'id' ] == ii . id } [ 'level' ]
str += " #{ ii . description } / #{ INSTRUMENT_PROFICIENCY [ proficiency . to_i ] } "
str += ', ' unless idx == ( instrs . length - 1 )
end
end
2015-04-04 14:55:41 +00:00
if ( val = jj [ KEY_INTERESTS ] ) != INTEREST_VALS [ 0 ]
2015-07-15 15:04:45 +00:00
str += " , Interest = #{ INTERESTS [ val ] } "
2015-04-04 14:55:41 +00:00
end
if ( val = jj [ KEY_SKILL ] . to_i ) != SKILL_VALS [ 0 ]
2015-07-15 15:04:45 +00:00
str += " , Skill = #{ SKILL_LEVELS [ val ] } "
2015-04-04 14:55:41 +00:00
end
if ( val = jj [ KEY_STUDIOS ] . to_i ) != STUDIO_COUNTS [ 0 ]
2015-07-15 15:04:45 +00:00
str += " , Studio Sessions = #{ STUDIOS_LABELS [ val ] } "
2015-04-04 14:55:41 +00:00
end
if ( val = jj [ KEY_GIGS ] . to_i ) != GIG_COUNTS [ 0 ]
2015-07-15 15:04:45 +00:00
str += " , Concert Gigs = #{ GIG_LABELS [ val ] } "
2015-04-04 14:55:41 +00:00
end
2015-04-04 20:26:41 +00:00
val = jj [ KEY_AGES ] . map ( & :to_i )
2015-04-04 14:55:41 +00:00
val . sort!
2015-04-04 20:26:41 +00:00
if ! val . blank?
2015-07-15 15:04:45 +00:00
str += " , Ages = "
2015-04-04 14:55:41 +00:00
val . each_with_index do | vv , idx |
str += " #{ AGES [ vv ] } "
str += ', ' unless idx == ( val . length - 1 )
end
end
if 0 < ( val = jj [ KEY_GENRES ] ) . length
2015-07-15 15:04:45 +00:00
str += " , Genres = "
2015-04-04 14:55:41 +00:00
genres = Genre . where ( [ " id IN (?) " , val ] ) . order ( 'description' ) . pluck ( :description )
genres . each_with_index do | gg , idx |
str += " #{ gg } "
str += ', ' unless idx == ( genres . length - 1 )
end
end
2015-07-15 15:04:45 +00:00
str += " , Sort = #{ SORT_ORDERS [ json_value ( MusicianSearch :: KEY_SORT_ORDER ) ] } "
if str . start_with? ( ', ' )
# trim off any leading ,
str = str [ 2 .. - 1 ]
2015-04-04 14:55:41 +00:00
end
2015-07-15 15:04:45 +00:00
str = 'Current Search: ' + str
2015-04-04 14:55:41 +00:00
str
end
2015-02-23 06:21:36 +00:00
end
end