lib/active_median/model.rb in active_median-0.2.3 vs lib/active_median/model.rb in active_median-0.2.4

- old
+ new

@@ -1,8 +1,18 @@ module ActiveMedian module Model def median(column) + percentile(column, 0.5) + end + + def percentile(column, percentile) + percentile = percentile.to_f + raise ArgumentError, "percentile is not between 0 and 1" if percentile < 0 || percentile > 1 + + # prevent SQL injection + percentile = connection.quote(percentile) + group_values = all.group_values relation = case connection.adapter_name when /mysql/i @@ -13,24 +23,34 @@ if mariadb if group_values.any? over = "PARTITION BY #{group_values.join(", ")}" end - select(*group_values, "PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY #{column}) OVER (#{over})").unscope(:group) + select(*group_values, "PERCENTILE_CONT(#{percentile}) WITHIN GROUP (ORDER BY #{column}) OVER (#{over})").unscope(:group) else # if mysql gets native function, check (and memoize) version first - select(*group_values, "PERCENTILE_CONT(#{column}, 0.50)") + select(*group_values, "PERCENTILE_CONT(#{column}, #{percentile})") end when /sqlserver/i if group_values.any? over = "PARTITION BY #{group_values.join(", ")}" end - select(*group_values, "PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY #{column}) OVER (#{over})").unscope(:group) + select(*group_values, "PERCENTILE_CONT(#{percentile}) WITHIN GROUP (ORDER BY #{column}) OVER (#{over})").unscope(:group) when /sqlite/i - select(*group_values, "MEDIAN(#{column})") + case percentile.to_f + when 0 + select(*group_values, "MIN(#{column})") + when 0.5 + select(*group_values, "MEDIAN(#{column})") + when 1 + select(*group_values, "MAX(#{column})") + else + # LOWER_QUARTILE and UPPER_QUARTILE use different calculation than 0.25 and 0.75 + raise "SQLite only supports 0, 0.5, and 1 percentiles" + end when /postg/i, /redshift/i # postgis too - select(*group_values, "PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY #{column})") + select(*group_values, "PERCENTILE_CONT(#{percentile}) WITHIN GROUP (ORDER BY #{column})") else raise "Connection adapter not supported: #{connection.adapter_name}" end result = connection.select_all(relation.to_sql)