click_house-client 0.7.1 → 0.8.0
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 +4 -4
- data/Gemfile.lock +2 -2
- data/lib/click_house/client/query_builder.rb +143 -1
- data/lib/click_house/client/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45b65dc2ae8ced88a1129e3fb7e78e3d314bbc5eb41402e430932d7e4c169e98
|
4
|
+
data.tar.gz: 9f25ca4f27d5c0b500339bc682d6de3b5b0ffd97b5484b14707949906064b891
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a50a5010fb3900ec46fc9c84fe4bbe6f3647beee599fd40940ddfa89f7ae6ba6a0eb569021b9f34a0e9f72237277b75b34a4820523fa2413a95b696d16c9a2c5
|
7
|
+
data.tar.gz: 998b97239232786839a7568a8a06aa69090127ae1ba68be5cb30bfb57ad26c599aedfda37a4603b0cdf3464a7dd9209f88793688ed5af81dcd0f4315646ed97c
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
click_house-client (0.
|
4
|
+
click_house-client (0.8.0)
|
5
5
|
activerecord (>= 7.0, < 9.0)
|
6
6
|
activesupport (>= 7.0, < 9.0)
|
7
7
|
addressable (~> 2.8)
|
@@ -136,7 +136,7 @@ CHECKSUMS
|
|
136
136
|
benchmark (0.4.1) sha256=d4ef40037bba27f03b28013e219b950b82bace296549ec15a78016552f8d2cce
|
137
137
|
bigdecimal (3.2.2) sha256=39085f76b495eb39a79ce07af716f3a6829bc35eb44f2195e2753749f2fa5adc
|
138
138
|
byebug (12.0.0) sha256=d4a150d291cca40b66ec9ca31f754e93fed8aa266a17335f71bb0afa7fca1a1e
|
139
|
-
click_house-client (0.
|
139
|
+
click_house-client (0.8.0)
|
140
140
|
concurrent-ruby (1.3.5) sha256=813b3e37aca6df2a21a3b9f1d497f8cbab24a2b94cab325bffe65ee0f6cbebc6
|
141
141
|
connection_pool (2.5.3) sha256=cfd74a82b9b094d1ce30c4f1a346da23ee19dc8a062a16a85f58eab1ced4305b
|
142
142
|
diff-lcs (1.5.0) sha256=49b934001c8c6aedb37ba19daec5c634da27b318a7a3c654ae979d6ba1929b67
|
@@ -23,7 +23,10 @@ module ClickHouse
|
|
23
23
|
Arel::Nodes::Or,
|
24
24
|
Arel::Nodes::Grouping,
|
25
25
|
Arel::Nodes::Matches,
|
26
|
-
Arel::Nodes::DoesNotMatch
|
26
|
+
Arel::Nodes::DoesNotMatch,
|
27
|
+
Arel::Nodes::Division,
|
28
|
+
Arel::Nodes::Multiplication,
|
29
|
+
Arel::Nodes::As
|
27
30
|
].freeze
|
28
31
|
|
29
32
|
def initialize(table_name)
|
@@ -178,6 +181,134 @@ module ClickHouse
|
|
178
181
|
end
|
179
182
|
end
|
180
183
|
|
184
|
+
# Aggregation helper methods
|
185
|
+
|
186
|
+
# Creates an AVG aggregate function node
|
187
|
+
# @param column [Symbol, String, Arel::Expressions] The column to average
|
188
|
+
# @return [Arel::Nodes::NamedFunction] The AVG function node
|
189
|
+
# @example Basic average
|
190
|
+
# query.select(query.avg(:duration)).to_sql
|
191
|
+
# # => "SELECT avg(`table`.`duration`) FROM `table`"
|
192
|
+
# @example Average with alias
|
193
|
+
# query.select(query.avg(:price).as('average_price')).to_sql
|
194
|
+
# # => "SELECT avg(`table`.`price`) AS average_price FROM `table`"
|
195
|
+
def avg(column)
|
196
|
+
column_node = normalize_operand(column)
|
197
|
+
Arel::Nodes::NamedFunction.new('avg', [column_node])
|
198
|
+
end
|
199
|
+
|
200
|
+
# Creates a quantile aggregate function node
|
201
|
+
# @param level [Float] The quantile level (e.g., 0.5 for median)
|
202
|
+
# @param column [Symbol, String, Arel::Expressions] The column to calculate quantile for
|
203
|
+
# @return [Arel::Nodes::NamedFunction] The quantile function node
|
204
|
+
# @example Calculate median (50th percentile)
|
205
|
+
# query.select(query.quantile(0.5, :response_time)).to_sql
|
206
|
+
# # => "SELECT quantile(0.5)(`table`.`response_time`) FROM `table`"
|
207
|
+
# @example Calculate 95th percentile with alias
|
208
|
+
# query.select(query.quantile(0.95, :latency).as('p95')).to_sql
|
209
|
+
# # => "SELECT quantile(0.95)(`table`.`latency`) AS p95 FROM `table`"
|
210
|
+
def quantile(level, column)
|
211
|
+
column_node = normalize_operand(column)
|
212
|
+
Arel::Nodes::NamedFunction.new("quantile(#{level})", [column_node])
|
213
|
+
end
|
214
|
+
|
215
|
+
# Creates a COUNT aggregate function node
|
216
|
+
# @param column [Symbol, String, Arel::Expressions, nil] The column to count, or nil for COUNT(*)
|
217
|
+
# @return [Arel::Nodes::NamedFunction] The COUNT function node
|
218
|
+
# @example Count all rows
|
219
|
+
# query.select(query.count).to_sql
|
220
|
+
# # => "SELECT count() FROM `table`"
|
221
|
+
# @example Count specific column
|
222
|
+
# query.select(query.count(:id)).to_sql
|
223
|
+
# # => "SELECT count(`table`.`id`) FROM `table`"
|
224
|
+
def count(column = nil)
|
225
|
+
if column.nil?
|
226
|
+
Arel::Nodes::NamedFunction.new('count', [])
|
227
|
+
else
|
228
|
+
column_node = normalize_operand(column)
|
229
|
+
Arel::Nodes::NamedFunction.new('count', [column_node])
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Creates a countIf aggregate function node
|
234
|
+
# @param condition [Arel::Nodes::Node] The condition to count
|
235
|
+
# @return [Arel::Nodes::NamedFunction] The countIf function node
|
236
|
+
# @raise [ArgumentError] if condition is not an Arel node
|
237
|
+
# @example Count rows matching a condition
|
238
|
+
# query.select(query.count_if(query.table[:status].eq('active'))).to_sql
|
239
|
+
# # => "SELECT countIf(`table`.`status` = 'active') FROM `table`"
|
240
|
+
def count_if(condition)
|
241
|
+
raise ArgumentError, "countIf requires an Arel node as condition" unless condition.is_a?(Arel::Nodes::Node)
|
242
|
+
|
243
|
+
Arel::Nodes::NamedFunction.new('countIf', [condition])
|
244
|
+
end
|
245
|
+
|
246
|
+
# Creates a division node with grouping
|
247
|
+
# @param left [Arel::Expressions, Symbol, String, Numeric] The dividend
|
248
|
+
# @param right [Arel::Expressions, Symbol, String, Numeric] The divisor
|
249
|
+
# @return [Arel::Nodes::Grouping] The grouped division node for proper precedence
|
250
|
+
# @example Simple division
|
251
|
+
# query.select(query.division(:completed, :total)).to_sql
|
252
|
+
# # => "SELECT (`table`.`completed` / `table`.`total`) FROM `table`"
|
253
|
+
# @example Calculate percentage
|
254
|
+
# rate = query.division(:success_count, :total_count)
|
255
|
+
# query.select(query.multiply(rate, 100).as('success_rate')).to_sql
|
256
|
+
# # => "SELECT ((`table`.`success_count` / `table`.`total_count`) * 100) AS success_rate FROM `table`"
|
257
|
+
def division(left, right)
|
258
|
+
left_node = normalize_operand(left)
|
259
|
+
right_node = normalize_operand(right)
|
260
|
+
|
261
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Division.new(left_node, right_node))
|
262
|
+
end
|
263
|
+
|
264
|
+
# Creates a multiplication node with grouping
|
265
|
+
# @param left [Arel::Expressions, Symbol, String, Numeric] The left operand
|
266
|
+
# @param right [Arel::Expressions, Symbol, String, Numeric] The right operand
|
267
|
+
# @return [Arel::Nodes::Grouping] The grouped multiplication node for proper precedence
|
268
|
+
# @example Multiply columns
|
269
|
+
# query.select(query.multiply(:quantity, :unit_price)).to_sql
|
270
|
+
# # => "SELECT (`table`.`quantity` * `table`.`unit_price`) FROM `table`"
|
271
|
+
# @example Convert to percentage
|
272
|
+
# query.select(query.multiply(:rate, 100).as('percentage')).to_sql
|
273
|
+
# # => "SELECT (`table`.`rate` * 100) AS percentage FROM `table`"
|
274
|
+
def multiply(left, right)
|
275
|
+
left_node = normalize_operand(left)
|
276
|
+
right_node = normalize_operand(right)
|
277
|
+
|
278
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Multiplication.new(left_node, right_node))
|
279
|
+
end
|
280
|
+
|
281
|
+
# Creates an equality node
|
282
|
+
# @param left [Arel::Expressions, Symbol, String] The left side of the comparison
|
283
|
+
# @param right [Arel::Expressions, Symbol, String, Numeric, Boolean] The right side of the comparison
|
284
|
+
# @return [Arel::Nodes::Equality] The equality node
|
285
|
+
# @example Use in WHERE clause
|
286
|
+
# query.where(query.equality(:status, 'active')).to_sql
|
287
|
+
# # => "SELECT * FROM `table` WHERE `table`.`status` = 'active'"
|
288
|
+
# @example Use with countIf
|
289
|
+
# query.select(query.count_if(query.equality(:type, 'premium'))).to_sql
|
290
|
+
# # => "SELECT countIf(`table`.`type` = 'premium') FROM `table`"
|
291
|
+
def equality(left, right)
|
292
|
+
left_node = normalize_operand(left)
|
293
|
+
right_node = normalize_operand(right)
|
294
|
+
Arel::Nodes::Equality.new(left_node, right_node)
|
295
|
+
end
|
296
|
+
|
297
|
+
# Creates an alias for a node
|
298
|
+
# @param node [Arel::Nodes::Node] The node to alias
|
299
|
+
# @param alias_name [String, Symbol] The alias name
|
300
|
+
# @return [Arel::Nodes::As] The aliased node
|
301
|
+
# @raise [ArgumentError] if node is not an Arel Expression
|
302
|
+
# @example Alias an aggregate function
|
303
|
+
# avg_node = query.avg(:price)
|
304
|
+
# query.select(query.as(avg_node, 'average_price')).to_sql
|
305
|
+
# # => "SELECT avg(`table`.`price`) AS average_price FROM `table`"
|
306
|
+
def as(node, alias_name)
|
307
|
+
raise ArgumentError, "as requires an Arel node" unless node.is_a?(Arel::Expressions)
|
308
|
+
|
309
|
+
node.as(alias_name.to_s)
|
310
|
+
end
|
311
|
+
|
181
312
|
def to_sql
|
182
313
|
visitor = ClickHouse::Client::ArelVisitor.new(ClickHouse::Client::ArelEngine.new)
|
183
314
|
visitor.accept(manager.ast, Arel::Collectors::SQLString.new).value
|
@@ -193,6 +324,17 @@ module ClickHouse
|
|
193
324
|
|
194
325
|
private
|
195
326
|
|
327
|
+
def normalize_operand(operand)
|
328
|
+
case operand
|
329
|
+
when Arel::Expressions
|
330
|
+
operand
|
331
|
+
when Symbol, String
|
332
|
+
table[operand.to_s]
|
333
|
+
else
|
334
|
+
Arel::Nodes.build_quoted(operand)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
196
338
|
def validate_constraint_type!(constraint)
|
197
339
|
return unless constraint.is_a?(Arel::Nodes::Node) && VALID_NODES.exclude?(constraint.class)
|
198
340
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: click_house-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- group::optimize
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-09-
|
11
|
+
date: 2025-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|