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,25 +1,23 @@
1
1
  require 'spec_helper'
2
- require 'conceptql/nodes/place_of_service_code'
2
+ require 'conceptql/operators/place_of_service_code'
3
3
 
4
- describe ConceptQL::Nodes::PlaceOfServiceCode do
5
- it 'behaves itself' do
6
- ConceptQL::Nodes::PlaceOfServiceCode.new.must_behave_like(:evaluator)
7
- end
4
+ describe ConceptQL::Operators::PlaceOfServiceCode do
5
+ it_behaves_like(:evaluator)
8
6
 
9
7
  describe '#query' do
10
8
  it 'works for 23' do
11
9
  correct_query = "SELECT * FROM visit_occurrence AS v INNER JOIN vocabulary.concept AS vc ON (vc.concept_id = v.place_of_service_concept_id) WHERE ((vc.concept_code IN ('23')) AND (vc.vocabulary_id = 14))"
12
- ConceptQL::Nodes::PlaceOfServiceCode.new('23').query(Sequel.mock).sql.must_equal correct_query
10
+ expect(ConceptQL::Operators::PlaceOfServiceCode.new('23').query(Sequel.mock).sql).to eq(correct_query)
13
11
  end
14
12
 
15
13
  it 'works for 23 as number' do
16
14
  correct_query = "SELECT * FROM visit_occurrence AS v INNER JOIN vocabulary.concept AS vc ON (vc.concept_id = v.place_of_service_concept_id) WHERE ((vc.concept_code IN ('23')) AND (vc.vocabulary_id = 14))"
17
- ConceptQL::Nodes::PlaceOfServiceCode.new(23).query(Sequel.mock).sql.must_equal correct_query
15
+ expect(ConceptQL::Operators::PlaceOfServiceCode.new(23).query(Sequel.mock).sql).to eq(correct_query)
18
16
  end
19
17
 
20
18
  it 'works for multiple values' do
21
19
  correct_query = "SELECT * FROM visit_occurrence AS v INNER JOIN vocabulary.concept AS vc ON (vc.concept_id = v.place_of_service_concept_id) WHERE ((vc.concept_code IN ('23', '22')) AND (vc.vocabulary_id = 14))"
22
- ConceptQL::Nodes::PlaceOfServiceCode.new('23', '22').query(Sequel.mock).sql.must_equal correct_query
20
+ expect(ConceptQL::Operators::PlaceOfServiceCode.new('23', '22').query(Sequel.mock).sql).to eq(correct_query)
23
21
  end
24
22
  end
25
23
  end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/procedure_occurrence'
3
+ require_double('stream_for_casting')
4
+
5
+ describe ConceptQL::Operators::ProcedureOccurrence do
6
+ it_behaves_like(:casting_operator)
7
+ end
8
+
9
+
10
+
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/prodcode'
3
+
4
+ describe ConceptQL::Operators::Prodcode 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 drug_exposure' do
13
+ expect(subject.table).to eq(:drug_exposure)
14
+ end
15
+ end
16
+
17
+ describe '#concept_column' do
18
+ it 'should be drug_concept_id' do
19
+ expect(subject.concept_column).to eq(:drug_concept_id)
20
+ end
21
+ end
22
+
23
+ describe '#source_column' do
24
+ it 'should be drug_source_valuej' do
25
+ expect(subject.source_column).to eq(:drug_source_value)
26
+ end
27
+ end
28
+
29
+ describe '#vocabulary_id' do
30
+ it 'should be 200 (a J&J provided mapping as part of CPRD)' do
31
+ expect(subject.vocabulary_id).to eq(200)
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,20 @@
1
+ require 'conceptql/operators/operator'
2
+ class QueryDouble < ConceptQL::Operators::Operator
3
+ def initialize(*args)
4
+ super
5
+ @num = arguments.first
6
+ @type = arguments[1] || :visit_occurrence
7
+ end
8
+
9
+ def types
10
+ [@type]
11
+ end
12
+
13
+ def evaluate(db)
14
+ query(db)
15
+ end
16
+
17
+ def query(db)
18
+ db["table#{@num}".to_sym]
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+ require_relative 'query_double'
3
+
4
+ describe QueryDouble do
5
+ it_behaves_like(:evaluator)
6
+ end
7
+
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/race'
3
+
4
+ describe ConceptQL::Operators::Race do
5
+ it_behaves_like(:evaluator)
6
+
7
+ describe '#query' do
8
+ it 'works for white' do
9
+ correct_query = "SELECT * FROM person AS p INNER JOIN vocabulary.concept AS vc ON (vc.concept_id = p.race_concept_id) WHERE (lower(vc.concept_name) IN ('white'))"
10
+ expect(ConceptQL::Operators::Race.new('White').query(Sequel.mock).sql).to eq(correct_query)
11
+ expect(ConceptQL::Operators::Race.new('white').query(Sequel.mock).sql).to eq(correct_query)
12
+ end
13
+
14
+ it 'works for multiple values' do
15
+ correct_query = "SELECT * FROM person AS p INNER JOIN vocabulary.concept AS vc ON (vc.concept_id = p.race_concept_id) WHERE (lower(vc.concept_name) IN ('white', 'other'))"
16
+ expect(ConceptQL::Operators::Race.new('White', 'Other').query(Sequel.mock).sql).to eq(correct_query)
17
+ expect(ConceptQL::Operators::Race.new('white', 'other').query(Sequel.mock).sql).to eq(correct_query)
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/rxnorm'
3
+
4
+ describe ConceptQL::Operators::Rxnorm 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 drug_exposure' do
13
+ expect(subject.table).to eq(:drug_exposure)
14
+ end
15
+ end
16
+
17
+ describe '#concept_column' do
18
+ it 'should be drug_concept_id' do
19
+ expect(subject.concept_column).to eq(:drug_concept_id)
20
+ end
21
+ end
22
+
23
+ describe '#vocabulary_id' do
24
+ it 'should be 8' do
25
+ expect(subject.vocabulary_id).to eq(8)
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/snomed'
3
+
4
+ describe ConceptQL::Operators::Snomed 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 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 '#vocabulary_id' do
24
+ it 'should be 1' do
25
+ expect(subject.vocabulary_id).to eq(1)
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/source_vocabulary_operator'
3
+
4
+ describe ConceptQL::Operators::SourceVocabularyOperator do
5
+ it_behaves_like(:evaluator)
6
+
7
+ class SourceVocabularyDouble < ConceptQL::Operators::SourceVocabularyOperator
8
+ def table
9
+ :table
10
+ end
11
+
12
+ def source_column
13
+ :source_column
14
+ end
15
+
16
+ def concept_column
17
+ :concept_column
18
+ end
19
+
20
+ def vocabulary_id
21
+ 1
22
+ end
23
+ end
24
+
25
+ describe '#query' do
26
+ it 'works for single values' do
27
+ expect(SourceVocabularyDouble.new('value').query(Sequel.mock).sql).to eq("SELECT * FROM table AS tab INNER JOIN vocabulary.source_to_concept_map AS scm ON (scm.target_concept_id = tab.concept_column) WHERE ((scm.source_code IN ('value')) AND (scm.source_vocabulary_id = 1) AND (scm.source_code = tab.source_column))")
28
+ end
29
+
30
+ it 'works for multiple values' do
31
+ expect(SourceVocabularyDouble.new('value1', 'value2').query(Sequel.mock).sql).to eq("SELECT * FROM table AS tab INNER JOIN vocabulary.source_to_concept_map AS scm ON (scm.target_concept_id = tab.concept_column) WHERE ((scm.source_code IN ('value1', 'value2')) AND (scm.source_vocabulary_id = 1) AND (scm.source_code = tab.source_column))")
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/standard_vocabulary_operator'
3
+
4
+ describe ConceptQL::Operators::StandardVocabularyOperator do
5
+ it_behaves_like(:evaluator)
6
+
7
+ class StandardVocabularyDouble < ConceptQL::Operators::StandardVocabularyOperator
8
+ def table
9
+ :table
10
+ end
11
+
12
+ def concept_column
13
+ :concept_column
14
+ end
15
+
16
+ def vocabulary_id
17
+ 1
18
+ end
19
+ end
20
+
21
+ describe StandardVocabularyDouble do
22
+ it_behaves_like(:standard_vocabulary_operator)
23
+ end
24
+
25
+ describe '#query' do
26
+ it 'works for single values' do
27
+ expect(StandardVocabularyDouble.new('value').query(Sequel.mock).sql).to eq("SELECT * FROM table AS tab INNER JOIN vocabulary.concept AS c ON (c.concept_id = tab.concept_column) WHERE ((c.concept_code IN ('value')) AND (c.vocabulary_id = 1))")
28
+ end
29
+
30
+ it 'works for multiple diagnoses' do
31
+ expect(StandardVocabularyDouble.new('value1', 'value2').query(Sequel.mock).sql).to eq("SELECT * FROM table AS tab INNER JOIN vocabulary.concept AS c ON (c.concept_id = tab.concept_column) WHERE ((c.concept_code IN ('value1', 'value2')) AND (c.vocabulary_id = 1))")
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/started_by'
3
+ require_double('stream_for_temporal')
4
+
5
+ describe ConceptQL::Operators::StartedBy do
6
+ it_behaves_like(:temporal_operator)
7
+ subject do
8
+ described_class.new(left: StreamForTemporalDouble.new, right: StreamForTemporalDouble.new)
9
+ end
10
+
11
+ it 'should use proper where clause' do
12
+ expect(subject.query(Sequel.mock).sql).to match('l.start_date = r.start_date')
13
+ expect(subject.query(Sequel.mock).sql).to match('l.end_date > r.end_date')
14
+ end
15
+
16
+ it 'should use proper where clause when inclusive' do
17
+ sub = ConceptQL::Operators::StartedBy.new(left: StreamForTemporalDouble.new, right: StreamForTemporalDouble.new, inclusive: true)
18
+ expect(sub.query(Sequel.mock).sql).to match('l.start_date = r.start_date')
19
+ expect(sub.query(Sequel.mock).sql).to match('l.end_date >= r.end_date')
20
+ end
21
+ end
22
+
@@ -1,13 +1,11 @@
1
1
  require 'spec_helper'
2
- require 'conceptql/nodes/temporal_node'
2
+ require 'conceptql/operators/temporal_operator'
3
3
  require_double('stream_for_temporal')
4
4
 
5
- describe ConceptQL::Nodes::TemporalNode do
6
- it 'behaves itself' do
7
- ConceptQL::Nodes::TemporalNode.new.must_behave_like(:evaluator)
8
- end
5
+ describe ConceptQL::Operators::TemporalOperator do
6
+ it_behaves_like(:evaluator)
9
7
 
10
- class TemporalDouble < ConceptQL::Nodes::TemporalNode
8
+ class TemporalDouble < ConceptQL::Operators::TemporalOperator
11
9
  def where_clause
12
10
  Proc.new do
13
11
  l__end_date < r__start_date
@@ -16,41 +14,37 @@ describe ConceptQL::Nodes::TemporalNode do
16
14
  end
17
15
 
18
16
  describe TemporalDouble do
19
- it 'behaves itself' do
20
- TemporalDouble.new.must_behave_like(:temporal_node)
21
- end
17
+ it_behaves_like(:temporal_operator)
22
18
  end
23
19
 
24
20
  describe StreamForTemporalDouble do
25
- it 'behaves itself' do
26
- StreamForTemporalDouble.new.must_behave_like(:evaluator)
27
- end
21
+ it_behaves_like(:evaluator)
28
22
  end
29
23
 
30
24
  describe '#inclusive?' do
31
25
  it 'defaults to false' do
32
- refute(TemporalDouble.new.inclusive?)
26
+ expect(TemporalDouble.new.inclusive?).to be_falsy
33
27
  end
34
28
 
35
29
  it 'can be set to true' do
36
- assert(TemporalDouble.new(inclusive: true).inclusive?)
30
+ expect(TemporalDouble.new(inclusive: true).inclusive?).to be_truthy
37
31
  end
38
32
  end
39
33
 
40
34
  describe '#query' do
41
35
  it 'uses logic from where_clause' do
42
36
  sql = TemporalDouble.new(left: StreamForTemporalDouble.new, right: StreamForTemporalDouble.new).query(Sequel.mock).sql
43
- sql.must_match('l.end_date < r.start_date')
37
+ expect(sql).to match('l.end_date < r.start_date')
44
38
  end
45
39
 
46
40
  it 'pulls from the right tables' do
47
41
  sql = TemporalDouble.new(left: StreamForTemporalDouble.new, right: StreamForTemporalDouble.new).query(Sequel.mock).sql
48
- sql.must_match('FROM table')
42
+ expect(sql).to match('FROM table')
49
43
  end
50
44
 
51
45
  it 'is ok with symbols' do
52
46
  sql = TemporalDouble.new(left: StreamForTemporalDouble.new, right: StreamForTemporalDouble.new).query(Sequel.mock).sql
53
- sql.must_match('FROM table')
47
+ expect(sql).to match('FROM table')
54
48
  end
55
49
  end
56
50
  end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/time_window'
3
+ require 'conceptql/operators/operator'
4
+
5
+ describe ConceptQL::Operators::TimeWindow do
6
+ class Stream4TimeWindowDouble < ConceptQL::Operators::Operator
7
+ def types
8
+ [:visit_occurrence]
9
+ end
10
+
11
+ def query(db)
12
+ db.from(:table)
13
+ end
14
+ end
15
+
16
+ let (:sequel_mock) do
17
+ sequel_mock = double("sequel")
18
+ end
19
+
20
+ before do
21
+ @db_mock = Sequel.mock
22
+ end
23
+
24
+ describe '#evaluate' do
25
+ it 'adjusts start by 1 day' do
26
+ expect(sequel_mock).to receive(:date_add).with(Sequel.identifier("start_date"), days: 1).and_return(sequel_mock)
27
+ expect(@db_mock).to receive(:extension).with(:date_arithmetic)
28
+
29
+ described_class.new(Stream4TimeWindowDouble.new, { start: 'd', end: '', date_manipulator: sequel_mock }).evaluate(@db_mock)
30
+ end
31
+
32
+ it 'adjusts end by 1 day' do
33
+ expect(sequel_mock).to receive(:date_add).with(Sequel.identifier("end_date"), days: 1).and_return(sequel_mock)
34
+ described_class.new(Stream4TimeWindowDouble.new, { start: '', end: 'd', date_manipulator: sequel_mock }).evaluate(@db_mock)
35
+ end
36
+
37
+ it 'adjusts both values by 1 day' do
38
+ expect(sequel_mock).to receive(:date_add).with(Sequel.identifier("start_date"), days: 1).and_return(sequel_mock)
39
+ expect(sequel_mock).to receive(:date_add).with(Sequel.identifier("end_date"), days: 1).and_return(sequel_mock)
40
+ described_class.new(Stream4TimeWindowDouble.new, { start: 'd', end: 'd', date_manipulator: sequel_mock }).evaluate(@db_mock)
41
+ end
42
+
43
+ it 'makes multiple adjustments to both values' do
44
+ expect(sequel_mock).to receive(:date_add).with(Sequel.identifier("start_date"), days: 1).and_return(sequel_mock)
45
+ expect(sequel_mock).to receive(:date_add).with(sequel_mock, months: 1).and_return(sequel_mock)
46
+ expect(sequel_mock).to receive(:date_add).with(sequel_mock, years: 1).and_return(sequel_mock)
47
+
48
+ expect(sequel_mock).to receive(:date_sub).with(Sequel.identifier("end_date"), days: 1).and_return(sequel_mock)
49
+ expect(sequel_mock).to receive(:date_sub).with(sequel_mock, months: 1).and_return(sequel_mock)
50
+ expect(sequel_mock).to receive(:date_sub).with(sequel_mock, years: 1).and_return(sequel_mock)
51
+
52
+ described_class.new(Stream4TimeWindowDouble.new, { start: 'dmy', end: '-d-m-y', date_manipulator: sequel_mock }).evaluate(@db_mock)
53
+ end
54
+
55
+ it 'can set start_date and end_date to specific dates' do
56
+ described_class.new(Stream4TimeWindowDouble.new, { start: '2000-01-01', end: '2000-02-02' }).evaluate(@db_mock)
57
+ end
58
+
59
+ it 'can set start_date to be end_date' do
60
+ described_class.new(Stream4TimeWindowDouble.new, { start: 'end', end: '' }).evaluate(@db_mock)
61
+ end
62
+
63
+ it 'can set end_date to be start_date' do
64
+ described_class.new(Stream4TimeWindowDouble.new, { start: '', end: 'start' }).evaluate(@db_mock)
65
+ end
66
+
67
+ it 'will swap start and end dates, though this is a bad idea but you should probably know about this' do
68
+ described_class.new(Stream4TimeWindowDouble.new, { start: 'end', end: 'start' }).evaluate(@db_mock)
69
+ end
70
+
71
+ it 'handles nil arguments to both start and end' do
72
+ described_class.new(Stream4TimeWindowDouble.new, { start: nil, end: nil }).evaluate(@db_mock)
73
+ end
74
+ end
75
+ end
76
+
77
+
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/union'
3
+ require_relative 'query_double'
4
+
5
+ describe ConceptQL::Operators::Union do
6
+ it_behaves_like(:evaluator)
7
+
8
+ describe '#query' do
9
+ it 'works for multiple criteria' do
10
+ double1 = QueryDouble.new(1)
11
+ double2 = QueryDouble.new(2)
12
+ double3 = QueryDouble.new(3)
13
+ expect(described_class.new(double1, double2, double3).query(Sequel.mock).sql).to eq("SELECT * FROM (SELECT * FROM (SELECT * FROM (SELECT * FROM table1) AS t1 UNION ALL SELECT * FROM (SELECT * FROM table2) AS t1) AS t1 UNION ALL SELECT * FROM (SELECT * FROM table3) AS t1) AS t1")
14
+ end
15
+
16
+ it 'works for single criteria' do
17
+ double1 = QueryDouble.new(1)
18
+ expect(described_class.new(double1).query(Sequel.mock).sql).to eq("SELECT * FROM (SELECT * FROM table1) AS t1")
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'conceptql/operators/visit_occurrence'
3
+ require_double('stream_for_casting')
4
+
5
+ describe ConceptQL::Operators::VisitOccurrence do
6
+ it_behaves_like(:casting_operator)
7
+ end
8
+
9
+
10
+
@@ -5,17 +5,18 @@ describe ConceptQL::Query do
5
5
  describe '#query' do
6
6
  it 'passes request on to tree' do
7
7
  yaml = Psych.dump({ icd9: '799.22' })
8
- mock_tree = Minitest::Mock.new
9
- mock_node = Minitest::Mock.new
10
- mock_query = Minitest::Mock.new
8
+ mock_tree = double("tree")
9
+ mock_operator = double("operator")
10
+ mock_query = double("query")
11
+ mock_db = double("db")
11
12
 
12
- query = ConceptQL::Query.new(:mock_db, yaml, mock_tree)
13
- mock_tree.expect :root, mock_node, [query]
14
- mock_node.expect :map, [mock_query]
15
- query.query
13
+ expect(mock_db).to receive(:extend_datasets).with(Module).and_return(mock_db)
16
14
 
17
- mock_node.verify
18
- mock_tree.verify
15
+ query = ConceptQL::Query.new(mock_db, yaml, mock_tree)
16
+ expect(mock_tree).to receive(:root).with(query).and_return(mock_operator)
17
+ expect(mock_operator).to receive(:evaluate).with(mock_db).and_return(mock_query)
18
+ expect(mock_query).to receive(:tap).and_return(mock_query)
19
+ query.query
19
20
  end
20
21
  end
21
22
  end
@@ -4,50 +4,46 @@ require 'conceptql/tree'
4
4
  describe ConceptQL::Tree do
5
5
  describe '#root' do
6
6
  before do
7
- @mock_query_obj = Minitest::Mock.new
8
- @mock_nodifier = Minitest::Mock.new
9
- @tree = ConceptQL::Tree.new(nodifier: @mock_nodifier)
7
+ @mock_query_obj = double("mock query")
8
+ @mock_nodifier = double("mock nodifier")
9
+ @mock_scope = double("mock scope")
10
+ @tree = ConceptQL::Tree.new(nodifier: @mock_nodifier, scope: @mock_scope)
10
11
  end
11
12
 
12
- after do
13
- @mock_query_obj.verify
14
- @mock_nodifier.verify
15
- end
16
-
17
- it 'should walk single node criteria tree and convert to node' do
18
- @mock_nodifier.expect :create, :success_indicator, [:icd9, '799.22', @tree]
19
- @mock_query_obj.expect :statement, { icd9: '799.22' }
13
+ it 'should walk single operator criteria tree and convert to operator' do
14
+ expect(@mock_nodifier).to receive(:create).with(@mock_scope, :icd9, '799.22').and_return(:success_indicator)
15
+ expect(@mock_query_obj).to receive(:statement).and_return({ icd9: '799.22' })
20
16
 
21
- @tree.root(@mock_query_obj).must_equal [:success_indicator]
17
+ expect(@tree.root(@mock_query_obj)).to eq(:success_indicator)
22
18
  end
23
19
 
24
- it 'should extend all nodes created with behavior passed in' do
25
- mock_icd9_obj = Minitest::Mock.new
26
- mock_icd9_obj.expect :extend, nil, [:mock_behavior]
20
+ it 'should extend all operators created with behavior passed in' do
21
+ mock_icd9_obj = double("mock icd9")
22
+ expect(mock_icd9_obj).to receive(:extend).with(:mock_behavior)
27
23
 
28
- tree = ConceptQL::Tree.new(nodifier: @mock_nodifier, behavior: :mock_behavior)
29
- @mock_nodifier.expect :create, mock_icd9_obj, [:icd9, '799.22', tree]
30
- @mock_query_obj.expect :statement, { icd9: '799.22' }
24
+ tree = ConceptQL::Tree.new(nodifier: @mock_nodifier, behavior: :mock_behavior, scope: @mock_scope)
25
+ expect(@mock_nodifier).to receive(:create).with(@mock_scope, :icd9, '799.22').and_return(mock_icd9_obj)
26
+ expect(@mock_query_obj).to receive(:statement).and_return({ icd9: '799.22' })
31
27
 
32
28
  tree.root(@mock_query_obj)
33
29
  end
34
30
 
35
- it 'should walk multi-criteria node' do
36
- @mock_nodifier.expect :create, :mock_icd9, [:icd9, '799.22', @tree]
37
- @mock_nodifier.expect :create, :success_indicator, [:nth, { occurrence: 1, expression: :mock_icd9 }, @tree]
31
+ it 'should walk multi-criteria operator' do
32
+ expect(@mock_nodifier).to receive(:create).with(@mock_scope, :icd9, '799.22').and_return(:mock_icd9)
33
+ expect(@mock_nodifier).to receive(:create).with(@mock_scope, :nth, { occurrence: 1, expression: :mock_icd9 }).and_return(:success_indicator)
38
34
 
39
- @mock_query_obj.expect :statement, { nth: { occurrence: 1, expression: { icd9: '799.22' } } }
35
+ expect(@mock_query_obj).to receive(:statement).and_return({ nth: { occurrence: 1, expression: { icd9: '799.22' } } })
40
36
 
41
- @tree.root(@mock_query_obj).must_equal [:success_indicator]
37
+ expect(@tree.root(@mock_query_obj)).to eq(:success_indicator)
42
38
  end
43
39
 
44
- it 'should walk multi-node criteria tree and convert to nodes' do
45
- @mock_nodifier.expect :create, :mock_icd9, [:icd9, '799.22', @tree]
46
- @mock_nodifier.expect :create, :success_indicator, [:any, [:mock_icd9], @tree]
40
+ it 'should walk multi-operator criteria tree and convert to operators' do
41
+ expect(@mock_nodifier).to receive(:create).with(@mock_scope, :icd9, '799.22').and_return(:mock_icd9)
42
+ expect(@mock_nodifier).to receive(:create).with(@mock_scope, :any, :mock_icd9).and_return(:success_indicator)
47
43
 
48
- @mock_query_obj.expect :statement, { any: [{ icd9: '799.22' }] }
44
+ expect(@mock_query_obj).to receive(:statement).and_return({ any: [{ icd9: '799.22' }] })
49
45
 
50
- @tree.root(@mock_query_obj).must_equal [:success_indicator]
46
+ expect(@tree.root(@mock_query_obj)).to eq(:success_indicator)
51
47
  end
52
48
  end
53
49
  end
@@ -1,4 +1,4 @@
1
- class StreamForCastingDouble < ConceptQL::Nodes::Node
1
+ class StreamForCastingDouble < ConceptQL::Operators::Operator
2
2
  def query(db)
3
3
  db.from(:table)
4
4
  end
@@ -1,4 +1,4 @@
1
- class StreamForOccurrenceDouble < ConceptQL::Nodes::Node
1
+ class StreamForOccurrenceDouble < ConceptQL::Operators::Operator
2
2
  def query(db)
3
3
  ds = db.from(:table)
4
4
  # Occurrence needs window functions to work
@@ -1,4 +1,4 @@
1
- class StreamForTemporalDouble < ConceptQL::Nodes::Node
1
+ class StreamForTemporalDouble < ConceptQL::Operators::Operator
2
2
  def query(db)
3
3
  db.from(:table)
4
4
  end