このコミットが含まれているのは:
@@ -0,0 +1,59 @@
|
||||
# Re-aggregate the population, never add up already grouped cells (multi-value axes overlap).
|
||||
class CountsPivotTotals
|
||||
def initialize(population, axes, row_headers, column_headers)
|
||||
@population = population
|
||||
@axes = axes
|
||||
@rows = row_headers.group_by { _1[:values].length }
|
||||
@columns = column_headers.group_by { _1[:values].length }
|
||||
end
|
||||
|
||||
def call
|
||||
queries = @rows.flat_map do |row_depth, headers|
|
||||
@columns.filter_map do |column_depth, columns|
|
||||
next if row_depth == @axes.rows.length && column_depth == @axes.columns.length
|
||||
|
||||
indexes = (0...row_depth).to_a +
|
||||
(0...column_depth).map { @axes.rows.length + _1 }
|
||||
expressions = indexes.map { @axes.expressions[_1] }
|
||||
scope = @population.scope.except(:select, :order)
|
||||
joins = @axes.joins(indexes.map { @axes.all[_1].fetch('field') })
|
||||
scope = scope.joins(joins) unless joins.empty?
|
||||
if row_depth.positive?
|
||||
tuples = headers.map { _1[:values] }.uniq
|
||||
predicate = tuples.map { Array.new(row_depth) { |i|
|
||||
"(#{ expressions[i] }) <=> ?"
|
||||
}.join(' AND ') }.map { "(#{ _1 })" }.join(' OR ')
|
||||
scope = scope.where(predicate, *tuples.flatten)
|
||||
end
|
||||
if column_depth.positive?
|
||||
tuples = columns.map { _1[:values] }.uniq
|
||||
predicate = tuples.map { Array.new(column_depth) { |i|
|
||||
"(#{ expressions[row_depth + i] }) <=> ?"
|
||||
}.join(' AND ') }.map { "(#{ _1 })" }.join(' OR ')
|
||||
scope = scope.where(predicate, *tuples.flatten)
|
||||
end
|
||||
grouped = expressions.each_with_index.map { |expression, index|
|
||||
"#{ expression } AS v#{ index }"
|
||||
}
|
||||
# Fixed-width result coordinates, independent of NULL and of translated labels.
|
||||
padding = (expressions.length...@axes.all.length).map { "NULL AS v#{ _1 }" }
|
||||
fields = ["#{ row_depth } AS row_depth", "#{ column_depth } AS column_depth"] +
|
||||
grouped + padding + ['COUNT(DISTINCT v.id) AS count']
|
||||
scope = scope.group(*expressions.map { Arel.sql(_1) }) if expressions.any?
|
||||
scope.optimizer_hints('MAX_EXECUTION_TIME(10000)')
|
||||
.select(Arel.sql(fields.join(', '))).to_sql
|
||||
end
|
||||
end
|
||||
return [] if queries.empty?
|
||||
|
||||
sql = queries.map { "(#{ _1 })" }.join(' UNION ALL ')
|
||||
@population.definition.model.connection.select_all(
|
||||
"SELECT /*+ MAX_EXECUTION_TIME(10000) */ * FROM (#{ sql }) totals").map do |record|
|
||||
row_depth = record['row_depth'].to_i
|
||||
column_depth = record['column_depth'].to_i
|
||||
{ row: (0...row_depth).map { record["v#{ _1 }"] },
|
||||
column: (0...column_depth).map { record["v#{ row_depth + _1 }"] },
|
||||
count: record['count'].to_i }
|
||||
end
|
||||
end
|
||||
end
|
||||
新しいイシューから参照
ユーザーをブロックする