conceptql 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+