Changeset 3653
- Timestamp:
- 02/25/06 23:41:51 (3 years ago)
- Files:
-
- trunk/activerecord/lib/active_record/calculations.rb (modified) (4 diffs)
- trunk/activerecord/test/calculations_test.rb (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activerecord/lib/active_record/calculations.rb
r3646 r3653 116 116 # Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name) # Selects the minimum age for any family without any minors 117 117 def calculate(operation, column_name, options = {}) 118 column_name = '*' if column_name == :all 119 column = columns.detect { |c| c.name.to_s == column_name.to_s } 118 column_name = '*' if column_name == :all 119 column = columns.detect { |c| c.name.to_s == column_name.to_s } 120 aggregate = select_aggregate(operation, column_name, options) 121 aggregate_alias = column_alias_for(operation, column_name) 120 122 if options[:group] 121 execute_grouped_calculation(operation, column_name, column, options)123 execute_grouped_calculation(operation, column_name, column, aggregate, aggregate_alias, options) 122 124 else 123 execute_simple_calculation(operation, column_name, column, options)125 execute_simple_calculation(operation, column_name, column, aggregate, aggregate_alias, options) 124 126 end 125 127 end 126 128 127 129 protected 128 def construct_calculation_sql( operation, column_name, options)129 sql = ["SELECT #{ operation}(#{'DISTINCT ' if options[:distinct]}#{column_name})"]130 sql << ", #{options[:group_field]} " if options[:group]130 def construct_calculation_sql(aggregate, aggregate_alias, options) 131 sql = ["SELECT #{aggregate} AS #{aggregate_alias}"] 132 sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group] 131 133 sql << " FROM #{table_name} " 132 134 add_joins!(sql, options) … … 137 139 end 138 140 139 def execute_simple_calculation(operation, column_name, column, options)140 value = connection.select_value(construct_calculation_sql(operation, column_name, options))141 def execute_simple_calculation(operation, column_name, column, aggregate, aggregate_alias, options) 142 value = connection.select_value(construct_calculation_sql(aggregate, aggregate_alias, options)) 141 143 type_cast_calculated_value(value, column, operation) 142 144 end 143 145 144 def execute_grouped_calculation(operation, column_name, column, options)146 def execute_grouped_calculation(operation, column_name, column, aggregate, aggregate_alias, options) 145 147 group_attr = options[:group].to_s 146 148 association = reflect_on_association(group_attr.to_sym) 147 149 associated = association && association.macro == :belongs_to # only count belongs_to associations 148 150 group_field = (associated ? "#{options[:group]}_id" : options[:group]).to_s 149 sql = construct_calculation_sql(operation, column_name, options.merge(:group_field => group_field)) 151 group_alias = column_alias_for(group_field) 152 sql = construct_calculation_sql(aggregate, aggregate_alias, options.merge(:group_field => group_field, :group_alias => group_alias)) 150 153 calculated_data = connection.select_all(sql) 151 154 152 155 if association 153 key_ids = calculated_data.collect { |row| row[group_ field] }156 key_ids = calculated_data.collect { |row| row[group_alias] } 154 157 key_records = ActiveRecord::Base.send(:class_of_active_record_descendant, association.klass).find(key_ids) 155 158 key_records = key_records.inject({}) { |hsh, r| hsh.merge(r.id => r) } … … 157 160 158 161 calculated_data.inject(OrderedHash.new) do |all, row| 159 key = associated ? key_records[row[group_ field].to_i] : row[column_key(group_field)]160 value = row[ column_key("#{operation}(#{column_name})")]162 key = associated ? key_records[row[group_alias].to_i] : row[group_alias] 163 value = row[aggregate_alias] 161 164 all << [key, type_cast_calculated_value(value, column, operation)] 162 165 end … … 164 167 165 168 private 169 def select_aggregate(operation, column_name, options) 170 "#{operation}(#{'DISTINCT ' if options[:distinct]}#{column_name})" 171 end 172 166 173 # converts a given key to the value that the database adapter returns as 167 174 # 168 # users.id #=> id 169 # sum(id) #=> sum(id) 170 # 171 # psql strips off the () function too 172 # 173 # sum(id) #=> sum 174 # 175 # Should this go in a DB Adapter? 176 def column_key(key) 177 return key.split('.').last unless key =~ /\(/ # split off table alias 178 case connection 179 when ActiveRecord::ConnectionAdapters::PostgreSQLAdapter 180 key.split('(').first.split('.').last 181 else 182 sql_func, sql_args = key.split('(') 183 "#{sql_func.split('.').last}(#{sql_args}" 184 end 175 # users.id #=> users_id 176 # sum(id) #=> sum_id 177 # count(distinct users.id) #=> count_distinct_users_id 178 # count(*) #=> count_all 179 def column_alias_for(*keys) 180 keys.join(' ').downcase.gsub(/\*/, 'all').gsub(/\W+/, ' ').strip.gsub(/ +/, '_') 185 181 end 186 182 trunk/activerecord/test/calculations_test.rb
r3646 r3653 109 109 110 110 def test_should_calculate_grouped_by_function_with_table_alias 111 c = Topic.count(:all, :group => 'DATE(topics.written_on)') 112 assert_equal 1, c["2003-07-15"] 113 assert_equal 1, c["2003-07-16"] 111 c = Company.count(:all, :group => 'UPPER(companies.type)') 112 assert_equal 2, c[nil] 113 assert_equal 1, c['DEPENDENTFIRM'] 114 assert_equal 3, c['CLIENT'] 115 assert_equal 2, c['FIRM'] 114 116 end 115 117