Class: QueryBuilder::Query

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

Constant Summary collapse

SELECT_WITH_TYPE_REGEX =
/^(.*):(.*)$/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(processor_class) ⇒ Query

Returns a new instance of Query.



16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/query_builder/query.rb', line 16

def initialize(processor_class)
  @processor_class = processor_class
  @tables = []
  @table_alias  = {}
  @unique_alias = {}
  @join_tables  = {}
  @needed_join_tables = {}
  @attributes_alias   = {}
  # Custom select foo as bar:time or 'types:' field in custom query.
  @types              = {}
  @key_value_tables   = {}
  @where = []
end

Instance Attribute Details

#attributes_aliasObject

Returns the value of attribute attributes_alias.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def attributes_alias
  @attributes_alias
end

#contextObject

Returns the value of attribute context.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def context
  @context
end

#distinctObject

Returns the value of attribute distinct.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def distinct
  @distinct
end

#errorObject

Returns the value of attribute error.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def error
  @error
end

#groupObject

Returns the value of attribute group.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def group
  @group
end

#havingObject

Returns the value of attribute having.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def having
  @having
end

#key_value_tablesObject

Returns the value of attribute key_value_tables.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def key_value_tables
  @key_value_tables
end

#limitObject

Returns the value of attribute limit.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def limit
  @limit
end

#main_classObject

Return the class of resulting objects (different from default_class if the value has been changed by the query building process).



37
38
39
# File 'lib/query_builder/query.rb', line 37

def main_class
  @main_class
end

#offsetObject

Returns the value of attribute offset.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def offset
  @offset
end

#orderObject

Returns the value of attribute order.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def order
  @order
end

#page_sizeObject

Returns the value of attribute page_size.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def page_size
  @page_size
end

#pagination_keyObject

Returns the value of attribute pagination_key.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def pagination_key
  @pagination_key
end

#processor_classObject

Returns the value of attribute processor_class.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def processor_class
  @processor_class
end

#selectObject

Returns the value of attribute select.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def select
  @select
end

#table_aliasObject

Returns the value of attribute table_alias.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def table_alias
  @table_alias
end

#tablesObject

Returns the value of attribute tables.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def tables
  @tables
end

#typesObject

Returns the value of attribute types.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def types
  @types
end

#unique_aliasObject

Returns the value of attribute unique_alias.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def unique_alias
  @unique_alias
end

#whereObject

Returns the value of attribute where.



6
7
8
# File 'lib/query_builder/query.rb', line 6

def where
  @where
end

Class Method Details

.adapterObject



11
12
13
# File 'lib/query_builder/query.rb', line 11

def adapter
  @adapter ||= ActiveRecord::Base.connection.class.name.split('::').last[/(.+)Adapter/,1].downcase
end

Instance Method Details

#add_filter(filter) ⇒ Object



59
60
61
# File 'lib/query_builder/query.rb', line 59

def add_filter(filter)
  @where << filter
end

#add_key_value_table(use_name, index_table, key, &block) ⇒ Object

Add a table to ‘import’ a key/value based field. This method ensures that a given field is only included once for each context.



130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/query_builder/query.rb', line 130

def add_key_value_table(use_name, index_table, key, &block)
  key_tables = (@key_value_tables[table] ||= {})
  key_table = (key_tables[use_name] ||= {})
  if alias_table = key_table[key]
    # done, the index_table has been used for the given key in the current context
    alias_table
  else
    # insert the new table
    # Let caller configure the filter with &block.
    key_table[key] = add_table(use_name + "_#{key}", index_table, false, :left, &block)
  end
end

#add_select(field, fname) ⇒ Object



143
144
145
146
147
148
149
150
151
# File 'lib/query_builder/query.rb', line 143

def add_select(field, fname)
  if fname =~ SELECT_WITH_TYPE_REGEX
    fname, type = $1, $2
    @types[fname] = type.to_sym
  end
  @select ||= ["#{main_table}.*"]
  @select << "#{field} AS #{quote_column_name(fname)}"
  @attributes_alias[fname] = field
end

#add_table(use_name, table_name = nil, avoid_alias = true, type = nil, &block) ⇒ Object

‘avoid_alias’ is used when parsing the last element so that it takes the real table name (nodes, not no1). We need this because we can use ‘OR’ between parts and we thus need the same table reference.



123
124
125
126
# File 'lib/query_builder/query.rb', line 123

def add_table(use_name, table_name = nil, avoid_alias = true, type = nil, &block)
  alias_name = get_alias(use_name, table_name, avoid_alias)
  add_alias_to_tables(table_name || use_name, alias_name, type, &block)
end

#default_classObject

Return the default class of resulting objects (usually the base class).



52
53
54
55
56
57
# File 'lib/query_builder/query.rb', line 52

def default_class
  @default_class ||= begin
    klass = @processor_class.main_class
    QueryBuilder.resolve_const(klass)
  end
end

#dupObject

Duplicate query, avoiding sharing some arrays and hash



179
180
181
182
183
184
185
# File 'lib/query_builder/query.rb', line 179

def dup
  other = super
  %w{tables table_alias unique_alias where tables key_value_tables}.each do |k|
    other.send("#{k}=", other.send(k).dup)
  end
  other
end

#filterObject



220
221
222
# File 'lib/query_builder/query.rb', line 220

def filter
  @where.reverse.join(' AND ')
end

#main_tableObject



30
31
32
33
# File 'lib/query_builder/query.rb', line 30

def main_table
  # @main_table is only used in custom queries
  @main_table || processor_class.main_table
end

#master_class(after_class = ActiveRecord::Base) ⇒ Object



41
42
43
44
45
46
47
48
49
# File 'lib/query_builder/query.rb', line 41

def master_class(after_class = ActiveRecord::Base)
  klass = main_class
  klass = klass.first if klass.kind_of?(Array)
  begin
    up = klass.superclass
    return klass if up == after_class
  end while klass = up
  return main_class
end

#needs_join_table(table_name1, type, table_name2, clause, join_name = nil) ⇒ Object

Use this method to add a join to another table (added only once for each join name). nodes JOIN idx_nodes_string AS id1 ON … FIXME: can we remove this ? It seems buggy (JOIN in or clauses)



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/query_builder/query.rb', line 160

def needs_join_table(table_name1, type, table_name2, clause, join_name = nil)
  join_name ||= "#{table_name1}=#{type}=#{table_name2}"
  @needed_join_tables[join_name] ||= {}
  @needed_join_tables[join_name][table] ||= begin
    # define join for this part ('table' = unique for each part)

    # don't add to list of tables, just get unique alias name
    second_table = get_alias(table_name2)

    # create join
    first_table = table(table_name1)

    @join_tables[first_table] ||= []
    @join_tables[first_table] << "#{type} JOIN #{second_table} ON #{clause.gsub('TABLE1',first_table).gsub('TABLE2',second_table)}"
    second_table
  end
end

#quote_column_name(name) ⇒ Object



224
225
226
# File 'lib/query_builder/query.rb', line 224

def quote_column_name(name)
  connection.quote_column_name(name)
end

#rebuild_attributes_hash!Object



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/query_builder/query.rb', line 201

def rebuild_attributes_hash!
  @attributes_alias = {}
  (@select || []).each do |field|
    if field =~ %r{\A(.*)\s+AS\s+(.+)\Z}i
      key, value = $2, $1
      if key =~ /('|"|`)(.*)\1/
        # TODO: is this clean enough unquoting ?
        key = $2
      end
      @attributes_alias[key] = value
    elsif field =~ %r{^(\w+\.|)([^\*]+)$}
      @attributes_alias[$2] = field
    end
  end
  # Force rebuild
  @select_keys = nil
end

#rebuild_tables!Object

Used after setting @tables from custom query.



188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/query_builder/query.rb', line 188

def rebuild_tables!
  @table_alias = {}
  @tables.each do |t|
    if t =~ /\A(.+)\s+AS\s+(.+)\Z/
      base, use_name = $1, $2
    else
      base = use_name = t
    end
    @table_alias[base] ||= []
    @table_alias[base] << use_name
  end
end

#select_keysObject

Return all explicit selected keys For example, sql such as “SELECT form.*, MAX(form.date) AS last_date” would provice ‘last_date’ key.



65
66
67
68
69
70
71
72
73
74
75
# File 'lib/query_builder/query.rb', line 65

def select_keys
  @select_keys ||= begin
    keys = @attributes_alias.keys.compact
    # When rebuilding select_keys, we rebuild @types
    keys.each do |k|
      # Default type is string
      @types[k] ||= :string
    end
    keys
  end
end

#sql(bindings, type = :find) ⇒ Object

Convert the query object into an SQL query.

Parameters

bindings<Binding>

Binding context in which to evaluate bind clauses (query arguments).

type<Symbol>

Type of SQL query (:find or :count)

Returns

NilClass

If the query is not valid and “ignore_warnings” was not set to true during initialize.

String

An SQL query, ready for execution (no more bind variables).

Examples

query.sql(binding)

> “SELECT objects.* FROM objects WHERE objects.project_id = 12489”

query.sql(bindings, :count)

> “SELECT COUNT(*) FROM objects WHERE objects.project_id = 12489”



116
117
118
119
# File 'lib/query_builder/query.rb', line 116

def sql(bindings, type = :find)
  statement, bind_values = build_statement(type)
  statement.gsub('?') { eval_bound_value(bind_values.shift, connection, bindings) }
end

#table(table_name = main_table, index = 0) ⇒ Object



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

def table(table_name = main_table, index = 0)
  @table_alias[table_name] ? @table_alias[table_name][index - 1] : nil
end

#to_s(type = :find) ⇒ Object

Convert query object to a string. This string should then be evaluated.

Parameters

type<Symbol>

Type of query to build (:find or :count).

Returns

NilClass

If the query is not valid and “ignore_warnings” was not set to true during initialize.

String

A string representing the query with its bind parameters.

Examples

query.to_s

> “[%Qobjects.* FROM objects WHERE objects.project_id = ?, project_id]”

DummyQuery.new(“nodes in site”).to_s

> “%Qobjects.* FROM objects”

query.to_s(:count)

> “[%QCOUNT(*) FROM objects WHERE objects.project_id = ?, project_id]”



95
96
97
98
# File 'lib/query_builder/query.rb', line 95

def to_s(type = :find)
  statement, bind_values = build_statement(type)
  bind_values.empty? ? "%Q{#{statement}}" : "[#{[["%Q{#{statement}}"] + bind_values].join(', ')}]"
end