Class: ConceptQL::Scope
- Inherits:
-
Object
- Object
- ConceptQL::Scope
- Defined in:
- lib/conceptql/scope.rb
Overview
Scope coordinates the creation of any common table expressions that might be used when a Recall operator is present in the statement.
Any time an operator is given a label, it becomes a candidate for a Recall operator to reuse the output of that operator somewhere else in the statement.
Scope keeps track of all labeled operators and provides an API for Recall operators to fetch the results/domains from labeled operators.
Constant Summary collapse
- DEFAULT_COLUMNS =
{ person_id: :Bigint, criterion_id: :Bigint, criterion_domain: :String, start_date: :Date, end_date: :Date, source_value: :String }.freeze
- ADDITIONAL_COLUMNS =
{ value_as_number: :Float, value_as_string: :String, value_as_concept_id: :Bigint, unit_source_value: :String, visit_occurrence_id: :Bigint, provenance_type: :Bigint, provider_id: :Bigint, place_of_service_concept_id: :Bigint, range_low: :Float, range_high: :Float, drug_name: :String, drug_amount: :Float, drug_amount_units: :String, days_supply: :Float, quantity: :Bigint }.freeze
- COLUMN_TYPES =
(DEFAULT_COLUMNS.merge(ADDITIONAL_COLUMNS)).freeze
Instance Attribute Summary collapse
-
#annotation ⇒ Object
readonly
Returns the value of attribute annotation.
-
#known_operators ⇒ Object
readonly
Returns the value of attribute known_operators.
-
#opts ⇒ Object
readonly
Returns the value of attribute opts.
-
#person_ids ⇒ Object
Returns the value of attribute person_ids.
-
#query_columns ⇒ Object
readonly
Returns the value of attribute query_columns.
-
#recall_dependencies ⇒ Object
readonly
Returns the value of attribute recall_dependencies.
-
#recall_stack ⇒ Object
readonly
Returns the value of attribute recall_stack.
Instance Method Summary collapse
- #add_counts(key, domain, counts) ⇒ Object
- #add_errors(key, errors) ⇒ Object
- #add_operator(operator) ⇒ Object
- #add_operators(operator) ⇒ Object
- #add_required_columns(op) ⇒ Object
- #add_warnings(key, errors) ⇒ Object
- #ctes ⇒ Object
- #domains(label, db) ⇒ Object
- #duplicate_label?(label) ⇒ Boolean
- #fetch_operator(label) ⇒ Object
- #from(db, label) ⇒ Object
-
#initialize(opts = {}) ⇒ Scope
constructor
A new instance of Scope.
- #nest(op) ⇒ Object
- #nested_recall?(label) ⇒ Boolean
- #sort_ctes(sorted, unsorted, deps) ⇒ Object
- #valid? ⇒ Boolean
- #with_ctes(query, db) ⇒ Object
Constructor Details
#initialize(opts = {}) ⇒ Scope
Returns a new instance of Scope.
46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/conceptql/scope.rb', line 46 def initialize(opts = {}) @known_operators = {} @recall_dependencies = {} @recall_stack = [] @annotation = {} @opts = opts.dup @annotation[:errors] = @errors = {} @annotation[:warnings] = @warnings = {} @annotation[:counts] = @counts = {} @annotation[:operators] = @operators = [] @query_columns = DEFAULT_COLUMNS.keys end |
Instance Attribute Details
#annotation ⇒ Object (readonly)
Returns the value of attribute annotation.
44 45 46 |
# File 'lib/conceptql/scope.rb', line 44 def annotation @annotation end |
#known_operators ⇒ Object (readonly)
Returns the value of attribute known_operators.
44 45 46 |
# File 'lib/conceptql/scope.rb', line 44 def known_operators @known_operators end |
#opts ⇒ Object (readonly)
Returns the value of attribute opts.
44 45 46 |
# File 'lib/conceptql/scope.rb', line 44 def opts @opts end |
#person_ids ⇒ Object
Returns the value of attribute person_ids.
42 43 44 |
# File 'lib/conceptql/scope.rb', line 42 def person_ids @person_ids end |
#query_columns ⇒ Object (readonly)
Returns the value of attribute query_columns.
44 45 46 |
# File 'lib/conceptql/scope.rb', line 44 def query_columns @query_columns end |
#recall_dependencies ⇒ Object (readonly)
Returns the value of attribute recall_dependencies.
44 45 46 |
# File 'lib/conceptql/scope.rb', line 44 def recall_dependencies @recall_dependencies end |
#recall_stack ⇒ Object (readonly)
Returns the value of attribute recall_stack.
44 45 46 |
# File 'lib/conceptql/scope.rb', line 44 def recall_stack @recall_stack end |
Instance Method Details
#add_counts(key, domain, counts) ⇒ Object
67 68 69 70 |
# File 'lib/conceptql/scope.rb', line 67 def add_counts(key, domain, counts) c = @counts[key] ||= {} c[domain] = counts end |
#add_errors(key, errors) ⇒ Object
59 60 61 |
# File 'lib/conceptql/scope.rb', line 59 def add_errors(key, errors) @errors[key] = errors end |
#add_operator(operator) ⇒ Object
123 124 125 |
# File 'lib/conceptql/scope.rb', line 123 def add_operator(operator) known_operators[operator.label] = operator end |
#add_operators(operator) ⇒ Object
72 73 74 75 76 |
# File 'lib/conceptql/scope.rb', line 72 def add_operators(operator) @operators << operator.operator_name @operators.compact! @operators.uniq! end |
#add_required_columns(op) ⇒ Object
78 79 80 |
# File 'lib/conceptql/scope.rb', line 78 def add_required_columns(op) @query_columns |= op.required_columns if op.required_columns end |
#add_warnings(key, errors) ⇒ Object
63 64 65 |
# File 'lib/conceptql/scope.rb', line 63 def add_warnings(key, errors) @warnings[key] = errors end |
#ctes ⇒ Object
199 200 201 |
# File 'lib/conceptql/scope.rb', line 199 def ctes @ctes ||= sort_ctes([], known_operators, recall_dependencies) end |
#domains(label, db) ⇒ Object
153 154 155 156 157 |
# File 'lib/conceptql/scope.rb', line 153 def domains(label, db) fetch_operator(label).domains(db) rescue [:invalid] end |
#duplicate_label?(label) ⇒ Boolean
127 128 129 |
# File 'lib/conceptql/scope.rb', line 127 def duplicate_label?(label) known_operators.keys.map(&:downcase).include?(label.downcase) end |
#fetch_operator(label) ⇒ Object
203 204 205 |
# File 'lib/conceptql/scope.rb', line 203 def fetch_operator(label) known_operators[label] end |
#from(db, label) ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/conceptql/scope.rb', line 135 def from(db, label) ds = db.from(label) if ENV['CONCEPTQL_CHECK_COLUMNS'] # Work around requests for columns by operators. These # would fail because the CTE would not be defined. You # don't want to define the CTE normally, but to allow the # columns to still work, send the columns request to the # underlying operator. op = fetch_operator(label) (class << ds; self; end).send(:define_method, :columns) do (@main_op ||= op.evaluate(db)).columns end end ds end |
#nest(op) ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/conceptql/scope.rb', line 82 def nest(op) add_required_columns(op) return yield unless label = op.is_a?(Operators::Recall) ? op.source : op.label unless label.is_a?(String) op.instance_eval do @errors = [] add_error("invalid label") end return end recall_dependencies[label] ||= [] if nested_recall?(label) op.instance_eval do @errors = [] add_error("nested recall") end return end if duplicate_label?(label) && !op.is_a?(Operators::Recall) op.instance_eval do @errors = [] add_error("duplicate label") end end if last = recall_stack.last recall_dependencies[last] << label end begin recall_stack.push(label) yield ensure recall_stack.pop if recall_stack.last == label end end |
#nested_recall?(label) ⇒ Boolean
131 132 133 |
# File 'lib/conceptql/scope.rb', line 131 def nested_recall?(label) recall_stack.map(&:downcase).include?(label.downcase) end |
#sort_ctes(sorted, unsorted, deps) ⇒ Object
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/conceptql/scope.rb', line 159 def sort_ctes(sorted, unsorted, deps) if unsorted.empty? return sorted end add, unsorted = unsorted.partition do |label, _| deps[label].length == 0 end sorted += add new_deps = {} deps.map do |label, dps| new_deps[label] = dps - sorted.map(&:first) end sort_ctes(sorted, unsorted, new_deps) end |
#valid? ⇒ Boolean
178 179 180 181 182 183 184 185 |
# File 'lib/conceptql/scope.rb', line 178 def valid? recall_dependencies.each_value do |deps| unless (deps - known_operators.keys).empty? return false end end true end |
#with_ctes(query, db) ⇒ Object
187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/conceptql/scope.rb', line 187 def with_ctes(query, db) raise "recall operator use without matching label" unless valid? query = query.from_self ctes.each do |label, operator| query = query.with(label, operator.evaluate(db)) end query end |