class ConceptQL::Operators::CastingOperator

Parent class of all casting operators

Subclasses must implement the following methods:

i_point_at returns a list of domains for which the operator's table of origin has foreign_keys pointing to another table, e.g.: procedure_occurrence has an FK to visit_occurrence, so we'd put :visit_occurrence in the i_point_at array

these_point_at_me is a list of domains for which that domain's table of origin has a FK pointing to the current operator's table of origin, e.g.: procedure_cost has an FK to procedure_occurrence so we'd put :procedure_cost in procedure_occurrence's these_point_at_me array

Also, if a casting operator is passed no streams, it will return all the rows in its table as results.

Public Instance Methods

castables() click to toggle source
# File lib/conceptql/operators/casting_operator.rb, line 39
def castables
  (i_point_at + these_point_at_me)
end
domain() click to toggle source
# File lib/conceptql/operators/casting_operator.rb, line 35
def domain
  my_domain
end
domains(db) click to toggle source
# File lib/conceptql/operators/casting_operator.rb, line 31
def domains(db)
  [domain]
end
query(db) click to toggle source
# File lib/conceptql/operators/casting_operator.rb, line 47
def query(db)
  return db.from(make_table_name(my_domain)) if stream.nil?
  base_query(db, stream.evaluate(db))
end
query_cols() click to toggle source
# File lib/conceptql/operators/casting_operator.rb, line 43
def query_cols
  table_columns(make_table_name(my_domain))
end

Private Instance Methods

base_query(db, stream_query) click to toggle source
# File lib/conceptql/operators/casting_operator.rb, line 54
def base_query(db, stream_query)
  uncastable_domains = stream.domains(db) - castables
  to_me_domains = stream.domains(db) & these_point_at_me
  from_me_domains = stream.domains(db) & i_point_at

  destination_table = make_table_name(my_domain)
  casting_query = db.from(destination_table)
  wheres = []

  unless uncastable_domains.empty?
    # We have a situation where one or more of the incoming streams
    # isn't castable so we'll just return all rows for
    # all people in each uncastable stream
    uncastable_person_ids = db.from(stream_query)
      .where(criterion_domain: uncastable_domains.map(&:to_s))
      .select_group(:person_id)
    wheres << Sequel.expr(person_id: uncastable_person_ids)
  end

  destination_domain_id = make_domain_id(my_domain)

  unless to_me_domains.empty?
    # For each castable domain in the stream, setup a query that
    # casts each domain to a set of IDs, union those IDs and fetch
    # them from the source table
    castable_domain_query = to_me_domains.map do |source_domain|
      source_ids = db.from(stream_query)
        .where(criterion_domain: source_domain.to_s)
        .select_group(:criterion_id)
      source_table = make_table_name(source_domain)
      source_domain_id = make_domain_id(source_domain)

      db.from(source_table)
        .where(source_domain_id => source_ids)
        .select(destination_domain_id)
    end.inject do |union_query, q|
      union_query.union(q, all: true)
    end
    wheres << Sequel.expr(destination_domain_id => castable_domain_query)
  end

  unless from_me_domains.empty?
    from_me_domains.each do |from_me_domain|
      fk_domain_id = make_domain_id(from_me_domain)
      wheres << Sequel.expr(fk_domain_id => db.from(stream_query).where(criterion_domain: from_me_domain.to_s).select_group(:criterion_id))
    end
  end

  casting_query.where(wheres.inject(&:|))
end