module Randumb::ActiveRecord::Relation
Public Instance Methods
order_by_rand(opts = {})
click to toggle source
# File lib/randumb/relation.rb, line 66 def order_by_rand(opts = {}) if opts.is_a?(Hash) build_order_scope(opts) else raise ArgumentError.new( "order_by_rand() expects a hash of options. If you need to limit "\ "results simply add a limit to your scope ex: Artist.order_by_rand.limit(1)" ) end end
order_by_rand_weighted(ranking_column, opts={})
click to toggle source
# File lib/randumb/relation.rb, line 77 def order_by_rand_weighted(ranking_column, opts={}) raise_unless_valid_ranking_column(ranking_column) is_randumb_postges_case?(self, ranking_column) build_order_scope(opts, ranking_column) end
random(max_items = nil, opts={})
click to toggle source
If the max_items argument is omitted, one random entity will be returned. If you provide the integer argument, you will get back an array of records.
# File lib/randumb/relation.rb, line 12 def random(max_items = nil, opts={}) ActiveSupport::Deprecation.warn "The random() method will be depricated in randumb 1.0 in favor of the order_by_rand scope." relation = clone return random_by_id_shuffle(max_items, opts) if is_randumb_postges_case?(relation) scope = relation.order_by_rand(opts) scope = scope.limit(max_items) if override_limit?(max_items, relation) # return first record if method was called without parameters max_items ? scope.to_a : scope.first end
random_by_id_shuffle(max_items = nil, opts={})
click to toggle source
This was my first implementation, adding it as an option for people to use and to fall back on for pesky DB one off situations…
https://github.com/spilliton/randumb/issues/7
# File lib/randumb/relation.rb, line 45 def random_by_id_shuffle(max_items = nil, opts={}) return_first_record = max_items.nil? # see return switch at end max_items ||= 1 relation = clone ids = fetch_random_ids(relation, max_items, opts) # build new scope for final query the_scope = klass.includes(includes_values) # specifying empty selects caused bug in rails 3.0.0/3.0.1 the_scope = the_scope.select(select_values) unless select_values.empty? # get the records and shuffle since the order of the ids # passed to where() isn't retained in the result set rng = random_number_generator(opts) records = the_scope.where(:id => ids).to_a.shuffle!(:random => rng) # return first record if method was called without parameters return_first_record ? records.first : records end
random_weighted(ranking_column, max_items = nil, opts={})
click to toggle source
If ranking_column is provided, that named column wil be multiplied by a random number to determine probability of order. The ranking column must be numeric.
# File lib/randumb/relation.rb, line 26 def random_weighted(ranking_column, max_items = nil, opts={}) ActiveSupport::Deprecation.warn "The random_weighted() method will be depricated in randumb 1.0 in favor of the order_by_rand_weighted scope." relation = clone return random_by_id_shuffle(max_items, opts) if is_randumb_postges_case?(relation, ranking_column) raise_unless_valid_ranking_column(ranking_column) scope = relation.order_by_rand_weighted(ranking_column, opts) # override the limit if they are requesting multiple records scope = scope.limit(max_items) if override_limit?(max_items, relation) # return first record if method was called without parameters max_items ? scope.to_a : scope.first end
Private Instance Methods
build_order_scope(options, ranking_column=nil)
click to toggle source
# File lib/randumb/relation.rb, line 85 def build_order_scope(options, ranking_column=nil) opts = options.reverse_merge(connection: connection, table_name: table_name) order_clause = if ranking_column Randumb::Syntax.random_weighted_order_clause(ranking_column, opts) else Randumb::Syntax.random_order_clause(opts) end # keep prior orders and append random all_orders = (arel.orders + [order_clause]) # override all previous orders reorder(all_orders) end
fetch_random_ids(relation, max_ids, opts = {})
click to toggle source
Returns all matching ids from the db, shuffles them, then returns an array containing at most max_ids
# File lib/randumb/relation.rb, line 123 def fetch_random_ids(relation, max_ids, opts = {}) # clear these for our id only query relation.select_values = [] relation.includes_values = [] # do original query but only for id field id_only_relation = relation.select("#{table_name}.id") id_results = connection.select_all(id_only_relation.to_sql) rng = random_number_generator(opts) if max_ids == 1 && id_results.count > 0 rand_index = rng.rand(id_results.count) [id_results[rand_index]["id"]] else # ActiveRecord 4 requires .to_a arr = id_results.respond_to?(:to_a) ? id_results.to_a : id_results arr.shuffle!(random: rng)[0, max_ids].collect! { |h| h["id"] } end end
is_randumb_postges_case?(relation, ranking_column=nil)
click to toggle source
postgres won't let you do an order_by when also doing a distinct let's just use the in-memory option in this case
# File lib/randumb/relation.rb, line 102 def is_randumb_postges_case?(relation, ranking_column=nil) if relation.respond_to?(:uniq_value) && relation.uniq_value && connection.adapter_name =~ /(postgres|postgis)/i if ranking_column raise Exception, "order_by_rand_weighted: not possible when using .uniq and the postgres/postgis db adapter" else return true end end end
override_limit?(max_items, relation)
click to toggle source
# File lib/randumb/relation.rb, line 152 def override_limit?(max_items, relation) max_items && (!relation.limit_value || relation.limit_value > max_items) end
raise_unless_valid_ranking_column(ranking_column)
click to toggle source
columns used for ranking must be a numeric type b/c they are multiplied
# File lib/randumb/relation.rb, line 113 def raise_unless_valid_ranking_column(ranking_column) if ranking_column column_data = @klass.columns_hash[ranking_column.to_s] raise ArgumentError.new("random_weighted: #{ranking_column} is not a column on #{@klass.table_name}!") unless column_data raise ArgumentError.new("random_weighted: #{ranking_column} is not a numeric column on #{@klass.table_name}!") unless [:integer, :float].include?(column_data.type) end end
random_number_generator(opts={})
click to toggle source
# File lib/randumb/relation.rb, line 144 def random_number_generator(opts={}) if seed = opts[:seed] Random.new(seed) else Random.new end end