Class: ClickHouse::Client::QueryBuilder
- Defined in:
- lib/click_house/client/query_builder.rb
Constant Summary collapse
- VALID_NODES =
[ Arel::Nodes::In, Arel::Nodes::Equality, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual, Arel::Nodes::NamedFunction, Arel::Nodes::NotIn, Arel::Nodes::NotEqual, Arel::Nodes::Between, Arel::Nodes::And, Arel::Nodes::Or, Arel::Nodes::Grouping, Arel::Nodes::Matches, Arel::Nodes::DoesNotMatch, Arel::Nodes::Division, Arel::Nodes::Multiplication, Arel::Nodes::As ].freeze
Instance Attribute Summary collapse
-
#manager ⇒ Object
Returns the value of attribute manager.
-
#table ⇒ Object
readonly
Returns the value of attribute table.
Instance Method Summary collapse
-
#as(node, alias_name) ⇒ Arel::Nodes::As
Creates an alias for a node.
-
#avg(column) ⇒ Arel::Nodes::NamedFunction
Creates an AVG aggregate function node.
-
#count(column = nil) ⇒ Arel::Nodes::NamedFunction
Creates a COUNT aggregate function node.
-
#count_if(condition) ⇒ Arel::Nodes::NamedFunction
Creates a countIf aggregate function node.
-
#division(left, right) ⇒ Arel::Nodes::Grouping
Creates a division node with grouping.
-
#equality(left, right) ⇒ Arel::Nodes::Equality
Creates an equality node.
- #from(subquery, alias_name) ⇒ Object
- #group(*columns) ⇒ Object
-
#having(constraints) ⇒ ClickHouse::QueryBuilder
The ‘having` method applies constraints to the HAVING clause, similar to how `where` applies constraints to the WHERE clause.
-
#initialize(table_name) ⇒ QueryBuilder
constructor
A new instance of QueryBuilder.
- #initialize_copy(other) ⇒ Object
- #joins(table_name, constraint = nil) ⇒ Object
- #limit(count) ⇒ Object
-
#multiply(left, right) ⇒ Arel::Nodes::Grouping
Creates a multiplication node with grouping.
- #offset(count) ⇒ Object
- #order(field, direction = :asc) ⇒ Object
-
#quantile(level, column) ⇒ Arel::Nodes::NamedFunction
Creates a quantile aggregate function node.
- #select(*fields) ⇒ Object
- #to_arel ⇒ Object
- #to_redacted_sql(bind_index_manager = ClickHouse::Client::BindIndexManager.new) ⇒ Object
- #to_sql ⇒ Object
-
#where(constraints) ⇒ ClickHouse::QueryBuilder
The ‘where` method currently only supports IN and equal to queries along with above listed VALID_NODES.
Methods inherited from QueryLike
Constructor Details
#initialize(table_name) ⇒ QueryBuilder
Returns a new instance of QueryBuilder.
32 33 34 35 |
# File 'lib/click_house/client/query_builder.rb', line 32 def initialize(table_name) @table = Arel::Table.new(table_name) @manager = Arel::SelectManager.new(Arel::Table.engine).from(@table).project(Arel.star) end |
Instance Attribute Details
#manager ⇒ Object
Returns the value of attribute manager.
9 10 11 |
# File 'lib/click_house/client/query_builder.rb', line 9 def manager @manager end |
#table ⇒ Object (readonly)
Returns the value of attribute table.
8 9 10 |
# File 'lib/click_house/client/query_builder.rb', line 8 def table @table end |
Instance Method Details
#as(node, alias_name) ⇒ Arel::Nodes::As
Creates an alias for a node
306 307 308 309 310 |
# File 'lib/click_house/client/query_builder.rb', line 306 def as(node, alias_name) raise ArgumentError, "as requires an Arel node" unless node.is_a?(Arel::Expressions) node.as(alias_name.to_s) end |
#avg(column) ⇒ Arel::Nodes::NamedFunction
Creates an AVG aggregate function node
195 196 197 198 |
# File 'lib/click_house/client/query_builder.rb', line 195 def avg(column) column_node = normalize_operand(column) Arel::Nodes::NamedFunction.new('avg', [column_node]) end |
#count(column = nil) ⇒ Arel::Nodes::NamedFunction
Creates a COUNT aggregate function node
224 225 226 227 228 229 230 231 |
# File 'lib/click_house/client/query_builder.rb', line 224 def count(column = nil) if column.nil? Arel::Nodes::NamedFunction.new('count', []) else column_node = normalize_operand(column) Arel::Nodes::NamedFunction.new('count', [column_node]) end end |
#count_if(condition) ⇒ Arel::Nodes::NamedFunction
Creates a countIf aggregate function node
240 241 242 243 244 |
# File 'lib/click_house/client/query_builder.rb', line 240 def count_if(condition) raise ArgumentError, "countIf requires an Arel node as condition" unless condition.is_a?(Arel::Nodes::Node) Arel::Nodes::NamedFunction.new('countIf', [condition]) end |
#division(left, right) ⇒ Arel::Nodes::Grouping
Creates a division node with grouping
257 258 259 260 261 262 |
# File 'lib/click_house/client/query_builder.rb', line 257 def division(left, right) left_node = normalize_operand(left) right_node = normalize_operand(right) Arel::Nodes::Grouping.new(Arel::Nodes::Division.new(left_node, right_node)) end |
#equality(left, right) ⇒ Arel::Nodes::Equality
Creates an equality node
291 292 293 294 295 |
# File 'lib/click_house/client/query_builder.rb', line 291 def equality(left, right) left_node = normalize_operand(left) right_node = normalize_operand(right) Arel::Nodes::Equality.new(left_node, right_node) end |
#from(subquery, alias_name) ⇒ Object
146 147 148 149 150 151 152 153 154 |
# File 'lib/click_house/client/query_builder.rb', line 146 def from(subquery, alias_name) clone.tap do |new_instance| if subquery.is_a?(self.class) new_instance.manager.from(subquery.to_arel.as(alias_name)) else new_instance.manager.from(Arel::Nodes::TableAlias.new(subquery, alias_name)) end end end |
#group(*columns) ⇒ Object
130 131 132 133 134 |
# File 'lib/click_house/client/query_builder.rb', line 130 def group(*columns) clone.tap do |new_instance| new_instance.manager.group(*columns) end end |
#having(constraints) ⇒ ClickHouse::QueryBuilder
The ‘having` method applies constraints to the HAVING clause, similar to how `where` applies constraints to the WHERE clause. It supports the same constraint types. Correct usage:
query.group(:name).having(count: 5).to_sql
"SELECT * FROM \"table\" GROUP BY \"table\".\"name\" HAVING \"table\".\"count\" = 5"
query.group(:name).having(query.table[:count].gt(10)).to_sql
"SELECT * FROM \"table\" GROUP BY \"table\".\"name\" HAVING \"table\".\"count\" > 10"
76 77 78 79 80 81 82 |
# File 'lib/click_house/client/query_builder.rb', line 76 def having(constraints) validate_constraint_type!(constraints) clone.tap do |new_instance| apply_constraints(new_instance, constraints, :having) end end |
#initialize_copy(other) ⇒ Object
37 38 39 40 41 |
# File 'lib/click_house/client/query_builder.rb', line 37 def initialize_copy(other) super @manager = other.manager.clone end |
#joins(table_name, constraint = nil) ⇒ Object
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/click_house/client/query_builder.rb', line 156 def joins(table_name, constraint = nil) clone.tap do |new_instance| join_table = table_name.is_a?(Arel::Table) ? table_name : Arel::Table.new(table_name) join_condition = case constraint when Hash # Handle hash based constraints like { table1.id: table2.ref_id } or {id: :ref_id} constraint_conditions = constraint.map do |left, right| left_field = left.is_a?(Arel::Attributes::Attribute) ? left : new_instance.table[left] right_field = right.is_a?(Arel::Attributes::Attribute) ? right : join_table[right] left_field.eq(right_field) end constraint_conditions.reduce(&:and) when Proc constraint.call(new_instance.table, join_table) when Arel::Nodes::Node constraint end if join_condition new_instance.manager.join(join_table).on(join_condition) else new_instance.manager.join(join_table) end end end |
#limit(count) ⇒ Object
136 137 138 139 |
# File 'lib/click_house/client/query_builder.rb', line 136 def limit(count) manager.take(count) self end |
#multiply(left, right) ⇒ Arel::Nodes::Grouping
Creates a multiplication node with grouping
274 275 276 277 278 279 |
# File 'lib/click_house/client/query_builder.rb', line 274 def multiply(left, right) left_node = normalize_operand(left) right_node = normalize_operand(right) Arel::Nodes::Grouping.new(Arel::Nodes::Multiplication.new(left_node, right_node)) end |
#offset(count) ⇒ Object
141 142 143 144 |
# File 'lib/click_house/client/query_builder.rb', line 141 def offset(count) manager.skip(count) self end |
#order(field, direction = :asc) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/click_house/client/query_builder.rb', line 114 def order(field, direction = :asc) validate_order_direction!(direction) clone.tap do |new_instance| order_node = case field when Arel::Nodes::SqlLiteral, Arel::Nodes::Node, Arel::Attribute field else new_instance.table[field] end new_order = direction.to_s.casecmp('desc').zero? ? order_node.desc : order_node.asc new_instance.manager.order(new_order) end end |
#quantile(level, column) ⇒ Arel::Nodes::NamedFunction
Creates a quantile aggregate function node
210 211 212 213 |
# File 'lib/click_house/client/query_builder.rb', line 210 def quantile(level, column) column_node = normalize_operand(column) Arel::Nodes::NamedFunction.new("quantile(#{level})", [column_node]) end |
#select(*fields) ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/click_house/client/query_builder.rb', line 84 def select(*fields) clone.tap do |new_instance| existing_fields = new_instance.manager.projections.filter_map do |projection| if projection.respond_to?(:to_s) && projection.to_s == '*' nil elsif projection.is_a?(Arel::Attributes::Attribute) projection.name.to_s elsif projection.is_a?(Arel::Expressions) projection end end new_projections = (existing_fields + fields).map do |field| if field.is_a?(Symbol) field.to_s else field end end new_instance.manager.projections = new_projections.uniq.map do |field| if field.is_a?(Arel::Expressions) field else new_instance.table[field.to_s] end end end end |
#to_arel ⇒ Object
321 322 323 |
# File 'lib/click_house/client/query_builder.rb', line 321 def to_arel manager end |
#to_redacted_sql(bind_index_manager = ClickHouse::Client::BindIndexManager.new) ⇒ Object
317 318 319 |
# File 'lib/click_house/client/query_builder.rb', line 317 def to_redacted_sql(bind_index_manager = ClickHouse::Client::BindIndexManager.new) ClickHouse::Client::Redactor.redact(self, bind_index_manager) end |
#to_sql ⇒ Object
312 313 314 315 |
# File 'lib/click_house/client/query_builder.rb', line 312 def to_sql visitor = ClickHouse::Client::ArelVisitor.new(ClickHouse::Client::ArelEngine.new) visitor.accept(manager.ast, Arel::Collectors::SQLString.new).value end |
#where(constraints) ⇒ ClickHouse::QueryBuilder
The ‘where` method currently only supports IN and equal to queries along with above listed VALID_NODES. For example, using a range (start_date..end_date) will result in incorrect SQL. If you need to query a range, use greater than and less than constraints with Arel.
Correct usage:
query.where(query.table[:created_at].lteq(Date.today)).to_sql
"SELECT * FROM \"table\" WHERE \"table\".\"created_at\" <= '2023-08-01'"
This also supports array constraints which will result in an IN query.
query.where(entity_id: [1,2,3]).to_sql
"SELECT * FROM \"table\" WHERE \"table\".\"entity_id\" IN (1, 2, 3)"
Range support and more ‘Arel::Nodes` could be considered for future iterations.
58 59 60 61 62 63 64 |
# File 'lib/click_house/client/query_builder.rb', line 58 def where(constraints) validate_constraint_type!(constraints) clone.tap do |new_instance| apply_constraints(new_instance, constraints, :where) end end |