Commit e532eb9bdb651d881c8f92dacf177cbc2a737e20

Authored by Luke Baker
1 parent 6d3412e9

refactor random prompt picking

when using catchup and no prompt found, use random prompt
Showing 1 changed file with 34 additions and 28 deletions   Show diff stats
app/models/question.rb
@@ -57,7 +57,7 @@ class Question < ActiveRecord::Base @@ -57,7 +57,7 @@ class Question < ActiveRecord::Base
57 next_prompt = self.pop_prompt_queue 57 next_prompt = self.pop_prompt_queue
58 if next_prompt.nil? 58 if next_prompt.nil?
59 logger.info("DEBUG Catchup prompt cache miss! Nothing in prompt_queue") 59 logger.info("DEBUG Catchup prompt cache miss! Nothing in prompt_queue")
60 - next_prompt = self.catchup_choose_prompt 60 + next_prompt = self.picked_prompt
61 record_prompt_cache_miss 61 record_prompt_cache_miss
62 else 62 else
63 record_prompt_cache_hit 63 record_prompt_cache_hit
@@ -66,24 +66,18 @@ class Question < ActiveRecord::Base @@ -66,24 +66,18 @@ class Question < ActiveRecord::Base
66 return next_prompt 66 return next_prompt
67 else 67 else
68 #Standard choose prompt at random 68 #Standard choose prompt at random
69 - next_prompt = self.picked_prompt  
70 - return next_prompt 69 + return self.picked_prompt
71 end 70 end
72 71
73 end 72 end
74 73
75 - #TODO: generalize for prompts of rank > 2  
76 - #TODO: add index for rapid finding  
77 - def picked_prompt(rank = 2)  
78 - logger.info "inside Question#picked_prompt"  
79 - raise NotImplementedError.new("Sorry, we currently only support pairwise prompts. Rank of the prompt must be 2.") unless rank == 2  
80 - begin  
81 - choice_id_array = distinct_array_of_choice_ids(rank)  
82 - @p = prompts.find_or_create_by_left_choice_id_and_right_choice_id(choice_id_array[0], choice_id_array[1], :include => [:left_choice ,:right_choice ])  
83 - logger.info "#{@p.inspect} is active? #{@p.active?}"  
84 - end until @p.active?  
85 - return @p  
86 - end 74 + #TODO: generalize for prompts of rank > 2
  75 + def picked_prompt(rank = 2)
  76 + logger.info "inside Question#picked_prompt"
  77 + raise NotImplementedError.new("Sorry, we currently only support pairwise prompts. Rank of the prompt must be 2.") unless rank == 2
  78 + choice_id_array = distinct_array_of_choice_ids(rank, true)
  79 + prompts.find_or_create_by_left_choice_id_and_right_choice_id(choice_id_array[0], choice_id_array[1], :include => [:left_choice ,:right_choice ])
  80 + end
87 81
88 # adapted from ruby cookbook(2006): section 5-11 82 # adapted from ruby cookbook(2006): section 5-11
89 def catchup_choose_prompt 83 def catchup_choose_prompt
@@ -118,6 +112,11 @@ class Question < ActiveRecord::Base @@ -118,6 +112,11 @@ class Question < ActiveRecord::Base
118 active_choices = choices.active 112 active_choices = choices.active
119 active_choice_ids = active_choices.map {|c| c.id} 113 active_choice_ids = active_choices.map {|c| c.id}
120 sql = "SELECT votes_count, left_choice_id, right_choice_id FROM prompts WHERE question_id = #{self.id} AND left_choice_id IN (#{active_choice_ids.join(',')}) AND right_choice_id IN (#{active_choice_ids.join(',')})" 114 sql = "SELECT votes_count, left_choice_id, right_choice_id FROM prompts WHERE question_id = #{self.id} AND left_choice_id IN (#{active_choice_ids.join(',')}) AND right_choice_id IN (#{active_choice_ids.join(',')})"
  115 + # Warning: lots of memory possibly used here
  116 + # We don't want to use Rails find_each or find_in_batches because
  117 + # it is too slow for close to a million rows. We don't need ActiveRecord
  118 + # objects here. It may be a good idea to update this to grab these in
  119 + # batches.
121 ActiveRecord::Base.connection.select_all(sql).each do |p| 120 ActiveRecord::Base.connection.select_all(sql).each do |p|
122 value = [(1.0/ (p['votes_count'].to_i + 1).to_f).to_f, throttle_min].min 121 value = [(1.0/ (p['votes_count'].to_i + 1).to_f).to_f, throttle_min].min
123 weights[p['left_choice_id']+", "+p['right_choice_id']] = value 122 weights[p['left_choice_id']+", "+p['right_choice_id']] = value
@@ -335,19 +334,26 @@ class Question < ActiveRecord::Base @@ -335,19 +334,26 @@ class Question < ActiveRecord::Base
335 end 334 end
336 335
337 336
338 - def distinct_array_of_choice_ids(rank = 2, only_active = true)  
339 - @choice_ids = choice_ids  
340 - @s = @choice_ids.size  
341 - begin  
342 - index_list = (0...@s).sort_by{rand}  
343 - first_one, second_one = index_list.first, index_list.second  
344 - @the_choice_ids = @choice_ids.values_at(first_one, second_one)  
345 - # @the_choice_ids << choices.active.first(:order => 'RAND()', :select => 'id').id  
346 - # @the_choice_ids << choices.active.last(:order => 'RAND()', :select => 'id').id  
347 - end until (@the_choice_ids.size == rank)  
348 - logger.info "List populated and looks like #{@the_choice_ids.inspect}"  
349 - return @the_choice_ids.to_a  
350 - end 337 + def distinct_array_of_choice_ids(rank = 2, only_active = true)
  338 + count = (only_active) ? choices.active.count : choices.count
  339 +
  340 + found_choices = []
  341 + rank.times do
  342 + # select only active choices?
  343 + conditions = (only_active) ? ['active = ?', true] : ['']
  344 + # if we've already found some, make sure we don't find them again
  345 + if found_choices.count > 0
  346 + conditions[0] += ' AND id NOT IN (?)'
  347 + conditions.push found_choices
  348 + end
  349 +
  350 + found_choices.push choices.find(:first,
  351 + :select => 'id',
  352 + :conditions => conditions,
  353 + :offset => rand(count - found_choices.count)).id
  354 + end
  355 + return found_choices
  356 + end
351 357
352 def picked_prompt_id 358 def picked_prompt_id
353 picked_prompt.id 359 picked_prompt.id