jam-cloud/ruby/lib/jam_ruby/models/search.rb

318 lines
11 KiB
Ruby
Raw Normal View History

2012-11-08 01:13:35 +00:00
module JamRuby
# not a active_record model; just a search result
class Search
attr_accessor :bands, :musicians, :fans, :recordings, :friends, :search_type
LIMIT = 10
2013-10-28 14:22:06 +00:00
# performs a site-white search
2013-06-22 23:35:42 +00:00
def self.search(query, user_id = nil)
2012-11-08 01:13:35 +00:00
users = User.search(query, :limit => LIMIT)
bands = Band.search(query, :limit => LIMIT)
2013-04-25 06:50:52 +00:00
# NOTE: I removed recordings from search here. This is because we switched
# to "claimed_recordings" so it's not clear what should be searched.
2012-11-08 01:13:35 +00:00
friends = Friendship.search(query, user_id, :limit => LIMIT)
2013-06-22 23:35:42 +00:00
return Search.new(users + bands + friends)
end
2013-06-22 23:35:42 +00:00
# performs a friend search scoped to a specific user
# def self.search_by_user(query, user_id)
# friends = Friendship.search(query, user_id, :limit => LIMIT)
# return Search.new(friends)
# end
2012-11-08 01:13:35 +00:00
# search_results - results from a Tire search across band/user/recording
def initialize(search_results=nil)
2012-11-08 01:13:35 +00:00
@bands = []
@musicians = []
@fans = []
@recordings = []
@friends = []
2012-11-08 01:13:35 +00:00
if search_results.nil?
return
end
search_results.take(LIMIT).each do |result|
2012-11-08 01:13:35 +00:00
if result.class == User
if result.musician
@musicians.push(result)
@search_type = PARAM_MUSICIAN
2012-11-08 01:13:35 +00:00
else
@fans.push(result)
end
elsif result.class == Band
2012-11-08 01:13:35 +00:00
@bands.push(result)
elsif result.class == Recording
@recordings.push(result)
elsif result.class == Friendship
@friends.push(result.friend)
2012-11-08 01:13:35 +00:00
else
raise Exception, "unknown class #{result.class} returned in search results"
end
2012-11-08 01:13:35 +00:00
end
end
def self.create_tsquery(query)
# empty queries don't hit back to elasticsearch
if query.nil? || query.length == 0
return nil
end
search_terms = query.split
if search_terms.length == 0
return nil
end
args = nil
search_terms.each do |search_term|
if args == nil
args = search_term
else
args = args + " & " + search_term
end
end
args = args + ":*"
return args
end
2013-11-06 13:50:34 +00:00
attr_accessor :user_counters, :page_num, :page_count
PARAM_MUSICIAN = :srch_m
PARAM_BAND = :srch_b
2013-11-06 13:50:34 +00:00
B_PER_PAGE = M_PER_PAGE = 10
M_MILES_DEFAULT = 500
B_MILES_DEFAULT = 0
2013-11-06 13:50:34 +00:00
M_ORDER_FOLLOWS = ['Most Followed', :followed]
M_ORDER_PLAYS = ['Most Plays', :plays]
M_ORDER_PLAYING = ['Playing Now', :playing]
ORDERINGS = B_ORDERINGS = M_ORDERINGS = [M_ORDER_FOLLOWS, M_ORDER_PLAYS, M_ORDER_PLAYING]
B_ORDERING_KEYS = M_ORDERING_KEYS = M_ORDERINGS.collect { |oo| oo[1] }
DISTANCE_OPTS = B_DISTANCE_OPTS = M_DISTANCE_OPTS = [['Any', 0], [1000.to_s, 1000], [500.to_s, 500], [250.to_s, 250], [100.to_s, 100], [50.to_s, 50], [25.to_s, 25]]
2013-11-06 13:50:34 +00:00
def self.order_param(params, keys=M_ORDERING_KEYS)
2013-11-06 13:50:34 +00:00
ordering = params[:orderby]
ordering.blank? ? keys[0] : keys.detect { |oo| oo.to_s == ordering }
2013-11-06 13:50:34 +00:00
end
def self.musician_search(params={}, current_user=nil)
2013-11-25 21:56:54 +00:00
rel = User.musicians
2013-11-06 13:50:34 +00:00
unless (instrument = params[:instrument]).blank?
rel = rel.joins("RIGHT JOIN musicians_instruments AS minst ON minst.user_id = users.id")
.where(['minst.instrument_id = ? AND users.id IS NOT NULL', instrument])
end
rel = MaxMindGeo.where_latlng(rel, params, current_user)
2013-11-06 13:50:34 +00:00
sel_str = 'users.*'
case ordering = self.order_param(params)
2013-11-25 21:56:54 +00:00
when :plays # FIXME: double counting?
sel_str = "COUNT(records)+COUNT(sessions) AS play_count, #{sel_str}"
rel = rel.joins("LEFT JOIN music_sessions AS sessions ON sessions.user_id = users.id")
.joins("LEFT JOIN recordings AS records ON records.owner_id = users.id")
.group("users.id")
.order("play_count DESC, users.created_at DESC")
2013-11-06 13:50:34 +00:00
when :followed
sel_str = "COUNT(follows) AS search_follow_count, #{sel_str}"
rel = rel.joins("LEFT JOIN users_followers AS follows ON follows.user_id = users.id")
.group("users.id")
.order("COUNT(follows) DESC, users.created_at DESC")
2013-11-06 13:50:34 +00:00
when :playing
rel = rel.joins("LEFT JOIN connections ON connections.user_id = users.id")
.where(['connections.music_session_id IS NOT NULL AND connections.aasm_state != ?',
'expired'])
.order("users.created_at DESC")
2013-11-06 13:50:34 +00:00
end
rel = rel.select(sel_str)
perpage = [(params[:per_page] || M_PER_PAGE).to_i, 100].min
2013-11-06 13:50:34 +00:00
page = [params[:page].to_i, 1].max
rel = rel.paginate(:page => page, :per_page => perpage)
rel = rel.includes([:instruments, :followings, :friends])
2013-11-06 13:50:34 +00:00
objs = rel.all
2013-11-06 13:50:34 +00:00
srch = Search.new
srch.page_num, srch.page_count = page, objs.total_pages
srch.musician_results_for_user(objs, current_user)
2013-11-06 13:50:34 +00:00
end
RESULT_FOLLOW = :follows
RESULT_FRIEND = :friends
2013-11-06 13:50:34 +00:00
COUNT_FRIEND = :count_friend
COUNT_FOLLOW = :count_follow
COUNT_RECORD = :count_record
COUNT_SESSION = :count_session
COUNTERS = [COUNT_FRIEND, COUNT_FOLLOW, COUNT_RECORD, COUNT_SESSION]
def musician_results_for_user(results, user)
@search_type, @musicians = PARAM_MUSICIAN, results
if user
2013-11-06 13:50:34 +00:00
@user_counters = results.inject({}) { |hh,val| hh[val.id] = []; hh }
mids = "'#{@musicians.map(&:id).join("','")}'"
# this gets counts for each search result on friends/follows/records/sessions
2013-11-06 13:50:34 +00:00
results.each do |uu|
counters = { }
counters[COUNT_FRIEND] = Friendship.where(:user_id => uu.id).count
counters[COUNT_FOLLOW] = UserFollowing.where(:user_id => uu.id).count
counters[COUNT_RECORD] = ClaimedRecording.where(:user_id => uu.id).count
2013-11-06 13:50:34 +00:00
counters[COUNT_SESSION] = MusicSession.where(:user_id => uu.id).count
@user_counters[uu.id] << counters
end
# 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 users_followers AS follows ON follows.follower_id = '#{user.id}'")
rel = rel.where(["users.id IN (#{mids}) AND follows.user_id = users.id"])
2013-11-06 13:50:34 +00:00
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"])
2013-11-06 13:50:34 +00:00
rel.all.each { |val| @user_counters[val.uid] << RESULT_FRIEND }
else
2013-11-06 13:50:34 +00:00
@user_counters = {}
end
self
end
2013-11-25 21:56:54 +00:00
private
2013-11-06 13:50:34 +00:00
def _count(musician, key)
if mm = @user_counters[musician.id]
return mm.detect { |ii| ii.is_a?(Hash) }[key]
end if @user_counters
2013-11-06 13:50:34 +00:00
0
end
2013-11-25 21:56:54 +00:00
public
2013-11-06 13:50:34 +00:00
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)
2013-11-06 13:50:34 +00:00
if mm = @user_counters[musician.id]
return mm.include?(RESULT_FRIEND)
end if @user_counters
false
end
def is_follower?(musician)
2013-11-06 13:50:34 +00:00
if mm = @user_counters[musician.id]
return mm.include?(RESULT_FOLLOW)
end if @user_counters
false
end
def self.new_musicians(usr, since_date=Time.now - 1.week, max_count=50, radius=M_MILES_DEFAULT)
rel = User.musicians
.where(['created_at >= ? AND users.id != ?', since_date, usr.id])
.within(radius, :origin => [usr.lat, usr.lng])
.order('created_at DESC')
.limit(max_count)
objs = rel.all.to_a
if block_given?
yield(objs) if 0 < objs.count
else
return objs
end
end
def self.band_search(params={}, current_user=nil)
rel = Band.scoped
# rel = Arel::Table.new(:bands)
unless (genre = params[:genre]).blank?
rel = Band.joins("RIGHT JOIN band_genres AS bgenres ON bgenres.band_id = bands.id")
.where(['bgenres.genre_id = ? AND bands.id IS NOT NULL', genre])
end
rel = MaxMindGeo.where_latlng(rel, params, current_user)
sel_str = 'bands.*'
case ordering = self.order_param(params)
when :plays # FIXME: double counting?
sel_str = "COUNT(records)+COUNT(sessions) AS play_count, #{sel_str}"
rel = rel.joins("LEFT JOIN music_sessions AS sessions ON sessions.user_id = users.id")
.joins("LEFT JOIN recordings AS records ON records.owner_id = users.id")
.group("users.id")
.order("play_count DESC, users.created_at DESC")
when :followed
sel_str = "COUNT(follows) AS search_follow_count, #{sel_str}"
rel = rel.joins("LEFT JOIN bands_followers AS follows ON follows.band_id = bands.id")
.group("bands.id")
.order("COUNT(follows) DESC, bands.created_at DESC")
when :playing
rel = rel.joins("LEFT JOIN connections ON connections.user_id = users.id")
.where(['connections.music_session_id IS NOT NULL AND connections.aasm_state != ?',
'expired'])
.order("users.created_at DESC")
end
rel = rel.select(sel_str)
perpage = [(params[:per_page] || M_PER_PAGE).to_i, 100].min
page = [params[:page].to_i, 1].max
rel = rel.paginate(:page => page, :per_page => perpage)
rel = rel.includes([:users])
objs = rel.all
srch = Search.new
srch.page_num, srch.page_count = page, objs.total_pages
srch.band_results_for_user(objs, current_user)
end
def band_results_for_user(results, user)
@search_type, @bands = PARAM_BAND, results
if user
@user_counters = results.inject({}) { |hh,val| hh[val.id] = []; hh }
mids = "'#{@bands.map(&:id).join("','")}'"
# this gets counts for each search result
results.each do |bb|
counters = { }
counters[COUNT_FOLLOW] = BandFollowing.where(:band_id => bb.id).count
# counters[COUNT_RECORD] = ClaimedRecording.where(:band_id => bb.id).count
# counters[COUNT_SESSION] = MusicSession.where(:band_id => bb.id).count
@user_counters[bb.id] << counters
end
# this section determines follow/like/friend status for each search result
# so that action links can be activated or not
rel = Band.select("bands.id AS bid")
rel = rel.joins("LEFT JOIN bands_followers AS follows ON follows.follower_id = '#{user.id}'")
rel = rel.where(["bands.id IN (#{mids}) AND follows.band_id = bands.id"])
rel.all.each { |val| @user_counters[val.bid] << RESULT_FOLLOW }
else
@user_counters = {}
end
self
end
2012-11-08 01:13:35 +00:00
end
2013-04-25 06:50:52 +00:00
end