Class: Flounder::Query::Select

Inherits:
Base
  • Object
show all
Includes:
Enumerable
Defined in:
lib/flounder/query/select.rb

Overview

A query obtained by calling any of the chain methods on an entity.

Instance Attribute Summary collapse

Attributes inherited from Base

#bind_values, #domain, #engine, #entity

Instance Method Summary collapse

Methods inherited from Base

#kick, #measure, #parse_conditions, #to_sql, #where, #with

Methods included from Helpers::Entity

#convert_to_entity, #entity_like?

Constructor Details

#initialize(domain, from_entity) ⇒ Select

Returns a new instance of Select.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/flounder/query/select.rb', line 30

def initialize domain, from_entity
  super domain, Arel::SelectManager, from_entity
  
  @has_projection = false

  @projection_prefixes = Hash.new
  @default_projection = []

  # by default, joins are made to the entity that you start the query with
  @join_entity = entity

  @relation_entity_map = {}

  add_fields_to_default from_entity

  manager.from from_entity.table
end

Instance Attribute Details

#default_projectionObject (readonly)

All projected fields if no custom projection is made. Fields are encoded so that they can be traced back to the entity that contributed them.



14
15
16
# File 'lib/flounder/query/select.rb', line 14

def default_projection
  @default_projection
end

#has_projectionObject (readonly)

Set to true once #project is used. From that point on, no fields can be added through default projections.



19
20
21
# File 'lib/flounder/query/select.rb', line 19

def has_projection
  @has_projection
end

#join_entityObject (readonly)

Returns the value of attribute join_entity.



25
26
27
# File 'lib/flounder/query/select.rb', line 25

def join_entity
  @join_entity
end

#last_joinObject (readonly)

Returns the value of attribute last_join.



25
26
27
# File 'lib/flounder/query/select.rb', line 25

def last_join
  @last_join
end

#managerObject (readonly)

Arel SqlManager that accumulates this query.



10
11
12
# File 'lib/flounder/query/select.rb', line 10

def manager
  @manager
end

#projection_prefixesObject (readonly)

Each projection has a unique prefix mapping to the entity that uses this prefix during this query.



23
24
25
# File 'lib/flounder/query/select.rb', line 23

def projection_prefixes
  @projection_prefixes
end

#relation_entity_mapObject (readonly)

maps relations that were included in this select to their entities.



28
29
30
# File 'lib/flounder/query/select.rb', line 28

def relation_entity_map
  @relation_entity_map
end

Instance Method Details

#_join(join_node, entity) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/flounder/query/select.rb', line 48

def _join join_node, entity
  entity = convert_to_entity(entity)

  @last_join = entity

  table = entity.table
  manager.join(table, join_node)
  add_fields_to_default(entity)

  self
end

#add_fields_to_default(entity) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/flounder/query/select.rb', line 60

def add_fields_to_default entity
  # Don't add fields through the default method if #project was already
  # used. 
  return if has_projection

  prefix = entity.name.to_s
  table = entity.table
  
  warn "Table alias #{prefix} already used in select; field aliasing will occur!" \
    if projection_prefixes.has_key? prefix

  projection_prefixes[prefix] = entity

  entity.column_names.each do |name|
    default_projection << table[name].as("_#{prefix}_#{name}")
  end
end

#anchorObject



94
95
96
97
# File 'lib/flounder/query/select.rb', line 94

def anchor
  @join_entity = last_join
  self
end

#connect(*connect_spec) ⇒ Object

Follows relationships on the currently anchored entity. This is like an explicit join, but allows to eliminate repetition.

Example:

users.
  connect(:posts => {:comments => :author}).
  where(:author, id: 2)

# roughly aequivalent to 
authors = users.as(:authors, :author)
users.
  join(:posts).on(:id => :user_id).anchor.
  join(:comments).on(:id => :post_id).anchor.
  join(authors).on(:author_id => :id).
  where(authors, id: 2)


185
186
187
188
189
# File 'lib/flounder/query/select.rb', line 185

def connect *connect_spec
  follow_relation_spec(join_entity, connect_spec)

  self
end

#each(&block) ⇒ Object



147
148
149
# File 'lib/flounder/query/select.rb', line 147

def each &block
  all.each(&block)
end

#firstObject



152
153
154
155
156
# File 'lib/flounder/query/select.rb', line 152

def first
  manager.take(1)

  all.first
end

#group_by(*field_list) ⇒ Object



115
116
117
118
# File 'lib/flounder/query/select.rb', line 115

def group_by *field_list
  manager.group *map_to_arel(field_list)
  self
end

#having(*conditions) ⇒ Object



119
120
121
# File 'lib/flounder/query/select.rb', line 119

def having *conditions
  parse_conditions(*conditions) { |bit| manager.having(bit) }
end

#hoistObject



98
99
100
101
# File 'lib/flounder/query/select.rb', line 98

def hoist
  @join_entity = entity
  self
end

#inner_join(*args) ⇒ Object Also known as: join



78
79
80
# File 'lib/flounder/query/select.rb', line 78

def inner_join *args
  _join(Arel::Nodes::InnerJoin, *args)
end

#limit(n) ⇒ Object



136
137
138
139
# File 'lib/flounder/query/select.rb', line 136

def limit n
  manager.take n
  self
end

#offset(n) ⇒ Object



140
141
142
143
# File 'lib/flounder/query/select.rb', line 140

def offset n
  manager.skip n
  self
end

#on(join_conditions) ⇒ Object



87
88
89
90
91
92
# File 'lib/flounder/query/select.rb', line 87

def on join_conditions
  manager.on(
    *join_conditions.map { |(k, v)| transform_tuple(
      join_entity, k, join_field(v)) })
  self
end

#order_by(*field_list) ⇒ Object

Orders by a list of field references.

Note: Replaces previous order_by.



127
128
129
130
131
132
133
134
# File 'lib/flounder/query/select.rb', line 127

def order_by *field_list
  manager.orders.clear
  field_list.each do |field|
    field = transform field
    manager.order field
  end
  self
end

#outer_join(*args) ⇒ Object



83
84
85
# File 'lib/flounder/query/select.rb', line 83

def outer_join *args
  _join(Arel::Nodes::OuterJoin, *args)
end

#project(*field_list) ⇒ Object

Adds a field to the projection clause of the SQL statement (the part between SELECT and FROM). Projection of ‘*’ is the default, so you can omit this call entirely if you want that.



107
108
109
110
111
112
113
# File 'lib/flounder/query/select.rb', line 107

def project *field_list
  @has_projection = true
  @default_projection = []

  manager.project *map_to_arel(field_list)
  self
end

#sizeObject

Executes a ‘select count(*)` query on the database and returns the result. If you want to avoid modifying your query (!) using this, dup it or execute it first and then call #size on the resulting array.



162
163
164
165
166
167
# File 'lib/flounder/query/select.rb', line 162

def size
  manager.projections = []
  project 'count(*)::int as count'

  all.first.count
end