portolan 0.1.0.pre.1 → 0.1.0.pre.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ddd564427e8a47bf2013520a1f2adb0de2970481c6c83f55f20ea4cd751c818
4
- data.tar.gz: ffe366aa7c3f03197e814f68648d1463b54c57f155af0ffa0782495e58390c85
3
+ metadata.gz: 557009d4c18190afccf70236dd6697ce44463bf51a278e6730a493f6f4235d01
4
+ data.tar.gz: 3caa4daa83800e0c510f60c377bf999f36510fba94dfc928532489a97a805492
5
5
  SHA512:
6
- metadata.gz: 1d696d9332f633dec4a9eb3231d538288c6aa17e387277cb4f75234b0d8f69f8a397e2dddd4019e3f0adb9e92616f5a4bcb919c3609d4cc24972f6176d022014
7
- data.tar.gz: 96e1499eb19e2f94e7d16811553fa5fa29dc3d686c318a63c318b2c14015970846569794aa5e1d14b443a9952c361392b0167d4a0790b8d88985252a918a3a1d
6
+ metadata.gz: f1edc5628a9cfcf11ff46e2e86bb7f7db491f26cb9785193ccd3025f37463421f73c434966127fa359bc8d077d2d1ec2a9e451b491acb7fa237c84583d00eb32
7
+ data.tar.gz: 0cc74bd0d2f599f602f1613ca8b57b5f303ca9f5af3099d46a460cf8d591e1f6b18ebe68c5feb568be0d7a1d9c8da8cd2b61726c98b460d110b4f6ab6c633d4d
data/README.md CHANGED
@@ -184,11 +184,15 @@ in Ruby).
184
184
  - Dimensions cannot join through a `has_many` or a polymorphic association; see
185
185
  the grain rule above.
186
186
  - The timeout applies to each SQL statement, not to the whole request. Grouping
187
- by buckets runs one statement per bucket combination.
187
+ by buckets runs one or two statements per bucket combination.
188
+ - Without a date dimension, the database sorts the groups by the measure and
189
+ returns at most the row limit, largest first; a second statement counts
190
+ them for the truncation note. A series loads every group, up to the group
191
+ limit, so it can keep whole periods.
188
192
  - Your `relation` block runs before Portolan's guard is in place, so it should
189
193
  build a relation, not run queries itself.
190
- - The timeout (5 seconds), row limit (50), group limit (10,000), and number of
191
- bucket combinations (50) are fixed for now.
194
+ - The timeout (5 seconds), row limit (50), group limit for series (10,000), and
195
+ number of bucket combinations (50) are fixed for now.
192
196
 
193
197
  ## Why the name?
194
198
 
@@ -24,6 +24,15 @@ module Portolan
24
24
  end
25
25
  end
26
26
 
27
+ def node(scope)
28
+ case aggregate
29
+ when :count then Arel.star.count
30
+ when :count_distinct then scope.arel_table[column].count(true)
31
+ when :sum then scope.arel_table[column].sum
32
+ when :average then scope.arel_table[column].average
33
+ end
34
+ end
35
+
27
36
  def describe = { name:, description: }
28
37
  def label(value) = value.is_a?(::BigDecimal) ? value.to_f : value
29
38
 
@@ -76,26 +76,26 @@ module Portolan
76
76
  def period = @params['period']
77
77
 
78
78
  def answer(scope)
79
- rows = measured_rows(scope)
79
+ rows, total_rows = measured_rows(scope)
80
80
  raise Refused, TOO_MANY_GROUPS if rows.size > GROUP_LIMIT
81
81
 
82
- rows = sorted(rows)
83
- Result.new(request:, rows: trimmed(rows), definitions:, total_rows: rows.size)
82
+ Result.new(request:, rows: trimmed(sorted(rows)), definitions:, total_rows:)
84
83
  end
85
84
 
86
85
  def measured_rows(scope)
87
86
  bucket_dimensions, column_dimensions = group_dimensions.partition(&:buckets?)
88
87
  rows = []
88
+ total_rows = 0
89
89
 
90
90
  bucket_combinations(bucket_dimensions).each do |combination|
91
91
  narrowed = combination.reduce(scope) { |current, (_dimension, bucket)| bucket.apply(current) }
92
92
  bucket_labels = combination.to_h { |dimension, bucket| [dimension.name, bucket.name] }
93
- grouped_values(narrowed, column_dimensions).each do |keys, value|
94
- rows << row(bucket_labels, column_dimensions.zip(keys), value)
95
- end
93
+ values, group_count = grouped_values(narrowed, column_dimensions)
94
+ values.each { |keys, value| rows << row(bucket_labels, column_dimensions.zip(keys), value) }
95
+ total_rows += group_count
96
96
  break if rows.size > GROUP_LIMIT
97
97
  end
98
- rows
98
+ [rows, total_rows]
99
99
  end
100
100
 
101
101
  def bucket_combinations(dimensions)
@@ -112,15 +112,31 @@ module Portolan
112
112
  end
113
113
 
114
114
  def grouped_values(scope, dimensions)
115
- return [[[], measure.call(scope)]] if dimensions.empty?
115
+ return [[[[], measure.call(scope)]], 1] if dimensions.empty?
116
116
 
117
- grouped = dimensions.reduce(scope) do |current, dimension|
117
+ grouped = grouped_scope(scope, dimensions)
118
+ return largest_first(grouped, dimensions) unless time_dimension
119
+
120
+ values = keyed(measure.call(grouped.limit(GROUP_LIMIT + 1)), dimensions)
121
+ [values, values.size]
122
+ end
123
+
124
+ def grouped_scope(scope, dimensions)
125
+ dimensions.reduce(scope) do |current, dimension|
118
126
  current = dimension.join(current)
119
127
  current.group(group_expression(current, dimension))
120
128
  end
121
- measure.call(grouped.limit(GROUP_LIMIT + 1)).map { |keys, value| [dimensions.one? ? [keys] : keys, value] }
122
129
  end
123
130
 
131
+ def largest_first(grouped, dimensions)
132
+ largest = grouped.order(measure.node(grouped).desc.nulls_last).limit(limit)
133
+ [keyed(measure.call(largest), dimensions), group_count(grouped)]
134
+ end
135
+
136
+ def group_count(grouped) = grouped.klass.unscoped.from(grouped.select(Arel.sql('1')), :groups).count
137
+
138
+ def keyed(values, dimensions) = values.map { |keys, value| [dimensions.one? ? [keys] : keys, value] }
139
+
124
140
  def group_expression(scope, dimension) = dimension.type.group(dimension.column(scope), period)
125
141
 
126
142
  def row(bucket_labels, keyed, value)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Portolan
4
- VERSION = '0.1.0.pre.1'
4
+ VERSION = '0.1.0.pre.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: portolan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre.1
4
+ version: 0.1.0.pre.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Busse