Class: Dorm::QueryBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/dorm/query_builder.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(table_name, data_class) ⇒ QueryBuilder

Returns a new instance of QueryBuilder.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/dorm/query_builder.rb', line 7

def initialize(table_name, data_class)
  @table_name = table_name
  @data_class = data_class
  @select_fields = ["#{table_name}.*"]
  @joins = []
  @where_conditions = []
  @group_by_fields = []
  @having_conditions = []
  @order_by_fields = []
  @limit_value = nil
  @offset_value = nil
  @params = []
  @param_counter = 0
end

Instance Attribute Details

#data_classObject (readonly)

Returns the value of attribute data_class.



5
6
7
# File 'lib/dorm/query_builder.rb', line 5

def data_class
  @data_class
end

#table_nameObject (readonly)

Returns the value of attribute table_name.



5
6
7
# File 'lib/dorm/query_builder.rb', line 5

def table_name
  @table_name
end

Instance Method Details

#add_hash_conditions(hash) ⇒ Object



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/dorm/query_builder.rb', line 222

def add_hash_conditions(hash)
  hash.each do |field, value|
    if value.is_a?(Array)
      placeholders = value.map { next_placeholder }.join(", ")
      @where_conditions << ["#{format_field(field)} IN (#{placeholders})", value]
    elsif value.is_a?(Range)
      condition = "#{format_field(field)} BETWEEN #{next_placeholder} AND #{next_placeholder}"
      @where_conditions << [condition, [value.begin, value.end]]
    elsif value.nil?
      @where_conditions << ["#{format_field(field)} IS NULL", []]
    else
      condition = "#{format_field(field)} = #{next_placeholder}"
      @where_conditions << [condition, [value]]
    end
  end
end

#add_where_condition(condition, params) ⇒ Object



218
219
220
# File 'lib/dorm/query_builder.rb', line 218

def add_where_condition(condition, params)
  @where_conditions << [condition, params]
end

#avg(field) ⇒ Object



200
201
202
203
204
# File 'lib/dorm/query_builder.rb', line 200

def avg(field)
  select_raw("AVG(#{format_field(field)}) as avg")
    .execute
    .map { |results| results.first&.[]("avg")&.to_f || 0 }
end

#countObject



164
165
166
167
168
169
170
# File 'lib/dorm/query_builder.rb', line 164

def count
  select_raw("COUNT(*) as count")
    .limit(nil)
    .offset(nil)
    .execute
    .map { |results| results.first&.[]("count")&.to_i || 0 }
end

#executeObject



179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/dorm/query_builder.rb', line 179

def execute
  Result.try do
    sql, params = build_sql_with_params
    result = Database.query(sql, params)

    case result
    when ->(r) { r.respond_to?(:map) }
      result.map { |row| row_to_data(row) }
    else
      [row_to_data(result)]
    end
  end
end

#exists?Boolean

Returns:

  • (Boolean)


172
173
174
175
176
177
# File 'lib/dorm/query_builder.rb', line 172

def exists?
  select_raw("1")
    .limit(1)
    .execute
    .map { |results| !results.empty? }
end

#firstObject



157
158
159
160
161
162
# File 'lib/dorm/query_builder.rb', line 157

def first
  limit(1).execute.bind do |results|
    result = results.first
    result ? Result.success(result) : Result.failure("No records found")
  end
end

#group_by(*fields) ⇒ Object

GROUP BY and HAVING



94
95
96
97
98
99
100
# File 'lib/dorm/query_builder.rb', line 94

def group_by(*fields)
  clone.tap do |query|
    formatted_fields = fields.map { |f| format_field(f) }
    query.instance_variable_set(:@group_by_fields,
                                query.instance_variable_get(:@group_by_fields) + formatted_fields)
  end
end

#having(condition, *params) ⇒ Object



102
103
104
105
106
107
108
# File 'lib/dorm/query_builder.rb', line 102

def having(condition, *params)
  clone.tap do |query|
    query.instance_variable_get(:@having_conditions) << [condition, params]
    query.instance_variable_set(:@param_counter,
                                query.instance_variable_get(:@param_counter) + params.length)
  end
end

#inner_join(table, condition = nil, **kwargs) ⇒ Object



89
90
91
# File 'lib/dorm/query_builder.rb', line 89

def inner_join(table, condition = nil, **kwargs)
  add_join("INNER JOIN", table, condition, kwargs)
end

#join(table, condition = nil, **kwargs) ⇒ Object

JOIN methods



77
78
79
# File 'lib/dorm/query_builder.rb', line 77

def join(table, condition = nil, **kwargs)
  add_join("INNER JOIN", table, condition, kwargs)
end

#left_join(table, condition = nil, **kwargs) ⇒ Object



81
82
83
# File 'lib/dorm/query_builder.rb', line 81

def left_join(table, condition = nil, **kwargs)
  add_join("LEFT JOIN", table, condition, kwargs)
end

#limit(count) ⇒ Object

LIMIT and OFFSET



130
131
132
133
134
# File 'lib/dorm/query_builder.rb', line 130

def limit(count)
  clone.tap do |query|
    query.instance_variable_set(:@limit_value, count)
  end
end

#max(field) ⇒ Object



206
207
208
209
210
# File 'lib/dorm/query_builder.rb', line 206

def max(field)
  select_raw("MAX(#{format_field(field)}) as max")
    .execute
    .map { |results| results.first&.[]("max") }
end

#min(field) ⇒ Object



212
213
214
215
216
# File 'lib/dorm/query_builder.rb', line 212

def min(field)
  select_raw("MIN(#{format_field(field)}) as min")
    .execute
    .map { |results| results.first&.[]("min") }
end

#offset(count) ⇒ Object



136
137
138
139
140
# File 'lib/dorm/query_builder.rb', line 136

def offset(count)
  clone.tap do |query|
    query.instance_variable_set(:@offset_value, count)
  end
end

#order(field, direction = :asc) ⇒ Object



125
126
127
# File 'lib/dorm/query_builder.rb', line 125

def order(field, direction = :asc)
  order_by(field => direction)
end

#order_by(*fields) ⇒ Object

ORDER BY



111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/dorm/query_builder.rb', line 111

def order_by(*fields)
  clone.tap do |query|
    formatted_fields = fields.map do |field|
      case field
      when Hash
        field.map { |f, direction| "#{format_field(f)} #{direction.to_s.upcase}" }.join(", ")
      else
        format_field(field)
      end
    end
    query.instance_variable_set(:@order_by_fields, formatted_fields)
  end
end

#page(page_num, per_page = 20) ⇒ Object

Pagination helpers



143
144
145
146
# File 'lib/dorm/query_builder.rb', line 143

def page(page_num, per_page = 20)
  offset_count = (page_num - 1) * per_page
  limit(per_page).offset(offset_count)
end

#right_join(table, condition = nil, **kwargs) ⇒ Object



85
86
87
# File 'lib/dorm/query_builder.rb', line 85

def right_join(table, condition = nil, **kwargs)
  add_join("RIGHT JOIN", table, condition, kwargs)
end

#select(*fields) ⇒ Object

SELECT methods



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/dorm/query_builder.rb', line 23

def select(*fields)
  clone.tap do |query|
    if fields.empty?
      query.instance_variable_set(:@select_fields, ["#{table_name}.*"])
    else
      formatted_fields = fields.map do |field|
        case field
        when Symbol
          "#{table_name}.#{field}"
        when String
          field.include?(".") ? field : "#{table_name}.#{field}"
        else
          field.to_s
        end
      end
      query.instance_variable_set(:@select_fields, formatted_fields)
    end
  end
end

#select_raw(sql) ⇒ Object



43
44
45
46
47
# File 'lib/dorm/query_builder.rb', line 43

def select_raw(sql)
  clone.tap do |query|
    query.instance_variable_set(:@select_fields, [sql])
  end
end

#sum(field) ⇒ Object

Aggregation methods



194
195
196
197
198
# File 'lib/dorm/query_builder.rb', line 194

def sum(field)
  select_raw("SUM(#{format_field(field)}) as sum")
    .execute
    .map { |results| results.first&.[]("sum")&.to_f || 0 }
end

#to_aObject



153
154
155
# File 'lib/dorm/query_builder.rb', line 153

def to_a
  execute.value_or([])
end

#to_sqlObject

Execution methods



149
150
151
# File 'lib/dorm/query_builder.rb', line 149

def to_sql
  build_sql
end

#where(conditions = nil, **kwargs, &block) ⇒ Object

WHERE methods



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/dorm/query_builder.rb', line 50

def where(conditions = nil, **kwargs, &block)
  clone.tap do |query|
    if block_given?
      # DSL block: where { name.eq("Alice").and(age.gt(18)) }
      dsl = WhereDSL.new(table_name, query.instance_variable_get(:@param_counter))
      condition = dsl.instance_eval(&block)
      query.instance_variable_get(:@where_conditions) << [condition.to_sql, condition.params]
      query.instance_variable_set(:@param_counter,
                                  query.instance_variable_get(:@param_counter) + condition.params.length)
    elsif conditions.is_a?(Hash) || !kwargs.empty?
      # Hash conditions: where(name: "Alice", age: 25)
      hash_conditions = conditions.is_a?(Hash) ? conditions : kwargs
      query.add_hash_conditions(hash_conditions)
    elsif conditions.is_a?(String)
      # Raw SQL: where("name = ? AND age > ?", "Alice", 18)
      query.add_where_condition(conditions, [])
    end
  end
end

#where_raw(sql, *params) ⇒ Object



70
71
72
73
74
# File 'lib/dorm/query_builder.rb', line 70

def where_raw(sql, *params)
  clone.tap do |query|
    query.add_where_condition(sql, params)
  end
end