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,33 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/date_range'
3
+
4
+ describe ConceptQL::Operators::DateRange do
5
+ it_behaves_like(:evaluator)
6
+
7
+ describe '#types' do
8
+ it 'should be [:date]' do
9
+ expect(ConceptQL::Operators::DateRange.new(start: '2004-12-13', end: '2010-03-20').types).to eq([:person])
10
+ end
11
+ end
12
+
13
+ describe '#query' do
14
+ it 'should be dates specified assigned to all persons' do
15
+ expect(ConceptQL::Operators::DateRange.new(start: '2004-12-13', end: '2010-03-20').query(Sequel.mock).sql).to eq("SELECT * FROM (SELECT *, CAST('person' AS varchar(255)) AS criterion_type, person_id AS criterion_id, date '2004-12-13' AS start_date, date '2010-03-20' AS end_date FROM person) AS t1")
16
+ end
17
+
18
+ it 'should handle strings for option keys' do
19
+ expect(ConceptQL::Operators::DateRange.new('start' => '2004-12-13', 'end' => '2010-03-20').query(Sequel.mock).sql).to eq("SELECT * FROM (SELECT *, CAST('person' AS varchar(255)) AS criterion_type, person_id AS criterion_id, date '2004-12-13' AS start_date, date '2010-03-20' AS end_date FROM person) AS t1")
20
+ end
21
+
22
+ it 'handles START as day before first recorded observation_period' do
23
+ expect(ConceptQL::Operators::DateRange.new(start: 'START', end: '2010-03-20').query(Sequel.mock).sql).to eq("SELECT * FROM (SELECT *, CAST('person' AS varchar(255)) AS criterion_type, person_id AS criterion_id, (SELECT min(observation_period_start_date) FROM observation_period) AS start_date, date '2010-03-20' AS end_date FROM person) AS t1")
24
+ end
25
+
26
+ it 'handles END as last date of recorded observation_period' do
27
+ expect(ConceptQL::Operators::DateRange.new(start: '2004-12-13', end: 'END').query(Sequel.mock).sql).to eq("SELECT * FROM (SELECT *, CAST('person' AS varchar(255)) AS criterion_type, person_id AS criterion_id, date '2004-12-13' AS start_date, (SELECT max(observation_period_end_date) FROM observation_period) AS end_date FROM person) AS t1")
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/death'
3
+ require_double('stream_for_casting')
4
+
5
+ describe ConceptQL::Operators::Death do
6
+ it_behaves_like(:casting_operator)
7
+ end
8
+
9
+
10
+
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/during'
3
+ require_double('stream_for_temporal')
4
+
5
+ describe ConceptQL::Operators::During do
6
+ it_behaves_like(:temporal_operator)
7
+
8
+ describe 'when not inclusive' do
9
+ subject do
10
+ described_class.new(left: StreamForTemporalDouble.new, right: StreamForTemporalDouble.new)
11
+ end
12
+
13
+ it 'should use proper where clause' do
14
+ expect(subject.query(Sequel.mock).sql).to match('l.end_date <= r.end_date')
15
+ expect(subject.query(Sequel.mock).sql).to match('r.start_date <= l.start_date')
16
+ end
17
+ end
18
+
19
+ describe 'when inclusive' do
20
+ subject do
21
+ described_class.new(left: StreamForTemporalDouble.new, right: StreamForTemporalDouble.new, inclusive: true)
22
+ end
23
+
24
+ it 'should use proper where clause' do
25
+ expect(subject.query(Sequel.mock).sql).to match(/\(r.start_date <= l.end_date\) AND \(l.end_date <= r.end_date\)/)
26
+ expect(subject.query(Sequel.mock).sql).to match(/\(r.start_date <= l.start_date\) AND \(l.start_date <= r.end_date\)/)
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/except'
3
+ require_relative 'query_double'
4
+
5
+ describe ConceptQL::Operators::Except do
6
+ it_behaves_like(:evaluator)
7
+
8
+ describe '#query' do
9
+ it 'uses right stream as argument to EXCEPT against left stream' do
10
+ double1 = QueryDouble.new(1)
11
+ double2 = QueryDouble.new(2)
12
+ expect(ConceptQL::Operators::Except.new(left: double1, right: double2).query(Sequel.mock).sql).to eq("SELECT * FROM (SELECT * FROM table1 EXCEPT SELECT * FROM table2) AS t1")
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/first'
3
+ require_double('stream_for_occurrence')
4
+
5
+ describe ConceptQL::Operators::First do
6
+ it_behaves_like(:evaluator)
7
+
8
+ it 'should have occurrence pegged at 1' do
9
+ expect(described_class.new.occurrence).to eq(1)
10
+ end
11
+
12
+ describe 'occurrence set to 1' do
13
+ subject do
14
+ described_class.new(StreamForOccurrenceDouble.new).query(Sequel.mock(host: 'postgres')).sql
15
+ end
16
+
17
+ it 'should order by ascending start_date' do
18
+ expect(subject).to match('ORDER BY "start_date" ASC')
19
+ end
20
+
21
+ it 'should partition by person_id' do
22
+ expect(subject).to match('PARTITION BY "person_id"')
23
+ end
24
+
25
+ it 'should assign a row number' do
26
+ expect(subject).to match('row_number()')
27
+ end
28
+
29
+ it 'should find the all rows with rn = 1' do
30
+ expect(subject).to match('"rn" = 1')
31
+ end
32
+ end
33
+ end
34
+
35
+
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/from'
3
+ require_relative 'query_double'
4
+
5
+ describe ConceptQL::Operators::From do
6
+ it_behaves_like(:evaluator)
7
+
8
+ describe '#query' do
9
+ it 'works for single criteria' do
10
+ expect(ConceptQL::Operators::From.new(:table1).query(Sequel.mock).sql).to eq("SELECT * FROM table1")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/gender'
3
+
4
+ describe ConceptQL::Operators::Gender do
5
+ it_behaves_like(:evaluator)
6
+
7
+ describe '#query' do
8
+ it 'works for male/MALE/Male/M/m' do
9
+ correct_query = "SELECT * FROM person WHERE (gender_concept_id IN (8507))"
10
+ expect(ConceptQL::Operators::Gender.new('male').query(Sequel.mock).sql).to eq(correct_query)
11
+ expect(ConceptQL::Operators::Gender.new('Male').query(Sequel.mock).sql).to eq(correct_query)
12
+ expect(ConceptQL::Operators::Gender.new('MALE').query(Sequel.mock).sql).to eq(correct_query)
13
+ expect(ConceptQL::Operators::Gender.new('M').query(Sequel.mock).sql).to eq(correct_query)
14
+ expect(ConceptQL::Operators::Gender.new('m').query(Sequel.mock).sql).to eq(correct_query)
15
+ end
16
+
17
+ it 'works for Female/FEMALE/female/F/f' do
18
+ correct_query = "SELECT * FROM person WHERE (gender_concept_id IN (8532))"
19
+ expect(ConceptQL::Operators::Gender.new('female').query(Sequel.mock).sql).to eq(correct_query)
20
+ expect(ConceptQL::Operators::Gender.new('Female').query(Sequel.mock).sql).to eq(correct_query)
21
+ expect(ConceptQL::Operators::Gender.new('FEMALE').query(Sequel.mock).sql).to eq(correct_query)
22
+ expect(ConceptQL::Operators::Gender.new('F').query(Sequel.mock).sql).to eq(correct_query)
23
+ expect(ConceptQL::Operators::Gender.new('f').query(Sequel.mock).sql).to eq(correct_query)
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/hcpcs'
3
+
4
+ describe ConceptQL::Operators::Hcpcs do
5
+ it_behaves_like(:standard_vocabulary_operator)
6
+
7
+ subject do
8
+ described_class.new
9
+ end
10
+
11
+ describe '#table' do
12
+ it 'should be procedure_occurrence' do
13
+ expect(subject.table).to eq(:procedure_occurrence)
14
+ end
15
+ end
16
+
17
+ describe '#concept_column' do
18
+ it 'should be procedure_concept_id' do
19
+ expect(subject.concept_column).to eq(:procedure_concept_id)
20
+ end
21
+ end
22
+
23
+ describe '#vocabulary_id' do
24
+ it 'should be 5' do
25
+ expect(subject.vocabulary_id).to eq(5)
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/icd10'
3
+
4
+ describe ConceptQL::Operators::Icd10 do
5
+ it_behaves_like(:source_vocabulary_operator)
6
+
7
+ subject do
8
+ described_class.new
9
+ end
10
+
11
+ describe '#table' do
12
+ it 'should be condition_occurrence' do
13
+ expect(subject.table).to eq(:condition_occurrence)
14
+ end
15
+ end
16
+
17
+ describe '#concept_column' do
18
+ it 'should be condition_concept_id' do
19
+ expect(subject.concept_column).to eq(:condition_concept_id)
20
+ end
21
+ end
22
+
23
+ describe '#source_column' do
24
+ it 'should be condition_source_valuej' do
25
+ expect(subject.source_column).to eq(:condition_source_value)
26
+ end
27
+ end
28
+
29
+ describe '#vocabulary_id' do
30
+ it 'should be 34' do
31
+ expect(subject.vocabulary_id).to eq(34)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/icd9_procedure'
3
+
4
+ describe ConceptQL::Operators::Icd9Procedure do
5
+ it_behaves_like(:standard_vocabulary_operator)
6
+
7
+ subject do
8
+ described_class.new
9
+ end
10
+
11
+ describe '#table' do
12
+ it 'should be procedure_occurrence' do
13
+ expect(subject.table).to eq(:procedure_occurrence)
14
+ end
15
+ end
16
+
17
+ describe '#concept_column' do
18
+ it 'should be procedure_concept_id' do
19
+ expect(subject.concept_column).to eq(:procedure_concept_id)
20
+ end
21
+ end
22
+
23
+ describe '#vocabulary_id' do
24
+ it 'should be 3' do
25
+ expect(subject.vocabulary_id).to eq(3)
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/icd9'
3
+
4
+ describe ConceptQL::Operators::Icd9 do
5
+ it_behaves_like(:source_vocabulary_operator)
6
+
7
+ subject do
8
+ ConceptQL::Operators::Icd9.new
9
+ end
10
+
11
+ describe '#table' do
12
+ it 'should be condition_occurrence' do
13
+ expect(subject.table).to eq(:condition_occurrence)
14
+ end
15
+ end
16
+
17
+ describe '#concept_column' do
18
+ it 'should be condition_concept_id' do
19
+ expect(subject.concept_column).to eq(:condition_concept_id)
20
+ end
21
+ end
22
+
23
+ describe '#source_column' do
24
+ it 'should be condition_source_valuej' do
25
+ expect(subject.source_column).to eq(:condition_source_value)
26
+ end
27
+ end
28
+
29
+ describe '#vocabulary_id' do
30
+ it 'should be 2' do
31
+ expect(subject.vocabulary_id).to eq(2)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/intersect'
3
+ require_relative 'query_double'
4
+
5
+ describe ConceptQL::Operators::Intersect do
6
+ it_behaves_like(:evaluator)
7
+
8
+ describe '#query' do
9
+ it 'works for multiple criteria of same type' do
10
+ double1 = QueryDouble.new(1)
11
+ double2 = QueryDouble.new(2)
12
+ double3 = QueryDouble.new(3)
13
+ expect(ConceptQL::Operators::Intersect.new(double1, double2, double3).query(Sequel.mock).sql).to eq("SELECT * FROM (SELECT * FROM (SELECT * FROM table1 INTERSECT SELECT * FROM table2) AS t1 INTERSECT SELECT * FROM table3) AS t1")
14
+ end
15
+
16
+ it 'works for multiple criteria of different type' do
17
+ double1 = QueryDouble.new(1)
18
+ double2 = QueryDouble.new(2, :person)
19
+ double3 = QueryDouble.new(3)
20
+ expect(ConceptQL::Operators::Intersect.new(double1, double2, double3).query(Sequel.mock).sql).to eq("SELECT * FROM (SELECT * FROM (SELECT * FROM table1 INTERSECT SELECT * FROM table3) AS t1 UNION ALL SELECT * FROM table2) AS t1")
21
+ end
22
+
23
+ it 'works for single criteria' do
24
+ double1 = QueryDouble.new(1)
25
+ expect(ConceptQL::Operators::Intersect.new(double1).query(Sequel.mock).sql).to eq("SELECT * FROM table1")
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/last'
3
+ require_double('stream_for_occurrence')
4
+
5
+ describe ConceptQL::Operators::Last do
6
+ it_behaves_like(:evaluator)
7
+
8
+ it 'should have occurrence pegged at -1' do
9
+ expect(described_class.new.occurrence).to eq(-1)
10
+ end
11
+
12
+ describe 'occurrence set to -1' do
13
+ subject do
14
+ described_class.new(StreamForOccurrenceDouble.new).query(Sequel.mock(host: 'postgres')).sql
15
+ end
16
+
17
+
18
+ it 'should order by descending start_date' do
19
+ expect(subject).to match('ORDER BY "start_date" DESC')
20
+ end
21
+
22
+ it 'should partition by person_id' do
23
+ expect(subject).to match('PARTITION BY "person_id"')
24
+ end
25
+
26
+ it 'should assign a row number' do
27
+ expect(subject).to match('row_number()')
28
+ end
29
+
30
+ it 'should find the all rows with rn = 1' do
31
+ expect(subject).to match('"rn" = 1')
32
+ end
33
+ end
34
+ end
35
+
36
+
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/loinc'
3
+
4
+ describe ConceptQL::Operators::Loinc do
5
+ it_behaves_like(:standard_vocabulary_operator)
6
+
7
+ subject do
8
+ described_class.new
9
+ end
10
+
11
+ describe '#table' do
12
+ it 'should be observation' do
13
+ expect(subject.table).to eq(:observation)
14
+ end
15
+ end
16
+
17
+ describe '#concept_column' do
18
+ it 'should be procedure_concept_id' do
19
+ expect(subject.concept_column).to eq(:observation_concept_id)
20
+ end
21
+ end
22
+
23
+ describe '#vocabulary_id' do
24
+ it 'should be 6' do
25
+ expect(subject.vocabulary_id).to eq(6)
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/medcode_procedure'
3
+
4
+ describe ConceptQL::Operators::MedcodeProcedure do
5
+ it_behaves_like(:source_vocabulary_operator)
6
+
7
+ subject do
8
+ described_class.new
9
+ end
10
+
11
+ describe '#table' do
12
+ it 'should be procedure_occurrence' do
13
+ expect(subject.table).to eq(:procedure_occurrence)
14
+ end
15
+ end
16
+
17
+ describe '#concept_column' do
18
+ it 'should be procedure_concept_id' do
19
+ expect(subject.concept_column).to eq(:procedure_concept_id)
20
+ end
21
+ end
22
+
23
+ describe '#source_column' do
24
+ it 'should be procedure_source_valuej' do
25
+ expect(subject.source_column).to eq(:procedure_source_value)
26
+ end
27
+ end
28
+
29
+ describe '#vocabulary_id' do
30
+ it 'should be 204 (a J&J provided mapping as part of CPRD)' do
31
+ expect(subject.vocabulary_id).to eq(204)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/medcode'
3
+
4
+ describe ConceptQL::Operators::Medcode do
5
+ it_behaves_like(:source_vocabulary_operator)
6
+
7
+ subject do
8
+ described_class.new
9
+ end
10
+
11
+ describe '#table' do
12
+ it 'should be condition_occurrence' do
13
+ expect(subject.table).to eq(:condition_occurrence)
14
+ end
15
+ end
16
+
17
+ describe '#concept_column' do
18
+ it 'should be condition_concept_id' do
19
+ expect(subject.concept_column).to eq(:condition_concept_id)
20
+ end
21
+ end
22
+
23
+ describe '#source_column' do
24
+ it 'should be condition_source_valuej' do
25
+ expect(subject.source_column).to eq(:condition_source_value)
26
+ end
27
+ end
28
+
29
+ describe '#vocabulary_id' do
30
+ it 'should be 203 (a J&J provided mapping as part of CPRD)' do
31
+ expect(subject.vocabulary_id).to eq(203)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/observation_period'
3
+ require_double('stream_for_casting')
4
+
5
+ describe ConceptQL::Operators::ObservationPeriod do
6
+ it_behaves_like(:casting_operator)
7
+ end
8
+
9
+
10
+
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/occurrence'
3
+ require_double('stream_for_occurrence')
4
+
5
+ describe ConceptQL::Operators::Occurrence do
6
+ it_behaves_like(:evaluator)
7
+
8
+ describe 'occurrence set to 1' do
9
+ subject do
10
+ described_class.new(1, StreamForOccurrenceDouble.new).query(Sequel.mock(host: 'postgres')).sql
11
+ end
12
+
13
+ it 'should order by ascending start_date' do
14
+ expect(subject).to match('ORDER BY "start_date" ASC')
15
+ end
16
+
17
+ it 'should partition by person_id' do
18
+ expect(subject).to match('PARTITION BY "person_id"')
19
+ end
20
+
21
+ it 'should assign a row number' do
22
+ expect(subject).to match('row_number()')
23
+ end
24
+
25
+ it 'should find the all rows with "rn" = 1' do
26
+ expect(subject).to match('"rn" = 1')
27
+ end
28
+ end
29
+
30
+ describe 'occurrence set to 2' do
31
+ subject do
32
+ described_class.new(2, StreamForOccurrenceDouble.new).query(Sequel.mock(host: 'postgres')).sql
33
+ end
34
+
35
+ it 'should order by ascending start_date' do
36
+ expect(subject).to match('ORDER BY "start_date" ASC')
37
+ end
38
+
39
+ it 'should find the all rows with "rn" = 2' do
40
+ expect(subject).to match('"rn" = 2')
41
+ end
42
+ end
43
+
44
+ describe 'occurrence set to -1' do
45
+ subject do
46
+ described_class.new(-1, StreamForOccurrenceDouble.new).query(Sequel.mock(host: 'postgres')).sql
47
+ end
48
+
49
+ it 'should order by ascending start_date' do
50
+ expect(subject).to match('ORDER BY "start_date" DESC')
51
+ end
52
+
53
+ it 'should find the all rows with "rn" = 1' do
54
+ expect(subject).to match('"rn" = 1')
55
+ end
56
+ end
57
+
58
+ describe 'occurrence set to -2' do
59
+ subject do
60
+ described_class.new(-2, StreamForOccurrenceDouble.new).query(Sequel.mock(host: 'postgres')).sql
61
+ end
62
+
63
+ it 'should order by ascending start_date' do
64
+ expect(subject).to match('ORDER BY "start_date" DESC')
65
+ end
66
+
67
+ it 'should find the all rows with "rn" = 2' do
68
+ expect(subject).to match('"rn" = 2')
69
+ end
70
+ end
71
+
72
+ describe 'occurrence respects types' do
73
+ subject do
74
+ dub = StreamForOccurrenceDouble.new
75
+ def dub.types
76
+ [:condition_occurrence]
77
+ end
78
+ described_class.new(-2, dub).query(Sequel.mock(host: 'postgres')).sql
79
+ end
80
+
81
+ it 'should order by ascending start_date' do
82
+ expect(subject).to match(', "criterion_type", "criterion_id"')
83
+ end
84
+ end
85
+ end
86
+
87
+
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/overlapped_by'
3
+ require_double('stream_for_temporal')
4
+
5
+ describe ConceptQL::Operators::OverlappedBy do
6
+ it_behaves_like(:temporal_operator)
7
+
8
+ describe 'when not inclusive' do
9
+ subject do
10
+ described_class.new(left: StreamForTemporalDouble.new, right: StreamForTemporalDouble.new)
11
+ end
12
+
13
+ it 'should use proper where clause' do
14
+ expect(subject.query(Sequel.mock).sql).to match('l.start_date <= r.end_date')
15
+ expect(subject.query(Sequel.mock).sql).to match('r.start_date <= l.start_date')
16
+ expect(subject.query(Sequel.mock).sql).to match('r.end_date <= l.end_date')
17
+ end
18
+ end
19
+
20
+ describe 'when inclusive' do
21
+ subject do
22
+ described_class.new(inclusive: true, left: StreamForTemporalDouble.new, right: StreamForTemporalDouble.new)
23
+ end
24
+
25
+ it 'should use proper where clause' do
26
+ expect(subject.query(Sequel.mock).sql).to match('l.start_date <= r.end_date')
27
+ expect(subject.query(Sequel.mock).sql).to match('r.start_date <= l.start_date')
28
+ end
29
+ end
30
+ end
31
+
32
+
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/overlaps'
3
+ require_double('stream_for_temporal')
4
+
5
+ describe ConceptQL::Operators::Overlaps do
6
+ it_behaves_like(:temporal_operator)
7
+
8
+ describe 'when not inclusive' do
9
+ subject do
10
+ described_class.new(left: StreamForTemporalDouble.new, right: StreamForTemporalDouble.new)
11
+ end
12
+
13
+ it 'should use proper where clause' do
14
+ expect(subject.query(Sequel.mock).sql).to match('r.start_date <= l.end_date')
15
+ expect(subject.query(Sequel.mock).sql).to match('l.start_date <= r.start_date')
16
+ expect(subject.query(Sequel.mock).sql).to match('l.end_date <= r.end_date')
17
+ end
18
+ end
19
+ end
20
+
21
+
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/person_filter'
3
+ require_relative 'query_double'
4
+
5
+ describe ConceptQL::Operators::PersonFilter do
6
+ it_behaves_like(:evaluator)
7
+
8
+ describe '#query' do
9
+ it 'uses right stream as argument to PERSON_FILTER against left stream' do
10
+ double1 = QueryDouble.new(1)
11
+ double2 = QueryDouble.new(2)
12
+ expect(ConceptQL::Operators::PersonFilter.new(left: double1, right: double2).query(Sequel.mock).sql).to eq("SELECT * FROM (SELECT * FROM table1) AS t1 WHERE (person_id IN (SELECT person_id FROM table2 GROUP BY person_id))")
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/person'
3
+ require_double('stream_for_casting')
4
+
5
+ describe ConceptQL::Operators::Person do
6
+ it_behaves_like(:casting_operator)
7
+ end
8
+
9
+
10
+