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
@@ -1,23 +0,0 @@
1
- require_relative 'pass_thru'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Count < PassThru
6
- def query(db)
7
- db.from(unioned(db))
8
- .group(*COLUMNS)
9
- .select(*(COLUMNS - [:value_as_numeric]))
10
- .select_append{count(1).as(:value_as_numeric)}
11
- .from_self
12
- end
13
-
14
- def unioned(db)
15
- children.map { |c| c.evaluate(db) }.inject do |uni, q|
16
- uni.union(q)
17
- end
18
- end
19
- end
20
- end
21
- end
22
-
23
-
@@ -1,20 +0,0 @@
1
- require_relative 'standard_vocabulary_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Cpt < StandardVocabularyNode
6
- def table
7
- :procedure_occurrence
8
- end
9
-
10
- def vocabulary_id
11
- 4
12
- end
13
-
14
- def concept_column
15
- :procedure_concept_id
16
- end
17
- end
18
- end
19
- end
20
-
@@ -1,19 +0,0 @@
1
- require_relative 'casting_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Death < CastingNode
6
- def my_type
7
- :death
8
- end
9
-
10
- def i_point_at
11
- [ :person ]
12
- end
13
-
14
- def these_point_at_me
15
- []
16
- end
17
- end
18
- end
19
- end
@@ -1,96 +0,0 @@
1
- require_relative 'node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- # Mimics creating a variable name that stores an itermediate result as
6
- # part of a larger concept
7
- #
8
- # The idea is that a concept might be very complex and it helps to break
9
- # that complex concept into a set of sub-concepts to better understand it.
10
- #
11
- # Also, sometimes a particular piece of a concept is used in many places,
12
- # so it makes sense to write that piece out once, store it as a "variable"
13
- # and then insert that variable into the concept as needed.
14
- # run the query once and subsequent calls
15
- class Define < Node
16
- def initialize(*args)
17
- super
18
- end
19
- # Create a temporary table and store the stream of results in that table.
20
- # This "caches" the results so we only have to execute stream's query
21
- # once.
22
- #
23
- # The logic here is that if something is assigned to a variable, chances
24
- # are that it will be used more than once, so why run the query more than
25
- # once?
26
- #
27
- # ConceptQL's SQL generator normally translates the entire statement into
28
- # one, large query that can be executed later.
29
- #
30
- # Unfortunately, Sequel's "create_table" function actually executes the
31
- # 'CREATE TABLE' SQL right away, meaning that the "define" node will
32
- # execute immediately _during_ the processing of the ConceptQL statement.
33
- # We'll see what kinds of problems this causes
34
- #
35
- # Lastly, this node does NOT pass its results to the next node. The
36
- # reason for this exception is to allow us to return the SQL that
37
- # generates the temp table. This is done so that the ConceptQL sandbox
38
- # can return the entire set of SQL statements needed to run a query.
39
- #
40
- # Perhaps in the future we can find a way around this.
41
- #
42
- # Also, things will blow up if you try to use a variable that hasn't been
43
- # defined yet.
44
- def query(db)
45
- # We'll wrap the creation of the temp table in memoization
46
- # That way we can call #query multiple times, but only suffer the
47
- # cost of creating the temp table just once
48
- @_run ||= begin
49
- if tree.opts[:sql_only]
50
- db.create_table!(table_name, temp: true, as: fake_row(db))
51
- else
52
- db.create_table!(table_name, temp: true, as: stream.evaluate(db))
53
- end
54
- true
55
- end
56
- db.from(table_name)
57
- end
58
-
59
- def columns(query, local_type)
60
- COLUMNS
61
- end
62
-
63
- def types
64
- stream.types
65
- end
66
-
67
- def sql(db)
68
- db[db.send(:create_table_as_sql, table_name, stream.evaluate(db).sql, temp: true)].sql
69
- end
70
-
71
- def tree=(tree)
72
- super
73
- tree.defined[table_name] = self
74
- end
75
-
76
- private
77
-
78
- def table_name
79
- @table_name ||= namify(arguments.first)
80
- end
81
-
82
- def fake_row(db)
83
- db
84
- .select(Sequel.cast(nil, Bignum).as(:person_id))
85
- .select_append(Sequel.cast(nil, Bignum).as(:criterion_id))
86
- .select_append(Sequel.cast(nil, String).as(:criterion_type))
87
- .select_append(Sequel.cast(nil, Date).as(:start_date))
88
- .select_append(Sequel.cast(nil, Date).as(:end_date))
89
- .select_append(Sequel.cast(nil, Bignum).as(:value_as_numeric))
90
- .select_append(Sequel.cast(nil, String).as(:value_as_string))
91
- .select_append(Sequel.cast(nil, Bignum).as(:value_as_concept_id))
92
- end
93
- end
94
- end
95
- end
96
-
@@ -1,18 +0,0 @@
1
- require_relative 'node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class DrugTypeConcept < Node
6
- def type
7
- :drug_exposure
8
- end
9
-
10
- def query(db)
11
- db.from(:drug_exposure)
12
- .where(drug_type_concept_id: arguments)
13
- end
14
- end
15
- end
16
- end
17
-
18
-
@@ -1,11 +0,0 @@
1
- require_relative 'temporal_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Equal < TemporalNode
6
- def where_clause
7
- { r__value_as_numeric: :l__value_as_numeric }
8
- end
9
- end
10
- end
11
- end
@@ -1,11 +0,0 @@
1
- require_relative 'binary_operator_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Except < BinaryOperatorNode
6
- def query(db)
7
- left.evaluate(db).except(right.evaluate(db))
8
- end
9
- end
10
- end
11
- end
@@ -1,20 +0,0 @@
1
- require_relative 'standard_vocabulary_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Hcpcs < StandardVocabularyNode
6
- def table
7
- :procedure_occurrence
8
- end
9
-
10
- def vocabulary_id
11
- 5
12
- end
13
-
14
- def concept_column
15
- :procedure_concept_id
16
- end
17
- end
18
- end
19
- end
20
-
@@ -1,23 +0,0 @@
1
- require_relative 'source_vocabulary_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Icd10 < SourceVocabularyNode
6
- def table
7
- :condition_occurrence
8
- end
9
-
10
- def vocabulary_id
11
- 34
12
- end
13
-
14
- def source_column
15
- :condition_source_value
16
- end
17
-
18
- def concept_column
19
- :condition_concept_id
20
- end
21
- end
22
- end
23
- end
@@ -1,23 +0,0 @@
1
- require_relative 'source_vocabulary_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Icd9 < SourceVocabularyNode
6
- def table
7
- :condition_occurrence
8
- end
9
-
10
- def vocabulary_id
11
- 2
12
- end
13
-
14
- def source_column
15
- :condition_source_value
16
- end
17
-
18
- def concept_column
19
- :condition_concept_id
20
- end
21
- end
22
- end
23
- end
@@ -1,20 +0,0 @@
1
- require_relative 'standard_vocabulary_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Icd9Procedure < StandardVocabularyNode
6
- def table
7
- :procedure_occurrence
8
- end
9
-
10
- def vocabulary_id
11
- 3
12
- end
13
-
14
- def concept_column
15
- :procedure_concept_id
16
- end
17
- end
18
- end
19
- end
20
-
@@ -1,20 +0,0 @@
1
- require_relative 'standard_vocabulary_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Loinc < StandardVocabularyNode
6
- def table
7
- :observation
8
- end
9
-
10
- def vocabulary_id
11
- 6
12
- end
13
-
14
- def concept_column
15
- :observation_concept_id
16
- end
17
- end
18
- end
19
- end
20
-
@@ -1,40 +0,0 @@
1
- require_relative 'pass_thru'
2
-
3
- module ConceptQL
4
- module Nodes
5
- # Represents a node that will either:
6
- # - create a value_as_numeric value for every person in the database
7
- # - change the value_as_numeric value for every every result passed in
8
- # - either to a numeric
9
- # - or a value from a column in the origin row
10
- #
11
- # Accepts two params:
12
- # - Either a numeric value or a symbol representing a column name
13
- # - An optional stream
14
- class Numeric < PassThru
15
- def query(db)
16
- stream.nil? ? as_criterion(db) : with_kids(db)
17
- end
18
-
19
- def types
20
- stream.nil? ? [:person] : super
21
- end
22
-
23
- private
24
- def with_kids(db)
25
- db.from(stream.evaluate(db))
26
- .select(*(COLUMNS - [:value_as_numeric]))
27
- .select_append(Sequel.lit('?', arguments.first).cast(Float).as(:value_as_numeric))
28
- .from_self
29
- end
30
-
31
- def as_criterion(db)
32
- db.from(select_it(db.from(:person), :person))
33
- .select(*(COLUMNS - [:value_as_numeric]))
34
- .select_append(Sequel.lit('?', arguments.first).cast(Float).as(:value_as_numeric))
35
- .from_self
36
- end
37
- end
38
- end
39
- end
40
-
@@ -1,49 +0,0 @@
1
- require_relative 'node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- # Represents a node 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 node 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 node
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 node returns nothing for that person
24
- class Occurrence < Node
25
- def query(db)
26
- stream.evaluate(db)
27
- .from_self
28
- .select_append { |o| o.row_number(:over, partition: :person_id, order: ordered_columns){}.as(:rn) }
29
- .from_self
30
- .where(rn: occurrence.abs)
31
- end
32
-
33
- def occurrence
34
- @occurrence ||= arguments.first
35
- end
36
-
37
- private
38
- def asc_or_desc
39
- occurrence < 0 ? :desc : :asc
40
- end
41
-
42
- def ordered_columns
43
- ordered_columns = [Sequel.send(asc_or_desc, :start_date)]
44
- ordered_columns += [:criterion_type, :criterion_id]
45
- end
46
- end
47
- end
48
- end
49
-
@@ -1,11 +0,0 @@
1
- require_relative 'node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class PassThru < Node
6
- def types
7
- children.map(&:types).flatten.uniq
8
- end
9
- end
10
- end
11
- end
@@ -1,12 +0,0 @@
1
- require_relative 'binary_operator_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class PersonFilter < BinaryOperatorNode
6
- def query(db)
7
- db.from(left.evaluate(db))
8
- .where(person_id: right.evaluate(db).select_group(:person_id))
9
- end
10
- end
11
- end
12
- end
@@ -1,21 +0,0 @@
1
- require_relative 'casting_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class ProcedureOccurrence < CastingNode
6
- def my_type
7
- :procedure_occurrence
8
- end
9
-
10
- def i_point_at
11
- [ :person ]
12
- end
13
-
14
- def these_point_at_me
15
- %i[
16
- procedure_cost
17
- ]
18
- end
19
- end
20
- end
21
- end
@@ -1,50 +0,0 @@
1
- require_relative 'pass_thru'
2
-
3
- module ConceptQL
4
- module Nodes
5
- # Mimics using a variable that has been set via "define" node
6
- #
7
- # The idea is that a concept might be very complex and it helps to break
8
- # that complex concept into a set of sub-concepts to better understand it.
9
- #
10
- # This node will look for a sub-concept that has been created through the
11
- # "define" node and will fetch the results cached in the corresponding table
12
- class Recall < Node
13
- # Behind the scenes we simply fetch all rows from the temp table that
14
- # corresponds to the name fed to "recall"
15
- #
16
- # We also set the @types variable by pulling the type information out
17
- # of the hash piggybacking on the database connection.
18
- #
19
- # TODO: This might be an issue since we might need the type information
20
- # before we call #query. Probably time to reevaluate how we're caching
21
- # the type information.
22
- def query(db)
23
- # We're going to call evaluate on definition to ensure the definition
24
- # has been created. We were running into odd timing issues when
25
- # drawing graphs where the recall node was being drawn before definition
26
- # was drawn.
27
- definition.evaluate(db)
28
- db.from(table_name)
29
- end
30
-
31
- def columns(query, local_type)
32
- COLUMNS
33
- end
34
-
35
- def types
36
- definition.types
37
- end
38
-
39
- private
40
- def table_name
41
- @table_name ||= namify(arguments.first)
42
- end
43
-
44
- def definition
45
- tree.defined[table_name]
46
- end
47
- end
48
- end
49
- end
50
-
@@ -1,20 +0,0 @@
1
- require_relative 'standard_vocabulary_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Rxnorm < StandardVocabularyNode
6
- def table
7
- :drug_exposure
8
- end
9
-
10
- def vocabulary_id
11
- 8
12
- end
13
-
14
- def concept_column
15
- :drug_concept_id
16
- end
17
- end
18
- end
19
- end
20
-
@@ -1,19 +0,0 @@
1
- require_relative 'standard_vocabulary_node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Snomed < StandardVocabularyNode
6
- def table
7
- :condition_occurrence
8
- end
9
-
10
- def vocabulary_id
11
- 1
12
- end
13
-
14
- def concept_column
15
- :condition_concept_id
16
- end
17
- end
18
- end
19
- end
@@ -1,11 +0,0 @@
1
- require_relative 'node'
2
-
3
- module ConceptQL
4
- module Nodes
5
- class Visit < Node
6
- def types
7
- [:visit_occurrence]
8
- end
9
- end
10
- end
11
- end
@@ -1,18 +0,0 @@
1
- require 'spec_helper'
2
- require 'conceptql/nodes/after'
3
- require_double('stream_for_temporal')
4
-
5
- describe ConceptQL::Nodes::After do
6
- it 'behaves itself' do
7
- ConceptQL::Nodes::After.new.must_behave_like(:temporal_node)
8
- end
9
-
10
- subject do
11
- ConceptQL::Nodes::After.new(left: StreamForTemporalDouble.new, right: StreamForTemporalDouble.new)
12
- end
13
-
14
- it 'should use proper where clause' do
15
- subject.query(Sequel.mock).sql.must_match 'l.start_date > r.end_date'
16
- end
17
- end
18
-
@@ -1,18 +0,0 @@
1
- require 'spec_helper'
2
- require 'conceptql/nodes/before'
3
- require_double('stream_for_temporal')
4
-
5
- describe ConceptQL::Nodes::Before do
6
- it 'behaves itself' do
7
- ConceptQL::Nodes::Before.new.must_behave_like(:temporal_node)
8
- end
9
-
10
- subject do
11
- ConceptQL::Nodes::Before.new(left: StreamForTemporalDouble.new, right: StreamForTemporalDouble.new)
12
- end
13
-
14
- it 'should use proper where clause' do
15
- subject.query(Sequel.mock).sql.must_match 'l.end_date < r.start_date'
16
- end
17
- end
18
-
@@ -1,15 +0,0 @@
1
- require 'spec_helper'
2
- require 'conceptql/nodes/complement'
3
- require_relative 'query_double'
4
-
5
- describe ConceptQL::Nodes::Complement do
6
- it 'behaves itself' do
7
- ConceptQL::Nodes::Complement.new.must_behave_like(:evaluator)
8
- end
9
-
10
- it 'generates complement for single criteria' do
11
- double1 = QueryDouble.new(1)
12
- double1.must_behave_like(:evaluator)
13
- ConceptQL::Nodes::Complement.new(double1).query(Sequel.mock).sql.must_equal "SELECT * FROM (SELECT person_id, visit_occurrence_id AS criterion_id, CAST('visit_occurrence' AS varchar(255)) AS criterion_type, CAST(visit_start_date AS date) AS start_date, CAST(visit_end_date AS date) AS end_date, CAST(NULL AS numeric) AS value_as_numeric, CAST(NULL AS varchar(255)) AS value_as_string, CAST(NULL AS integer) AS value_as_concept_id FROM visit_occurrence AS tab WHERE (visit_occurrence_id NOT IN (SELECT criterion_id FROM (SELECT * FROM table1) AS t1 WHERE ((criterion_id IS NOT NULL) AND (criterion_type = 'visit_occurrence'))))) AS t1"
14
- end
15
- end
@@ -1,34 +0,0 @@
1
- require 'spec_helper'
2
- require 'conceptql/nodes/concept'
3
-
4
- describe ConceptQL::Nodes::Concept do
5
- it 'behaves itself' do
6
- ConceptQL::Nodes::Concept.new.must_behave_like(:evaluator)
7
- end
8
-
9
- class ConceptDouble < ConceptQL::Nodes::Concept
10
- def arguments
11
- [1]
12
- end
13
-
14
- def set_statement(value)
15
- # Do Nothing
16
- end
17
-
18
- def stream
19
- @stream ||= Minitest::Mock.new
20
- end
21
- end
22
-
23
- describe '#query' do
24
- it 'evaluates child' do
25
- cd = ConceptDouble.new(1)
26
- cd.stream.expect :evaluate, nil, [:db]
27
- cd.query(:db)
28
- cd.stream.verify
29
- end
30
- end
31
- end
32
-
33
-
34
-
@@ -1,31 +0,0 @@
1
- require 'spec_helper'
2
- require 'conceptql/nodes/cpt'
3
-
4
- describe ConceptQL::Nodes::Cpt do
5
- it 'behaves itself' do
6
- ConceptQL::Nodes::Cpt.new.must_behave_like(:standard_vocabulary_node)
7
- end
8
-
9
- subject do
10
- ConceptQL::Nodes::Cpt.new
11
- end
12
-
13
- describe '#table' do
14
- it 'should be procedure_occurrence' do
15
- subject.table.must_equal :procedure_occurrence
16
- end
17
- end
18
-
19
- describe '#concept_column' do
20
- it 'should be procedure_concept_id' do
21
- subject.concept_column.must_equal :procedure_concept_id
22
- end
23
- end
24
-
25
- describe '#vocabulary_id' do
26
- it 'should be 4' do
27
- subject.vocabulary_id.must_equal 4
28
- end
29
- end
30
- end
31
-