conceptql 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (196) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +53 -1
  4. data/Guardfile +52 -24
  5. data/conceptql.gemspec +5 -3
  6. data/doc/spec.md +2 -2
  7. data/lib/conceptql/behaviors/debuggable.rb +70 -0
  8. data/lib/conceptql/behaviors/dottable.rb +28 -14
  9. data/lib/conceptql/behaviors/metadatable.rb +97 -0
  10. data/lib/conceptql/behaviors/preppable.rb +20 -0
  11. data/lib/conceptql/cli.rb +31 -5
  12. data/lib/conceptql/converter.rb +65 -0
  13. data/lib/conceptql/debugger.rb +48 -0
  14. data/lib/conceptql/graph.rb +14 -13
  15. data/lib/conceptql/graph_nodifier.rb +49 -17
  16. data/lib/conceptql/nodifier.rb +31 -6
  17. data/lib/conceptql/operators/after.rb +25 -0
  18. data/lib/conceptql/operators/any_overlap.rb +15 -0
  19. data/lib/conceptql/operators/before.rb +21 -0
  20. data/lib/conceptql/{nodes/binary_operator_node.rb → operators/binary_operator_operator.rb} +12 -8
  21. data/lib/conceptql/{nodes/casting_node.rb → operators/casting_operator.rb} +8 -7
  22. data/lib/conceptql/{nodes → operators}/complement.rb +14 -11
  23. data/lib/conceptql/operators/concept.rb +70 -0
  24. data/lib/conceptql/{nodes → operators}/condition_type.rb +13 -5
  25. data/lib/conceptql/operators/contains.rb +17 -0
  26. data/lib/conceptql/operators/count.rb +26 -0
  27. data/lib/conceptql/operators/cpt.rb +25 -0
  28. data/lib/conceptql/{nodes → operators}/date_range.rb +11 -6
  29. data/lib/conceptql/operators/death.rb +23 -0
  30. data/lib/conceptql/operators/drug_type_concept.rb +21 -0
  31. data/lib/conceptql/{nodes → operators}/during.rb +9 -3
  32. data/lib/conceptql/operators/equal.rb +13 -0
  33. data/lib/conceptql/operators/except.rb +28 -0
  34. data/lib/conceptql/operators/filter.rb +21 -0
  35. data/lib/conceptql/{nodes → operators}/first.rb +7 -5
  36. data/lib/conceptql/{nodes → operators}/from.rb +2 -2
  37. data/lib/conceptql/operators/from_seer_visits.rb +23 -0
  38. data/lib/conceptql/{nodes → operators}/gender.rb +6 -6
  39. data/lib/conceptql/operators/hcpcs.rb +25 -0
  40. data/lib/conceptql/operators/icd10.rb +28 -0
  41. data/lib/conceptql/operators/icd9.rb +28 -0
  42. data/lib/conceptql/operators/icd9_procedure.rb +25 -0
  43. data/lib/conceptql/{nodes → operators}/intersect.rb +7 -3
  44. data/lib/conceptql/{nodes → operators}/last.rb +7 -5
  45. data/lib/conceptql/operators/loinc.rb +25 -0
  46. data/lib/conceptql/operators/medcode.rb +28 -0
  47. data/lib/conceptql/operators/medcode_procedure.rb +27 -0
  48. data/lib/conceptql/operators/ndc.rb +29 -0
  49. data/lib/conceptql/operators/numeric.rb +54 -0
  50. data/lib/conceptql/operators/observation_by_enttype.rb +29 -0
  51. data/lib/conceptql/operators/observation_period.rb +30 -0
  52. data/lib/conceptql/operators/occurrence.rb +70 -0
  53. data/lib/conceptql/operators/one_in_two_out.rb +62 -0
  54. data/lib/conceptql/{nodes/node.rb → operators/operator.rb} +84 -52
  55. data/lib/conceptql/operators/overlapped_by.rb +23 -0
  56. data/lib/conceptql/operators/overlaps.rb +19 -0
  57. data/lib/conceptql/operators/pass_thru.rb +11 -0
  58. data/lib/conceptql/{nodes → operators}/person.rb +8 -4
  59. data/lib/conceptql/operators/person_filter.rb +13 -0
  60. data/lib/conceptql/{nodes → operators}/place_of_service_code.rb +7 -7
  61. data/lib/conceptql/operators/procedure_occurrence.rb +25 -0
  62. data/lib/conceptql/operators/prodcode.rb +29 -0
  63. data/lib/conceptql/{nodes → operators}/race.rb +7 -7
  64. data/lib/conceptql/operators/recall.rb +38 -0
  65. data/lib/conceptql/operators/rxnorm.rb +24 -0
  66. data/lib/conceptql/operators/snomed.rb +24 -0
  67. data/lib/conceptql/operators/snomed_condition.rb +26 -0
  68. data/lib/conceptql/{nodes/source_vocabulary_node.rb → operators/source_vocabulary_operator.rb} +7 -4
  69. data/lib/conceptql/{nodes/standard_vocabulary_node.rb → operators/standard_vocabulary_operator.rb} +6 -4
  70. data/lib/conceptql/{nodes → operators}/started_by.rb +9 -3
  71. data/lib/conceptql/{nodes → operators}/sum.rb +9 -4
  72. data/lib/conceptql/{nodes/temporal_node.rb → operators/temporal_operator.rb} +7 -4
  73. data/lib/conceptql/{nodes → operators}/time_window.rb +19 -7
  74. data/lib/conceptql/operators/to_seer_visits.rb +24 -0
  75. data/lib/conceptql/operators/trim_date_end.rb +55 -0
  76. data/lib/conceptql/operators/trim_date_start.rb +56 -0
  77. data/lib/conceptql/{nodes → operators}/union.rb +6 -2
  78. data/lib/conceptql/operators/visit.rb +15 -0
  79. data/lib/conceptql/{nodes → operators}/visit_occurrence.rb +7 -3
  80. data/lib/conceptql/query.rb +19 -17
  81. data/lib/conceptql/scope.rb +69 -0
  82. data/lib/conceptql/tree.rb +33 -18
  83. data/lib/conceptql/utils/temp_table.rb +72 -0
  84. data/lib/conceptql/version.rb +1 -1
  85. data/spec/conceptql/behaviors/dottable_spec.rb +39 -51
  86. data/spec/conceptql/converter_spec.rb +51 -0
  87. data/spec/conceptql/date_adjuster_spec.rb +15 -15
  88. data/spec/conceptql/operators/after_spec.rb +16 -0
  89. data/spec/conceptql/operators/before_spec.rb +16 -0
  90. data/spec/conceptql/{nodes/casting_node_spec.rb → operators/casting_operator_spec.rb} +16 -20
  91. data/spec/conceptql/operators/complement_spec.rb +15 -0
  92. data/spec/conceptql/operators/concept_spec.rb +40 -0
  93. data/spec/conceptql/{nodes → operators}/condition_type_spec.rb +39 -24
  94. data/spec/conceptql/operators/contains_spec.rb +19 -0
  95. data/spec/conceptql/operators/cpt_spec.rb +29 -0
  96. data/spec/conceptql/operators/date_range_spec.rb +33 -0
  97. data/spec/conceptql/operators/death_spec.rb +10 -0
  98. data/spec/conceptql/operators/during_spec.rb +30 -0
  99. data/spec/conceptql/operators/except_spec.rb +15 -0
  100. data/spec/conceptql/operators/first_spec.rb +35 -0
  101. data/spec/conceptql/operators/from_spec.rb +13 -0
  102. data/spec/conceptql/operators/gender_spec.rb +27 -0
  103. data/spec/conceptql/operators/hcpcs_spec.rb +29 -0
  104. data/spec/conceptql/operators/icd10_spec.rb +34 -0
  105. data/spec/conceptql/operators/icd9_procedure_spec.rb +29 -0
  106. data/spec/conceptql/operators/icd9_spec.rb +34 -0
  107. data/spec/conceptql/operators/intersect_spec.rb +28 -0
  108. data/spec/conceptql/operators/last_spec.rb +36 -0
  109. data/spec/conceptql/operators/loinc_spec.rb +29 -0
  110. data/spec/conceptql/operators/medcode_procedure_spec.rb +34 -0
  111. data/spec/conceptql/operators/medcode_spec.rb +34 -0
  112. data/spec/conceptql/operators/observation_period_spec.rb +10 -0
  113. data/spec/conceptql/operators/occurrence_spec.rb +87 -0
  114. data/spec/conceptql/operators/overlapped_by_spec.rb +32 -0
  115. data/spec/conceptql/operators/overlaps_spec.rb +21 -0
  116. data/spec/conceptql/operators/person_filter_spec.rb +15 -0
  117. data/spec/conceptql/operators/person_spec.rb +10 -0
  118. data/spec/conceptql/{nodes → operators}/place_of_service_code_spec.rb +6 -8
  119. data/spec/conceptql/operators/procedure_occurrence_spec.rb +10 -0
  120. data/spec/conceptql/operators/prodcode_spec.rb +35 -0
  121. data/spec/conceptql/operators/query_double.rb +20 -0
  122. data/spec/conceptql/operators/query_double_spec.rb +7 -0
  123. data/spec/conceptql/operators/race_spec.rb +21 -0
  124. data/spec/conceptql/operators/rxnorm_spec.rb +29 -0
  125. data/spec/conceptql/operators/snomed_spec.rb +29 -0
  126. data/spec/conceptql/operators/source_vocabulary_operator_spec.rb +35 -0
  127. data/spec/conceptql/operators/standard_vocabulary_operator_spec.rb +35 -0
  128. data/spec/conceptql/operators/started_by_spec.rb +22 -0
  129. data/spec/conceptql/{nodes/temporal_node_spec.rb → operators/temporal_operator_spec.rb} +11 -17
  130. data/spec/conceptql/operators/time_window_spec.rb +77 -0
  131. data/spec/conceptql/operators/union_spec.rb +21 -0
  132. data/spec/conceptql/operators/visit_occurrence_spec.rb +10 -0
  133. data/spec/conceptql/query_spec.rb +10 -9
  134. data/spec/conceptql/tree_spec.rb +24 -28
  135. data/spec/doubles/stream_for_casting_double.rb +1 -1
  136. data/spec/doubles/stream_for_occurrence_double.rb +1 -1
  137. data/spec/doubles/stream_for_temporal_double.rb +1 -1
  138. data/spec/spec_helper.rb +74 -58
  139. metadata +202 -133
  140. data/lib/conceptql/nodes/after.rb +0 -12
  141. data/lib/conceptql/nodes/before.rb +0 -11
  142. data/lib/conceptql/nodes/concept.rb +0 -38
  143. data/lib/conceptql/nodes/count.rb +0 -23
  144. data/lib/conceptql/nodes/cpt.rb +0 -20
  145. data/lib/conceptql/nodes/death.rb +0 -19
  146. data/lib/conceptql/nodes/define.rb +0 -96
  147. data/lib/conceptql/nodes/drug_type_concept.rb +0 -18
  148. data/lib/conceptql/nodes/equal.rb +0 -11
  149. data/lib/conceptql/nodes/except.rb +0 -11
  150. data/lib/conceptql/nodes/hcpcs.rb +0 -20
  151. data/lib/conceptql/nodes/icd10.rb +0 -23
  152. data/lib/conceptql/nodes/icd9.rb +0 -23
  153. data/lib/conceptql/nodes/icd9_procedure.rb +0 -20
  154. data/lib/conceptql/nodes/loinc.rb +0 -20
  155. data/lib/conceptql/nodes/numeric.rb +0 -40
  156. data/lib/conceptql/nodes/occurrence.rb +0 -49
  157. data/lib/conceptql/nodes/pass_thru.rb +0 -11
  158. data/lib/conceptql/nodes/person_filter.rb +0 -12
  159. data/lib/conceptql/nodes/procedure_occurrence.rb +0 -21
  160. data/lib/conceptql/nodes/recall.rb +0 -50
  161. data/lib/conceptql/nodes/rxnorm.rb +0 -20
  162. data/lib/conceptql/nodes/snomed.rb +0 -19
  163. data/lib/conceptql/nodes/visit.rb +0 -11
  164. data/spec/conceptql/nodes/after_spec.rb +0 -18
  165. data/spec/conceptql/nodes/before_spec.rb +0 -18
  166. data/spec/conceptql/nodes/complement_spec.rb +0 -15
  167. data/spec/conceptql/nodes/concept_spec.rb +0 -34
  168. data/spec/conceptql/nodes/cpt_spec.rb +0 -31
  169. data/spec/conceptql/nodes/date_range_spec.rb +0 -35
  170. data/spec/conceptql/nodes/death_spec.rb +0 -12
  171. data/spec/conceptql/nodes/during_spec.rb +0 -32
  172. data/spec/conceptql/nodes/except_spec.rb +0 -18
  173. data/spec/conceptql/nodes/first_spec.rb +0 -37
  174. data/spec/conceptql/nodes/from_spec.rb +0 -15
  175. data/spec/conceptql/nodes/gender_spec.rb +0 -29
  176. data/spec/conceptql/nodes/hcpcs_spec.rb +0 -31
  177. data/spec/conceptql/nodes/icd10_spec.rb +0 -36
  178. data/spec/conceptql/nodes/icd9_procedure_spec.rb +0 -31
  179. data/spec/conceptql/nodes/icd9_spec.rb +0 -36
  180. data/spec/conceptql/nodes/intersect_spec.rb +0 -33
  181. data/spec/conceptql/nodes/last_spec.rb +0 -38
  182. data/spec/conceptql/nodes/loinc_spec.rb +0 -31
  183. data/spec/conceptql/nodes/occurrence_spec.rb +0 -89
  184. data/spec/conceptql/nodes/person_filter_spec.rb +0 -18
  185. data/spec/conceptql/nodes/person_spec.rb +0 -12
  186. data/spec/conceptql/nodes/procedure_occurrence_spec.rb +0 -12
  187. data/spec/conceptql/nodes/query_double.rb +0 -19
  188. data/spec/conceptql/nodes/race_spec.rb +0 -23
  189. data/spec/conceptql/nodes/rxnorm_spec.rb +0 -31
  190. data/spec/conceptql/nodes/snomed_spec.rb +0 -31
  191. data/spec/conceptql/nodes/source_vocabulary_node_spec.rb +0 -37
  192. data/spec/conceptql/nodes/standard_vocabulary_node_spec.rb +0 -40
  193. data/spec/conceptql/nodes/started_by_spec.rb +0 -25
  194. data/spec/conceptql/nodes/time_window_spec.rb +0 -85
  195. data/spec/conceptql/nodes/union_spec.rb +0 -25
  196. data/spec/conceptql/nodes/visit_occurrence_spec.rb +0 -12
@@ -0,0 +1,23 @@
1
+ require_relative 'casting_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class Death < CastingOperator
6
+ desc 'Generates all death records, or, if fed a stream, fetches all death records for the people represented in the incoming result set.'
7
+ types :death
8
+ allows_one_upstream
9
+
10
+ def my_type
11
+ :death
12
+ end
13
+
14
+ def i_point_at
15
+ [ :person ]
16
+ end
17
+
18
+ def these_point_at_me
19
+ []
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class DrugTypeConcept < Operator
6
+ desc 'Given a set of concept IDs in RxNorm, returns that set of drug exposures'
7
+ argument :concept_ids, type: :codelist, vocab: 'RxNorm'
8
+
9
+ def type
10
+ :drug_exposure
11
+ end
12
+
13
+ def query(db)
14
+ db.from(:drug_exposure)
15
+ .where(drug_type_concept_id: arguments)
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+
@@ -1,8 +1,14 @@
1
- require_relative 'temporal_node'
1
+ require_relative 'temporal_operator'
2
2
 
3
3
  module ConceptQL
4
- module Nodes
5
- class During < TemporalNode
4
+ module Operators
5
+ class During < TemporalOperator
6
+ desc <<-EOF
7
+ Compares all results on a person-by-person basis between the left hand results (LHR) and the right hand resuls (RHR).
8
+ For any result in the LHR whose start_date and end_date occur within the start_date and end_date of a RHR row, that result is passed through.
9
+ All other results are discarded, including all results in the RHR.
10
+ EOF
11
+
6
12
  def where_clause
7
13
  if inclusive?
8
14
  Sequel.expr(Sequel.expr(Proc.new { r__start_date <= l__start_date}).&(Sequel.expr( Proc.new { l__start_date <= r__end_date })))
@@ -0,0 +1,13 @@
1
+ require_relative 'temporal_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class Equal < TemporalOperator
6
+ desc 'If a LHR result has the same value_as_number as a RHR result, it is passed through'
7
+
8
+ def where_clause
9
+ { r__value_as_number: :l__value_as_number }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'binary_operator_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class Except < BinaryOperatorOperator
6
+ desc 'If a LHR result appears in the RHR result, it is removed from the output result set.'
7
+ category 'Set Logic'
8
+
9
+ def query(db)
10
+ if ignore_dates?
11
+ query = db.from(Sequel.as(left.evaluate(db), :l))
12
+ .left_join(Sequel.as(right.evaluate(db), :r), l__criterion_id: :r__criterion_id, l__criterion_type: :r__criterion_type)
13
+ .where(r__criterion_id: nil)
14
+ .select_all(:l)
15
+ db.from(query)
16
+ else
17
+ left.evaluate(db).except(right.evaluate(db))
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def ignore_dates?
24
+ options[:ignore_dates]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'binary_operator_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class Filter < BinaryOperatorOperator
6
+ desc 'Only pass through results from the LHR that have a corresponding RHR with the same person, criterion_id, and criterion_type'
7
+
8
+ def query(db)
9
+ rhs = right.evaluate(db)
10
+ rhs = rhs.select_group(:person_id, :criterion_id, :criterion_type)
11
+ query = db.from(Sequel.as(left.evaluate(db), :l))
12
+ query = query
13
+ .left_join(Sequel.as(rhs, :r), l__person_id: :r__person_id, l__criterion_id: :r__criterion_id, l__criterion_type: :r__criterion_type)
14
+ .exclude(r__criterion_id: nil)
15
+ .select_all(:l)
16
+ db.from(query)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
@@ -1,20 +1,22 @@
1
1
  require_relative 'occurrence'
2
2
 
3
3
  module ConceptQL
4
- module Nodes
5
- # Represents a node that will grab the first occurrence of something
4
+ module Operators
5
+ # Represents a operator that will grab the first occurrence of something
6
6
  #
7
- # The node treats all streams as a single, large stream. It partitions
7
+ # The operator treats all streams as a single, large stream. It partitions
8
8
  # that larget stream by person_id, then sorts within those groupings
9
9
  # by start_date and then select at most one row per person, regardless
10
- # of how many different types of streams enter the node
10
+ # of how many different types of streams enter the operator
11
11
  #
12
12
  # If two rows have the same start_date, the order of their ranking
13
13
  # is arbitrary
14
14
  #
15
15
  # If we ask for the first occurrence of something and a person has no
16
- # occurrences, this node returns nothing for that person
16
+ # occurrences, this operator returns nothing for that person
17
17
  class First < Occurrence
18
+ desc 'Only passes through the row with the earliest start_date per person. If more than one row qualifies, result is arbitrarily chosen.'
19
+
18
20
  def occurrence
19
21
  1
20
22
  end
@@ -1,8 +1,8 @@
1
1
  require_relative 'pass_thru'
2
2
 
3
3
  module ConceptQL
4
- module Nodes
5
- class From < Node
4
+ module Operators
5
+ class From < Operator
6
6
  def query(db)
7
7
  db.from(values.first)
8
8
  end
@@ -0,0 +1,23 @@
1
+ require_relative 'operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class FromSeerVisits < Operator
6
+ def type
7
+ :observation
8
+ end
9
+ def query(db)
10
+ visit_ids = stream.evaluate(db)
11
+ .from_self
12
+ .where(criterion_type: 'visit_occurrence')
13
+ query = db[:observation].where(visit_occurrence_id: visit_ids.select(:criterion_id))
14
+ arguments.inject(query) do |q, key|
15
+ q.where(observation_source_value: key.to_s.upcase)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+
23
+
@@ -1,11 +1,11 @@
1
- require_relative 'node'
1
+ require_relative 'operator'
2
2
 
3
3
  module ConceptQL
4
- module Nodes
5
- class Gender < Node
6
- def type
7
- :person
8
- end
4
+ module Operators
5
+ class Gender < Operator
6
+ desc 'Returns all person records that match the selected gender.'
7
+ argument :gender, type: :string, options: ['Male', 'Female']
8
+ types :person
9
9
 
10
10
  def query(db)
11
11
  gender_concept_ids = values.map do |value|
@@ -0,0 +1,25 @@
1
+ require_relative 'standard_vocabulary_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class Hcpcs < StandardVocabularyOperator
6
+ preferred_name 'HCPCS'
7
+ desc 'Searches the procedure_occurrence table for all procedures with matching HCPCS codes'
8
+ argument :hcpcs, type: :codelist, vocab: 'HCPCS'
9
+ predominant_types :procedure_occurrence
10
+
11
+ def table
12
+ :procedure_occurrence
13
+ end
14
+
15
+ def vocabulary_id
16
+ 5
17
+ end
18
+
19
+ def concept_column
20
+ :procedure_concept_id
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,28 @@
1
+ require_relative 'source_vocabulary_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class Icd10 < SourceVocabularyOperator
6
+ preferred_name 'ICD-10 CM'
7
+ desc 'Searches the condition_occurrence table for the given set of ICD-10 codes.'
8
+ argument :icd10s, type: :codelist, vocab: 'ICD10CM'
9
+ predominant_types :condition_occurrence
10
+
11
+ def table
12
+ :condition_occurrence
13
+ end
14
+
15
+ def vocabulary_id
16
+ 34
17
+ end
18
+
19
+ def source_column
20
+ :condition_source_value
21
+ end
22
+
23
+ def concept_column
24
+ :condition_concept_id
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'source_vocabulary_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class Icd9 < SourceVocabularyOperator
6
+ preferred_name 'ICD-9 CM'
7
+ desc 'Searches the condition_occurrence table for the given set of ICD-9 codes.'
8
+ argument :icd9s, type: :codelist, vocab: 'ICD9CM'
9
+ predominant_types :condition_occurrence
10
+
11
+ def table
12
+ :condition_occurrence
13
+ end
14
+
15
+ def vocabulary_id
16
+ 2
17
+ end
18
+
19
+ def source_column
20
+ :condition_source_value
21
+ end
22
+
23
+ def concept_column
24
+ :condition_concept_id
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ require_relative 'standard_vocabulary_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class Icd9Procedure < StandardVocabularyOperator
6
+ preferred_name 'ICD-9 Proc'
7
+ desc 'Searches the procedure_occurrence table for the given set of ICD-9 codes.'
8
+ argument :icd9s, type: :codelist, vocab: 'ICD9Proc'
9
+ predominant_types :procedure_occurrence
10
+
11
+ def table
12
+ :procedure_occurrence
13
+ end
14
+
15
+ def vocabulary_id
16
+ 3
17
+ end
18
+
19
+ def concept_column
20
+ :procedure_concept_id
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -1,15 +1,19 @@
1
1
  require_relative 'pass_thru'
2
2
 
3
3
  module ConceptQL
4
- module Nodes
4
+ module Operators
5
5
  class Intersect < PassThru
6
+ desc 'Passes thru any result row that appears in all incoming result sets.'
7
+ allows_many_upstreams
8
+ category 'Set Logic'
9
+
6
10
  def types
7
- values.map(&:types).flatten.uniq
11
+ upstreams.map(&:types).flatten.uniq
8
12
  end
9
13
 
10
14
  def query(db)
11
15
  exprs = {}
12
- values.each do |expression|
16
+ upstreams.each do |expression|
13
17
  evaled = expression.evaluate(db)
14
18
  expression.types.each do |type|
15
19
  (exprs[type] ||= []) << evaled
@@ -1,20 +1,22 @@
1
1
  require_relative 'occurrence'
2
2
 
3
3
  module ConceptQL
4
- module Nodes
5
- # Represents a node that will grab the last occurrence of something
4
+ module Operators
5
+ # Represents a operator that will grab the last occurrence of something
6
6
  #
7
- # The node treats all streams as a single, large stream. It partitions
7
+ # The operator treats all streams as a single, large stream. It partitions
8
8
  # that larget stream by person_id, then sorts within those groupings
9
9
  # by start_date and then select at most one row per person, regardless
10
- # of how many different types of streams enter the node
10
+ # of how many different types of streams enter the operator
11
11
  #
12
12
  # If two rows have the same start_date, the order of their ranking
13
13
  # is arbitrary
14
14
  #
15
15
  # If we ask for the last occurrence of something and a person has no
16
- # occurrences, this node returns nothing for that person
16
+ # occurrences, this operator returns nothing for that person
17
17
  class Last < Occurrence
18
+ desc 'Only passes through the row with the most recent start_date per person. If more than one row qualifies, result is arbitrarily chosen.'
19
+
18
20
  def occurrence
19
21
  -1
20
22
  end
@@ -0,0 +1,25 @@
1
+ require_relative 'standard_vocabulary_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class Loinc < StandardVocabularyOperator
6
+ preferred_name 'LOINC'
7
+ desc 'Searches the observation table for all observations with matching LOINC codes'
8
+ argument :loincs, type: :codelist, vocab: 'LOINC'
9
+ predominant_types :observation
10
+
11
+ def table
12
+ :observation
13
+ end
14
+
15
+ def vocabulary_id
16
+ 6
17
+ end
18
+
19
+ def concept_column
20
+ :observation_concept_id
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,28 @@
1
+ require_relative 'source_vocabulary_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class Medcode < SourceVocabularyOperator
6
+ desc 'Searches the condition_occurrence table for all conditions with matching Medcodes'
7
+ argument :medcodes, type: :codelist, vocab_id: '203'
8
+ predominant_types :condition_occurrence
9
+
10
+ def table
11
+ :condition_occurrence
12
+ end
13
+
14
+ def vocabulary_id
15
+ 203
16
+ end
17
+
18
+ def source_column
19
+ :condition_source_value
20
+ end
21
+
22
+ def concept_column
23
+ :condition_concept_id
24
+ end
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,27 @@
1
+ require_relative 'source_vocabulary_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class MedcodeProcedure < SourceVocabularyOperator
6
+ desc 'Searches the procedure_occurrence table for all procedures with matching Medcodes'
7
+ argument :medcodes, type: :codelist, vocab: '204'
8
+ predominant_types :procedure_occurrence
9
+
10
+ def table
11
+ :procedure_occurrence
12
+ end
13
+
14
+ def vocabulary_id
15
+ 204
16
+ end
17
+
18
+ def source_column
19
+ :procedure_source_value
20
+ end
21
+
22
+ def concept_column
23
+ :procedure_concept_id
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ require_relative 'source_vocabulary_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class Ndc < SourceVocabularyOperator
6
+ preferred_name 'NDC'
7
+ desc 'Searches the drug_exposure table for all procedures with matching NDC codes'
8
+ argument :ndcs, type: :codelist, vocab: 'NDC'
9
+ predominant_types :drug_exposure
10
+
11
+ def table
12
+ :drug_exposure
13
+ end
14
+
15
+ def vocabulary_id
16
+ 9
17
+ end
18
+
19
+ def source_column
20
+ :drug_source_value
21
+ end
22
+
23
+ def concept_column
24
+ :drug_concept_id
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,54 @@
1
+ require_relative 'pass_thru'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ # Represents a operator that will either:
6
+ # - create a value_as_number value for every person in the database
7
+ # - change the value_as_number value for every every result passed in
8
+ # - either to a number
9
+ # - or a value from a column in the origin row
10
+ #
11
+ # Accepts two params:
12
+ # - Either a number value or a symbol representing a column name
13
+ # - An optional stream
14
+ class Numeric < PassThru
15
+ desc <<-EOF
16
+ Represents a operator that will either:
17
+ - create a value_as_number value for every person in the database
18
+ - change the value_as_number value for every every result passed in
19
+ - either to a number
20
+ - or a value from a column in the origin row
21
+
22
+ Accepts two params:
23
+ - Either a number value or a symbol representing a column name
24
+ - An optional stream
25
+ EOF
26
+ argument :value, type: :float
27
+ allows_one_upstream
28
+
29
+ def query(db)
30
+ stream.nil? ? as_criterion(db) : with_kids(db)
31
+ end
32
+
33
+ def types
34
+ stream.nil? ? [:person] : super
35
+ end
36
+
37
+ private
38
+ def with_kids(db)
39
+ db.from(stream.evaluate(db))
40
+ .select(*(COLUMNS - [:value_as_number]))
41
+ .select_append(Sequel.lit('?', arguments.first).cast(Float).as(:value_as_number))
42
+ .from_self
43
+ end
44
+
45
+ def as_criterion(db)
46
+ db.from(select_it(db.from(:person), :person))
47
+ .select(*(COLUMNS - [:value_as_number]))
48
+ .select_append(Sequel.lit('?', arguments.first).cast(Float).as(:value_as_number))
49
+ .from_self
50
+ end
51
+ end
52
+ end
53
+ end
54
+
@@ -0,0 +1,29 @@
1
+ require_relative 'source_vocabulary_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class ObservationByEnttype < SourceVocabularyOperator
6
+ desc 'Searches the observation table for all observations with matching Enttype'
7
+ argument :enttypes, type: :codelist, vocab_id: [206, 207]
8
+ predominant_types :observation
9
+
10
+ def table
11
+ :observation
12
+ end
13
+
14
+ def vocabulary_id
15
+ [206, 207]
16
+ end
17
+
18
+ def source_column
19
+ :observation_source_value
20
+ end
21
+
22
+ def concept_column
23
+ :observation_concept_id
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+
@@ -0,0 +1,30 @@
1
+ require_relative 'casting_operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ class ObservationPeriod < CastingOperator
6
+ desc 'Generates all observation_period records, or, if fed a stream, fetches all observation_period records for the people represented in the incoming result set.'
7
+ types :observation_period
8
+ allows_one_upstream
9
+
10
+ def my_type
11
+ :observation_period
12
+ end
13
+
14
+ def i_point_at
15
+ []
16
+ end
17
+
18
+ def these_point_at_me
19
+ # I could list ALL the types we use, but the default behavior of casting,
20
+ # when there is no explicit casting defined, is to convert everything to
21
+ # person IDs
22
+ #
23
+ # So by defining no known castable relationships in this operator, all
24
+ # types will be converted to person
25
+ []
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,70 @@
1
+ require_relative 'operator'
2
+
3
+ module ConceptQL
4
+ module Operators
5
+ # Represents a operator that will grab the Nth occurrence of something
6
+ #
7
+ # Specify occurrences as integers, excluding O
8
+ # 1 => first
9
+ # 2 => second
10
+ # ...
11
+ # -1 => last
12
+ # -2 => second-to-last
13
+ #
14
+ # The operator treats all streams as a single, large stream. It partitions
15
+ # that larget stream by person_id, then sorts within those groupings
16
+ # by start_date and then select at most one row per person, regardless
17
+ # of how many different types of streams enter the operator
18
+ #
19
+ # If two rows have the same start_date, the order of their ranking
20
+ # is arbitrary
21
+ #
22
+ # If we ask for the second occurrence of something and a person has only one
23
+ # occurrence, this operator returns nothing for that person
24
+ class Occurrence < Operator
25
+ preferred_name 'Nth Occurrence'
26
+ desc <<-EOF
27
+ Groups all results by person, then orders by start_date, then finds the nth occurrence.
28
+ nth occurrence can be positive or negative.
29
+ 1 => first
30
+ 2 => second
31
+ ...
32
+ -1 => last
33
+ -2 => second-to-last
34
+
35
+ If two rows have the same start_date, the order of their ranking
36
+ is arbitrary
37
+
38
+ If we ask for the second occurrence of something and a person has only one
39
+ occurrence, this operator returns nothing for that person
40
+ EOF
41
+ argument :occurrence, type: :integer
42
+ allows_one_upstream
43
+ category %w(Temporal Occurrence)
44
+
45
+ def query(db)
46
+ db[:occurrences]
47
+ .with(:occurrences,
48
+ stream.evaluate(db)
49
+ .from_self
50
+ .select_append { |o| o.row_number(:over, partition: :person_id, order: ordered_columns){}.as(:rn) })
51
+ .where(rn: occurrence.abs)
52
+ end
53
+
54
+ def occurrence
55
+ @occurrence ||= arguments.first
56
+ end
57
+
58
+ private
59
+ def asc_or_desc
60
+ occurrence < 0 ? :desc : :asc
61
+ end
62
+
63
+ def ordered_columns
64
+ ordered_columns = [Sequel.send(asc_or_desc, :start_date)]
65
+ ordered_columns += [:criterion_type, :criterion_id]
66
+ end
67
+ end
68
+ end
69
+ end
70
+