PageRenderTime 59ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/activerecord/lib/active_record/relation/calculations.rb

http://github.com/rails/rails
Ruby | 450 lines | 244 code | 54 blank | 152 comment | 62 complexity | e621d3b0221857d20dca3213f62e6608 MD5 | raw file
  1. # frozen_string_literal: true
  2. require "active_support/core_ext/enumerable"
  3. module ActiveRecord
  4. module Calculations
  5. # Count the records.
  6. #
  7. # Person.count
  8. # # => the total count of all people
  9. #
  10. # Person.count(:age)
  11. # # => returns the total count of all people whose age is present in database
  12. #
  13. # Person.count(:all)
  14. # # => performs a COUNT(*) (:all is an alias for '*')
  15. #
  16. # Person.distinct.count(:age)
  17. # # => counts the number of different age values
  18. #
  19. # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group],
  20. # it returns a Hash whose keys represent the aggregated column,
  21. # and the values are the respective amounts:
  22. #
  23. # Person.group(:city).count
  24. # # => { 'Rome' => 5, 'Paris' => 3 }
  25. #
  26. # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
  27. # keys are an array containing the individual values of each column and the value
  28. # of each key would be the #count.
  29. #
  30. # Article.group(:status, :category).count
  31. # # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
  32. # ["published", "business"]=>0, ["published", "technology"]=>2}
  33. #
  34. # If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
  35. #
  36. # Person.select(:age).count
  37. # # => counts the number of different age values
  38. #
  39. # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
  40. # between databases. In invalid cases, an error from the database is thrown.
  41. def count(column_name = nil)
  42. if block_given?
  43. unless column_name.nil?
  44. raise ArgumentError, "Column name argument is not supported when a block is passed."
  45. end
  46. super()
  47. else
  48. calculate(:count, column_name)
  49. end
  50. end
  51. # Calculates the average value on a given column. Returns +nil+ if there's
  52. # no row. See #calculate for examples with options.
  53. #
  54. # Person.average(:age) # => 35.8
  55. def average(column_name)
  56. calculate(:average, column_name)
  57. end
  58. # Calculates the minimum value on a given column. The value is returned
  59. # with the same data type of the column, or +nil+ if there's no row. See
  60. # #calculate for examples with options.
  61. #
  62. # Person.minimum(:age) # => 7
  63. def minimum(column_name)
  64. calculate(:minimum, column_name)
  65. end
  66. # Calculates the maximum value on a given column. The value is returned
  67. # with the same data type of the column, or +nil+ if there's no row. See
  68. # #calculate for examples with options.
  69. #
  70. # Person.maximum(:age) # => 93
  71. def maximum(column_name)
  72. calculate(:maximum, column_name)
  73. end
  74. # Calculates the sum of values on a given column. The value is returned
  75. # with the same data type of the column, +0+ if there's no row. See
  76. # #calculate for examples with options.
  77. #
  78. # Person.sum(:age) # => 4562
  79. def sum(column_name = nil)
  80. if block_given?
  81. unless column_name.nil?
  82. raise ArgumentError, "Column name argument is not supported when a block is passed."
  83. end
  84. super()
  85. else
  86. calculate(:sum, column_name)
  87. end
  88. end
  89. # This calculates aggregate values in the given column. Methods for #count, #sum, #average,
  90. # #minimum, and #maximum have been added as shortcuts.
  91. #
  92. # Person.calculate(:count, :all) # The same as Person.count
  93. # Person.average(:age) # SELECT AVG(age) FROM people...
  94. #
  95. # # Selects the minimum age for any family without any minors
  96. # Person.group(:last_name).having("min(age) > 17").minimum(:age)
  97. #
  98. # Person.sum("2 * age")
  99. #
  100. # There are two basic forms of output:
  101. #
  102. # * Single aggregate value: The single value is type cast to Integer for COUNT, Float
  103. # for AVG, and the given column's type for everything else.
  104. #
  105. # * Grouped values: This returns an ordered hash of the values and groups them. It
  106. # takes either a column name, or the name of a belongs_to association.
  107. #
  108. # values = Person.group('last_name').maximum(:age)
  109. # puts values["Drake"]
  110. # # => 43
  111. #
  112. # drake = Family.find_by(last_name: 'Drake')
  113. # values = Person.group(:family).maximum(:age) # Person belongs_to :family
  114. # puts values[drake]
  115. # # => 43
  116. #
  117. # values.each do |family, max_age|
  118. # ...
  119. # end
  120. def calculate(operation, column_name)
  121. if has_include?(column_name)
  122. relation = apply_join_dependency
  123. if operation.to_s.downcase == "count"
  124. unless distinct_value || distinct_select?(column_name || select_for_count)
  125. relation.distinct!
  126. relation.select_values = [ klass.primary_key || table[Arel.star] ]
  127. end
  128. # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
  129. relation.order_values = []
  130. end
  131. relation.calculate(operation, column_name)
  132. else
  133. perform_calculation(operation, column_name)
  134. end
  135. end
  136. # Use #pluck as a shortcut to select one or more attributes without
  137. # loading a bunch of records just to grab the attributes you want.
  138. #
  139. # Person.pluck(:name)
  140. #
  141. # instead of
  142. #
  143. # Person.all.map(&:name)
  144. #
  145. # Pluck returns an Array of attribute values type-casted to match
  146. # the plucked column names, if they can be deduced. Plucking an SQL fragment
  147. # returns String values by default.
  148. #
  149. # Person.pluck(:name)
  150. # # SELECT people.name FROM people
  151. # # => ['David', 'Jeremy', 'Jose']
  152. #
  153. # Person.pluck(:id, :name)
  154. # # SELECT people.id, people.name FROM people
  155. # # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
  156. #
  157. # Person.distinct.pluck(:role)
  158. # # SELECT DISTINCT role FROM people
  159. # # => ['admin', 'member', 'guest']
  160. #
  161. # Person.where(age: 21).limit(5).pluck(:id)
  162. # # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
  163. # # => [2, 3]
  164. #
  165. # Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
  166. # # SELECT DATEDIFF(updated_at, created_at) FROM people
  167. # # => ['0', '27761', '173']
  168. #
  169. # See also #ids.
  170. #
  171. def pluck(*column_names)
  172. if loaded? && all_attributes?(column_names)
  173. return records.pluck(*column_names)
  174. end
  175. if has_include?(column_names.first)
  176. relation = apply_join_dependency
  177. relation.pluck(*column_names)
  178. else
  179. klass.disallow_raw_sql!(column_names)
  180. relation = spawn
  181. relation.select_values = column_names
  182. result = skip_query_cache_if_necessary do
  183. if where_clause.contradiction?
  184. ActiveRecord::Result.new([], [])
  185. else
  186. klass.connection.select_all(relation.arel, nil)
  187. end
  188. end
  189. result.cast_values(klass.attribute_types)
  190. end
  191. end
  192. # Pick the value(s) from the named column(s) in the current relation.
  193. # This is short-hand for <tt>relation.limit(1).pluck(*column_names).first</tt>, and is primarily useful
  194. # when you have a relation that's already narrowed down to a single row.
  195. #
  196. # Just like #pluck, #pick will only load the actual value, not the entire record object, so it's also
  197. # more efficient. The value is, again like with pluck, typecast by the column type.
  198. #
  199. # Person.where(id: 1).pick(:name)
  200. # # SELECT people.name FROM people WHERE id = 1 LIMIT 1
  201. # # => 'David'
  202. #
  203. # Person.where(id: 1).pick(:name, :email_address)
  204. # # SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1
  205. # # => [ 'David', 'david@loudthinking.com' ]
  206. def pick(*column_names)
  207. if loaded? && all_attributes?(column_names)
  208. return records.pick(*column_names)
  209. end
  210. limit(1).pluck(*column_names).first
  211. end
  212. # Pluck all the ID's for the relation using the table's primary key
  213. #
  214. # Person.ids # SELECT people.id FROM people
  215. # Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.person_id = people.id
  216. def ids
  217. pluck primary_key
  218. end
  219. private
  220. def all_attributes?(column_names)
  221. (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
  222. end
  223. def has_include?(column_name)
  224. eager_loading? || (includes_values.present? && column_name && column_name != :all)
  225. end
  226. def perform_calculation(operation, column_name)
  227. operation = operation.to_s.downcase
  228. # If #count is used with #distinct (i.e. `relation.distinct.count`) it is
  229. # considered distinct.
  230. distinct = distinct_value
  231. if operation == "count"
  232. column_name ||= select_for_count
  233. if column_name == :all
  234. if !distinct
  235. distinct = distinct_select?(select_for_count) if group_values.empty?
  236. elsif group_values.any? || select_values.empty? && order_values.empty?
  237. column_name = primary_key
  238. end
  239. elsif distinct_select?(column_name)
  240. distinct = nil
  241. end
  242. end
  243. if group_values.any?
  244. execute_grouped_calculation(operation, column_name, distinct)
  245. else
  246. execute_simple_calculation(operation, column_name, distinct)
  247. end
  248. end
  249. def distinct_select?(column_name)
  250. column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
  251. end
  252. def aggregate_column(column_name)
  253. return column_name if Arel::Expressions === column_name
  254. arel_column(column_name.to_s) do |name|
  255. Arel.sql(column_name == :all ? "*" : name)
  256. end
  257. end
  258. def operation_over_aggregate_column(column_name, operation, distinct)
  259. column = aggregate_column(column_name)
  260. operation == "count" ? column.count(distinct) : column.send(operation)
  261. end
  262. def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
  263. if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
  264. # Shortcut when limit is zero.
  265. return 0 if limit_value == 0
  266. query_builder = build_count_subquery(spawn, column_name, distinct)
  267. else
  268. # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
  269. relation = unscope(:order).distinct!(false)
  270. select_value = operation_over_aggregate_column(column_name, operation, distinct)
  271. select_value.distinct = true if operation == "sum" && distinct
  272. relation.select_values = [select_value]
  273. query_builder = relation.arel
  274. end
  275. result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
  276. type_cast_calculated_value(result.cast_values.first, operation) do |value|
  277. if value.is_a?(String) &&
  278. column = klass.columns_hash[column_name.to_s]
  279. type = connection.lookup_cast_type_from_column(column)
  280. type.deserialize(value)
  281. else
  282. value
  283. end
  284. end
  285. end
  286. def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
  287. group_fields = group_values
  288. if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
  289. association = klass._reflect_on_association(group_fields.first)
  290. associated = association && association.belongs_to? # only count belongs_to associations
  291. group_fields = Array(association.foreign_key) if associated
  292. end
  293. group_fields = arel_columns(group_fields)
  294. group_aliases = group_fields.map { |field|
  295. field = connection.visitor.compile(field) if Arel.arel_node?(field)
  296. column_alias_for(field.to_s.downcase)
  297. }
  298. group_columns = group_aliases.zip(group_fields)
  299. column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
  300. select_value = operation_over_aggregate_column(column_name, operation, distinct)
  301. select_value.as(column_alias)
  302. select_values = [select_value]
  303. select_values += self.select_values unless having_clause.empty?
  304. select_values.concat group_columns.map { |aliaz, field|
  305. if field.respond_to?(:as)
  306. field.as(aliaz)
  307. else
  308. "#{field} AS #{aliaz}"
  309. end
  310. }
  311. relation = except(:group).distinct!(false)
  312. relation.group_values = group_fields
  313. relation.select_values = select_values
  314. calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
  315. if association
  316. key_ids = calculated_data.collect { |row| row[group_aliases.first] }
  317. key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
  318. key_records = key_records.index_by(&:id)
  319. end
  320. key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
  321. types[aliaz] = type_for(col_name) do
  322. calculated_data.column_types.fetch(aliaz, Type.default_value)
  323. end
  324. end
  325. hash_rows = calculated_data.cast_values(key_types).map! do |row|
  326. calculated_data.columns.each_with_object({}).with_index do |(column, hash), i|
  327. hash[column] = row[i]
  328. end
  329. end
  330. type = nil
  331. hash_rows.each_with_object({}) do |row, result|
  332. key = group_aliases.map { |aliaz| row[aliaz] }
  333. key = key.first if key.size == 1
  334. key = key_records[key] if associated
  335. result[key] = type_cast_calculated_value(row[column_alias], operation) do |value|
  336. if value.is_a?(String) &&
  337. (type || column = klass.columns_hash[column_name.to_s])
  338. type ||= connection.lookup_cast_type_from_column(column)
  339. type.deserialize(value)
  340. else
  341. value
  342. end
  343. end
  344. end
  345. end
  346. # Converts the given field to the value that the database adapter returns as
  347. # a usable column name:
  348. #
  349. # column_alias_for("users.id") # => "users_id"
  350. # column_alias_for("sum(id)") # => "sum_id"
  351. # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
  352. # column_alias_for("count(*)") # => "count_all"
  353. def column_alias_for(field)
  354. column_alias = +field
  355. column_alias.gsub!(/\*/, "all")
  356. column_alias.gsub!(/\W+/, " ")
  357. column_alias.strip!
  358. column_alias.gsub!(/ +/, "_")
  359. connection.table_alias_for(column_alias)
  360. end
  361. def type_for(field, &block)
  362. field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
  363. @klass.type_for_attribute(field_name, &block)
  364. end
  365. def type_cast_calculated_value(value, operation)
  366. case operation
  367. when "count", "sum"
  368. value || 0
  369. when "average"
  370. value&.respond_to?(:to_d) ? value.to_d : value
  371. else # "minimum", "maximum"
  372. yield value
  373. end
  374. end
  375. def select_for_count
  376. if select_values.present?
  377. return select_values.first if select_values.one?
  378. select_values.join(", ")
  379. else
  380. :all
  381. end
  382. end
  383. def build_count_subquery(relation, column_name, distinct)
  384. if column_name == :all
  385. column_alias = Arel.star
  386. relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
  387. else
  388. column_alias = Arel.sql("count_column")
  389. relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
  390. end
  391. subquery_alias = Arel.sql("subquery_for_count")
  392. select_value = operation_over_aggregate_column(column_alias, "count", false)
  393. relation.build_subquery(subquery_alias, select_value)
  394. end
  395. end
  396. end