Class: Praxis::Mapper::Query::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/praxis-mapper/query/base.rb

Overview

Abstract base class for assembling read queries for a data store. May be implemented for SQL, CQL, etc. Collects query statistics.

See Also:

  • lib/support/memory_querylib/support/memory_query.rb

Direct Known Subclasses

Sequel, Sql, Support::MemoryQuery

Constant Summary collapse

MULTI_GET_BATCH_SIZE =
4_096

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(identity_map, model, &block) ⇒ Base

Sets up a read query.

Parameters:



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/praxis-mapper/query/base.rb', line 20

def initialize(identity_map, model, &block)

  @identity_map = identity_map
  @model = model

  @select = nil

  @where = nil

  @limit = nil
  @track = Set.new
  @load = Set.new
  @contexts = Set.new

  @statistics = Hash.new(0) # general-purpose hash

  if (selector = identity_map.selectors[model])
    self.apply_selector(selector)
  end

  if block_given?
    self.instance_eval(&block)
  end

end

Instance Attribute Details

#contextsObject (readonly)

Returns the value of attribute contexts.



12
13
14
# File 'lib/praxis-mapper/query/base.rb', line 12

def contexts
  @contexts
end

#identity_mapObject (readonly)

Returns the value of attribute identity_map.



12
13
14
# File 'lib/praxis-mapper/query/base.rb', line 12

def identity_map
  @identity_map
end

#modelObject (readonly)

Returns the value of attribute model.



12
13
14
# File 'lib/praxis-mapper/query/base.rb', line 12

def model
  @model
end

#statisticsObject (readonly)

Returns the value of attribute statistics.



12
13
14
# File 'lib/praxis-mapper/query/base.rb', line 12

def statistics
  @statistics
end

#where(value = nil) ⇒ Object

Gets or sets an SQL-like ‘WHERE’ clause to this query.

Examples:

where(“deployment_id=2”)

Parameters:

  • value (String) (defaults to: nil)

    a ‘WHERE’ clause



108
109
110
111
112
113
114
# File 'lib/praxis-mapper/query/base.rb', line 108

def where(value=nil)
  if value
    @where = value
  else
    return @where
  end
end

Instance Method Details

#_executeObject

Subclasses Must Implement



238
239
240
# File 'lib/praxis-mapper/query/base.rb', line 238

def _execute
  raise "subclass responsibility"
end

#_multi_get(identity, values) ⇒ Object

Subclasses Must Implement



233
234
235
# File 'lib/praxis-mapper/query/base.rb', line 233

def _multi_get(identity, values)
  raise "subclass responsibility"
end

#apply_selector(selector) ⇒ Object



46
47
48
49
50
51
52
53
54
# File 'lib/praxis-mapper/query/base.rb', line 46

def apply_selector(selector)
  if selector[:select]
    self.select(*selector[:select])
  end

  if selector[:track]
    self.track(*selector[:track])
  end
end

#connectionObject

Returns handle to configured data store.

Returns:

  • handle to configured data store



57
58
59
# File 'lib/praxis-mapper/query/base.rb', line 57

def connection
  identity_map.connection(model.repository_name)
end

#context(name = nil) ⇒ Object



157
158
159
160
161
162
163
164
165
# File 'lib/praxis-mapper/query/base.rb', line 157

def context(name=nil)
  @contexts << name
  spec = model.contexts.fetch(name) do
    raise "context #{name.inspect} not found for #{model}"
  end

  select(*spec[:select])
  track(*spec[:track])
end

#default_selectObject



97
98
99
100
101
# File 'lib/praxis-mapper/query/base.rb', line 97

def default_select
  model.identities.each_with_object({}).each do |identity, hash|
    hash[identity] = nil
  end
end

#describeObject

Subclasses Must Implement the sql or “sql-like” representation of the query



244
245
246
# File 'lib/praxis-mapper/query/base.rb', line 244

def describe
  raise "subclass responsibility"
end

#executeArray

Executes assembled read query and returns all matching records.

Returns:

  • (Array)

    list of matching records, wrapped as models



212
213
214
215
216
217
218
219
220
221
222
# File 'lib/praxis-mapper/query/base.rb', line 212

def execute
  if self.frozen?
    raise TypeError.new "can not reuse a frozen query"
  end
  statistics[:execute] += 1

  rows = _execute

  statistics[:records_loaded] += rows.size
  to_records(rows)
end

#limit(value = nil) ⇒ Object

Gets or sets an SQL-like ‘LIMIT’ clause to this query.

Examples:

limit(“LIMIT 10 OFFSET 20”)

Parameters:

  • value (String) (defaults to: nil)

    a ‘LIMIT’ clause



121
122
123
124
125
126
127
# File 'lib/praxis-mapper/query/base.rb', line 121

def limit(value=nil)
  if value
    @limit = value
  else
    return @limit
  end
end

#load(*values, &block) ⇒ Object

Parameters:

  • *values (Array)

    a list of associations to load immediately after this



144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/praxis-mapper/query/base.rb', line 144

def load(*values, &block)
  if values.any?
    if block_given?
      raise "block and multiple values not supported" if values.size > 1
      @load << [values.first, block]
    else
      @load.merge(values)
    end
  else
    return @load
  end
end

#multi_get(identity, values, select: nil, raw: false) ⇒ Array

Executes multi-get read query and returns all matching records.

Parameters:

  • identity (Symbol|Array)

    a simple or composite key for this model

  • values (Array)

    list of identifier values (ideally a sorted set)

  • select (Array) (defaults to: nil)

    list of field names to select

  • raw (Boolean) (defaults to: false)

    return raw hashes instead of models (default false)

Returns:

  • (Array)

    list of matching records, wrapped as models



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/praxis-mapper/query/base.rb', line 184

def multi_get(identity, values, select: nil, raw: false)
  if self.frozen?
    raise TypeError.new "can not reuse a frozen query"
  end

  statistics[:multi_get] += 1

  rows = []

  original_select = @select
  self.select(*select.flatten.uniq) if select

  values.each_slice(MULTI_GET_BATCH_SIZE) do |batch|
    rows += _multi_get(identity, batch)
  end

  statistics[:records_loaded] += rows.size

  return rows if raw
  to_records(rows)
ensure
  @select = original_select unless self.frozen?
end

#select(*fields) ⇒ Hash

Gets or sets an SQL-like ‘SELECT’ clause to this query. TODO: fix any specs or code that uses alias

Examples:

select(:account_id, “user_id”, => :created_at)

Parameters:

  • *fields (Array)

    list of fields, of type Symbol, String, or Hash

Returns:

  • (Hash)

    current list of fields



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/praxis-mapper/query/base.rb', line 68

def select(*fields)
  if fields.any?
    return @select if @select == true

    if @select.nil?
      @select = default_select
    end
    fields.each do |field|
      case field
      when Symbol, String
        if field == :* || field == "*"
          @select = true
          break
        else
          @select[field] = nil
        end
      when Hash
        field.each do |alias_name, column_name|
          @select[alias_name] = column_name
        end
      else
        raise "unknown field type: #{field.class.name}"
      end
    end
  else
    return @select
  end
end

#to_records(rows) ⇒ Object



224
225
226
227
228
229
230
# File 'lib/praxis-mapper/query/base.rb', line 224

def to_records(rows)
  rows.collect do |row|
    m = model.new(row)
    m._query = self
    m
  end
end

#track(*values, &block) ⇒ Object

Parameters:

  • *values (Array)

    a list of associations to track



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

def track(*values, &block)
  if values.any?
    if block_given?
      raise "block and multiple values not supported" if values.size > 1
      @track << [values.first, block]
    else
      @track.merge(values)
    end
  else
    return @track
  end
end

#tracked_associationsArray

Returns a list of associated models.

Returns:

  • (Array)

    a list of associated models



169
170
171
172
173
174
175
# File 'lib/praxis-mapper/query/base.rb', line 169

def tracked_associations
  track.collect do |(name, _)|
    model.associations.fetch(name) do
      raise "association #{name.inspect} not found for #{model}"
    end
  end.uniq
end