Class: ConceptQL::Operators::Operator

Inherits:
Object
  • Object
show all
Extended by:
Metadatable, Forwardable
Defined in:
lib/conceptql/operators/operator.rb

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Metadatable

allows_many_upstreams, allows_one_upstream, argument, auto_label, basic_type, category, derive_metadata_from_validations, desc, get_desc, humanized_class_name, inherited, just_class_name, no_desc, option, predominant_domains, pref_name, preferred_name, reset_categories, standard_description, to_metadata, validate_at_least_one_upstream_to_metadata, validate_at_most_one_upstream_to_metadata, validate_no_arguments_to_metadata, validate_no_upstreams_to_metadata, validate_one_upstream_to_metadata, validate_required_options_to_metadata, warn_about_missing_metadata

Constructor Details

#initialize(nodifier, *args) ⇒ Operator

Returns a new instance of Operator.



153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/conceptql/operators/operator.rb', line 153

def initialize(nodifier, *args)
  @nodifier = nodifier
  @options = {}
  while args.last.is_a?(Hash)
    @options = @options.merge(args.extract_options!.deep_rekey)
  end
  args.reject!{|arg| arg.nil? || arg == ''}
  @upstreams, @arguments = args.partition { |arg| arg.is_a?(Array) || arg.is_a?(Operator) }
  @values = args

  scope.nest(self) do
    create_upstreams
  end
end

Class Attribute Details

.codes_regexpObject (readonly)

Returns the value of attribute codes_regexp.



81
82
83
# File 'lib/conceptql/operators/operator.rb', line 81

def codes_regexp
  @codes_regexp
end

.required_columnsObject (readonly)

Returns the value of attribute required_columns.



81
82
83
# File 'lib/conceptql/operators/operator.rb', line 81

def required_columns
  @required_columns
end

.validationsObject (readonly)

Returns the value of attribute validations.



81
82
83
# File 'lib/conceptql/operators/operator.rb', line 81

def validations
  @validations
end

Instance Attribute Details

#argumentsObject (readonly)

Returns the value of attribute arguments.



74
75
76
# File 'lib/conceptql/operators/operator.rb', line 74

def arguments
  @arguments
end

#errorsObject (readonly)

Returns the value of attribute errors.



313
314
315
# File 'lib/conceptql/operators/operator.rb', line 313

def errors
  @errors
end

#nodifierObject (readonly)

Returns the value of attribute nodifier.



74
75
76
# File 'lib/conceptql/operators/operator.rb', line 74

def nodifier
  @nodifier
end

#optionsObject (readonly)

Returns the value of attribute options.



74
75
76
# File 'lib/conceptql/operators/operator.rb', line 74

def options
  @options
end

#upstreamsObject (readonly)

Returns the value of attribute upstreams.



74
75
76
# File 'lib/conceptql/operators/operator.rb', line 74

def upstreams
  @upstreams
end

#valuesObject (readonly)

Returns the value of attribute values.



74
75
76
# File 'lib/conceptql/operators/operator.rb', line 74

def values
  @values
end

Class Method Details

.codes_should_match(format) ⇒ Object



128
129
130
131
# File 'lib/conceptql/operators/operator.rb', line 128

def codes_should_match(format)
  @codes_regexp = format
  validate_codes_match
end

.default_query_columnsObject



96
97
98
99
100
# File 'lib/conceptql/operators/operator.rb', line 96

def default_query_columns
  define_method(:query_cols) do
    dynamic_columns
  end
end

.inherited(subclass) ⇒ Object



133
134
135
136
137
# File 'lib/conceptql/operators/operator.rb', line 133

def inherited(subclass)
  super
  subclass.instance_variable_set(:@validations, validations.dup)
  subclass.instance_variable_set(:@codes_regexp, codes_regexp.dup) if codes_regexp
end

.newObject



139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/conceptql/operators/operator.rb', line 139

def new(*)
  operator = super

  # If operator has a label, replace it with a recall so all references
  # to it use the same code.
  if operator.label && !operator.errors
    operator.scope.add_operator(operator)
    operator = Operators::Recall.new(operator.nodifier, operator.label, replaced: true)
  end

  operator
end

.query_columns(*tables) ⇒ Object



90
91
92
93
94
# File 'lib/conceptql/operators/operator.rb', line 90

def query_columns(*tables)
  define_method(:query_cols) do
    table_columns(*tables)
  end
end

.register(file, *data_models) ⇒ Object



83
84
85
86
87
88
# File 'lib/conceptql/operators/operator.rb', line 83

def register(file, *data_models)
  data_models = OPERATORS.keys if data_models.empty?
  data_models.each do |dm|
    OPERATORS[dm][File.basename(file).sub(/\.rb\z/, '')] = self
  end
end

.require_column(column) ⇒ Object



102
103
104
105
# File 'lib/conceptql/operators/operator.rb', line 102

def require_column(column)
  @required_columns ||= []
  @required_columns << column
end

Instance Method Details

#annotate(db, opts = {}) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/conceptql/operators/operator.rb', line 188

def annotate(db, opts = {})
  return @annotation if defined?(@annotation)

  scope_key = options[:id]||self.class.just_class_name.underscore
  annotation = {}
  counts = (annotation[:counts] ||= {})
   = {:annotation=>annotation}
  if name = self.class.preferred_name
    [:name] = name
  end
  res = [operator_name, *annotate_values(db, opts)]

  if upstreams_valid?(db, opts) && scope.valid? && include_counts?(db, opts)
    scope.with_ctes(evaluate(db), db)
      .from_self
      .select_group(:criterion_domain)
      .select_append{count{}.*.as(:rows)}
      .select_append{count(:person_id).distinct.as(:n)}
      .each do |h|
        counts[h.delete(:criterion_domain).to_sym] = h
      end
  elsif !errors.empty?
    annotation[:errors] = errors
    scope.add_errors(scope_key, errors)
  end
  scope.add_operators(self)
  domains(db).each do |domain|
    cur_counts = counts[domain] ||= {:rows=>0, :n=>0}
    scope.add_counts(scope_key, domain, cur_counts)
  end

  if defined?(@warnings) && !warnings.empty?
    annotation[:warnings] = warnings
    scope.add_warnings(scope_key, warnings)
  end

  if res.last.is_a?(Hash)
    res.last.merge!()
  else
    res << 
  end

  @annotation = res
end

#cast_column(column, value = nil) ⇒ Object



339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/conceptql/operators/operator.rb', line 339

def cast_column(column, value = nil)
  type = Scope::COLUMN_TYPES.fetch(column)
  case type
  when String, :String
    Sequel.cast_string(value).as(column)
  when Date, :Date
    Sequel.cast(value, type).as(column)
  when Float, :Bigint, :Float
    Sequel.cast_numeric(value, type).as(column)
  else
    raise "Unexpected type: '#{type.inspect}' for column: '#{column}'"
  end
end

#code_list(db) ⇒ Object



233
234
235
236
237
238
# File 'lib/conceptql/operators/operator.rb', line 233

def code_list(db)
  code_lists = @upstreams.map do | upstream_op |
    upstream_op.code_list(db)
  end
  code_lists.flatten(1)
end

#columns(query, local_domain) ⇒ Object



291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/conceptql/operators/operator.rb', line 291

def columns(query, local_domain)
  criterion_domain = :criterion_domain

  if local_domain
    criterion_domain = cast_column(:criterion_domain, local_domain.to_s)
  end

  columns = [:person_id,
              domain_id(local_domain),
              criterion_domain]
  columns += date_columns(query, local_domain)
  columns += [ source_value(query, local_domain) ]
  columns += additional_columns(query, local_domain)
end

#create_upstreamsObject



168
169
170
# File 'lib/conceptql/operators/operator.rb', line 168

def create_upstreams
  @upstreams.map!{|stmt| to_op(stmt)}
end

#data_modelObject



331
332
333
# File 'lib/conceptql/operators/operator.rb', line 331

def data_model
  nodifier.data_model
end

#database_typeObject



335
336
337
# File 'lib/conceptql/operators/operator.rb', line 335

def database_type
  nodifier.database_type
end

#domains(db) ⇒ Object



278
279
280
# File 'lib/conceptql/operators/operator.rb', line 278

def domains(db)
  @domains ||= determine_domains(db)
end

#dup_values(args) ⇒ Object



240
241
242
# File 'lib/conceptql/operators/operator.rb', line 240

def dup_values(args)
  self.class.new(nodifier, *args)
end

#dynamic_columnsObject



184
185
186
# File 'lib/conceptql/operators/operator.rb', line 184

def dynamic_columns
  scope.query_columns
end

#evaluate(db) ⇒ Object



248
249
250
# File 'lib/conceptql/operators/operator.rb', line 248

def evaluate(db)
  select_it(query(db))
end

#inspectObject



244
245
246
# File 'lib/conceptql/operators/operator.rb', line 244

def inspect
  "<##{self.class} upstreams=[#{upstreams.map(&:inspect).join(', ')}] arguments=[#{arguments.map(&:inspect).join(', ')}]>"
end

#labelObject



306
307
308
309
310
311
# File 'lib/conceptql/operators/operator.rb', line 306

def label
  @label ||= begin
    options.delete(:label) if options[:label] && options[:label].to_s.strip.empty?
    options[:label].respond_to?(:strip) ? options[:label].strip : options[:label]
  end
end

#operator_nameObject



176
177
178
# File 'lib/conceptql/operators/operator.rb', line 176

def operator_name
  self.class.just_class_name.underscore
end

#optimizedObject



256
257
258
# File 'lib/conceptql/operators/operator.rb', line 256

def optimized
  dup_values(values.map{|x| x.is_a?(Operator) ? x.optimized : x})
end

#required_columnsObject



180
181
182
# File 'lib/conceptql/operators/operator.rb', line 180

def required_columns
  self.class.required_columns
end

#scopeObject



327
328
329
# File 'lib/conceptql/operators/operator.rb', line 327

def scope
  nodifier.scope
end

#select_it(query, specific_domain = nil) ⇒ Object



264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/conceptql/operators/operator.rb', line 264

def select_it(query, specific_domain = nil)
  if specific_domain.nil? && respond_to?(:domain) && TABLE_COLUMNS.keys.include?(domain)
    specific_domain = domain
  end

  q = setup_select(query, specific_domain)

  if scope && scope.person_ids && upstreams.empty?
    q = q.where(person_id: scope.person_ids).from_self
  end

  q
end

#setup_select(query, local_domain = nil) ⇒ Object



286
287
288
289
# File 'lib/conceptql/operators/operator.rb', line 286

def setup_select(query, local_domain = nil)
  query = modify_query(query, local_domain)
  query.select(*columns(query, local_domain))
end

#sql(db) ⇒ Object



252
253
254
# File 'lib/conceptql/operators/operator.rb', line 252

def sql(db)
  evaluate(db).sql
end

#streamObject



282
283
284
# File 'lib/conceptql/operators/operator.rb', line 282

def stream
  @stream ||= upstreams.first
end

#to_op(stmt) ⇒ Object



172
173
174
# File 'lib/conceptql/operators/operator.rb', line 172

def to_op(stmt)
  stmt.is_a?(Operator) ? stmt : nodifier.create(*stmt)
end

#unionable?(other) ⇒ Boolean

Returns:

  • (Boolean)


260
261
262
# File 'lib/conceptql/operators/operator.rb', line 260

def unionable?(other)
  false
end

#upstreams_valid?(db, opts = {}) ⇒ Boolean

Returns:

  • (Boolean)


323
324
325
# File 'lib/conceptql/operators/operator.rb', line 323

def upstreams_valid?(db, opts = {})
    valid?(db, opts) && upstreams.all?{|u| u.upstreams_valid?(db, opts)}
end

#valid?(db, opts = {}) ⇒ Boolean

Returns:

  • (Boolean)


315
316
317
318
319
320
321
# File 'lib/conceptql/operators/operator.rb', line 315

def valid?(db, opts = {})
  return @errors.empty? if defined?(@errors)
  @errors = []
  @warnings = []
  validate(db, opts)
  errors.empty?
end