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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +53 -1
- data/Guardfile +52 -24
- data/conceptql.gemspec +5 -3
- data/doc/spec.md +2 -2
- data/lib/conceptql/behaviors/debuggable.rb +70 -0
- data/lib/conceptql/behaviors/dottable.rb +28 -14
- data/lib/conceptql/behaviors/metadatable.rb +97 -0
- data/lib/conceptql/behaviors/preppable.rb +20 -0
- data/lib/conceptql/cli.rb +31 -5
- data/lib/conceptql/converter.rb +65 -0
- data/lib/conceptql/debugger.rb +48 -0
- data/lib/conceptql/graph.rb +14 -13
- data/lib/conceptql/graph_nodifier.rb +49 -17
- data/lib/conceptql/nodifier.rb +31 -6
- data/lib/conceptql/operators/after.rb +25 -0
- data/lib/conceptql/operators/any_overlap.rb +15 -0
- data/lib/conceptql/operators/before.rb +21 -0
- data/lib/conceptql/{nodes/binary_operator_node.rb → operators/binary_operator_operator.rb} +12 -8
- data/lib/conceptql/{nodes/casting_node.rb → operators/casting_operator.rb} +8 -7
- data/lib/conceptql/{nodes → operators}/complement.rb +14 -11
- data/lib/conceptql/operators/concept.rb +70 -0
- data/lib/conceptql/{nodes → operators}/condition_type.rb +13 -5
- data/lib/conceptql/operators/contains.rb +17 -0
- data/lib/conceptql/operators/count.rb +26 -0
- data/lib/conceptql/operators/cpt.rb +25 -0
- data/lib/conceptql/{nodes → operators}/date_range.rb +11 -6
- data/lib/conceptql/operators/death.rb +23 -0
- data/lib/conceptql/operators/drug_type_concept.rb +21 -0
- data/lib/conceptql/{nodes → operators}/during.rb +9 -3
- data/lib/conceptql/operators/equal.rb +13 -0
- data/lib/conceptql/operators/except.rb +28 -0
- data/lib/conceptql/operators/filter.rb +21 -0
- data/lib/conceptql/{nodes → operators}/first.rb +7 -5
- data/lib/conceptql/{nodes → operators}/from.rb +2 -2
- data/lib/conceptql/operators/from_seer_visits.rb +23 -0
- data/lib/conceptql/{nodes → operators}/gender.rb +6 -6
- data/lib/conceptql/operators/hcpcs.rb +25 -0
- data/lib/conceptql/operators/icd10.rb +28 -0
- data/lib/conceptql/operators/icd9.rb +28 -0
- data/lib/conceptql/operators/icd9_procedure.rb +25 -0
- data/lib/conceptql/{nodes → operators}/intersect.rb +7 -3
- data/lib/conceptql/{nodes → operators}/last.rb +7 -5
- data/lib/conceptql/operators/loinc.rb +25 -0
- data/lib/conceptql/operators/medcode.rb +28 -0
- data/lib/conceptql/operators/medcode_procedure.rb +27 -0
- data/lib/conceptql/operators/ndc.rb +29 -0
- data/lib/conceptql/operators/numeric.rb +54 -0
- data/lib/conceptql/operators/observation_by_enttype.rb +29 -0
- data/lib/conceptql/operators/observation_period.rb +30 -0
- data/lib/conceptql/operators/occurrence.rb +70 -0
- data/lib/conceptql/operators/one_in_two_out.rb +62 -0
- data/lib/conceptql/{nodes/node.rb → operators/operator.rb} +84 -52
- data/lib/conceptql/operators/overlapped_by.rb +23 -0
- data/lib/conceptql/operators/overlaps.rb +19 -0
- data/lib/conceptql/operators/pass_thru.rb +11 -0
- data/lib/conceptql/{nodes → operators}/person.rb +8 -4
- data/lib/conceptql/operators/person_filter.rb +13 -0
- data/lib/conceptql/{nodes → operators}/place_of_service_code.rb +7 -7
- data/lib/conceptql/operators/procedure_occurrence.rb +25 -0
- data/lib/conceptql/operators/prodcode.rb +29 -0
- data/lib/conceptql/{nodes → operators}/race.rb +7 -7
- data/lib/conceptql/operators/recall.rb +38 -0
- data/lib/conceptql/operators/rxnorm.rb +24 -0
- data/lib/conceptql/operators/snomed.rb +24 -0
- data/lib/conceptql/operators/snomed_condition.rb +26 -0
- data/lib/conceptql/{nodes/source_vocabulary_node.rb → operators/source_vocabulary_operator.rb} +7 -4
- data/lib/conceptql/{nodes/standard_vocabulary_node.rb → operators/standard_vocabulary_operator.rb} +6 -4
- data/lib/conceptql/{nodes → operators}/started_by.rb +9 -3
- data/lib/conceptql/{nodes → operators}/sum.rb +9 -4
- data/lib/conceptql/{nodes/temporal_node.rb → operators/temporal_operator.rb} +7 -4
- data/lib/conceptql/{nodes → operators}/time_window.rb +19 -7
- data/lib/conceptql/operators/to_seer_visits.rb +24 -0
- data/lib/conceptql/operators/trim_date_end.rb +55 -0
- data/lib/conceptql/operators/trim_date_start.rb +56 -0
- data/lib/conceptql/{nodes → operators}/union.rb +6 -2
- data/lib/conceptql/operators/visit.rb +15 -0
- data/lib/conceptql/{nodes → operators}/visit_occurrence.rb +7 -3
- data/lib/conceptql/query.rb +19 -17
- data/lib/conceptql/scope.rb +69 -0
- data/lib/conceptql/tree.rb +33 -18
- data/lib/conceptql/utils/temp_table.rb +72 -0
- data/lib/conceptql/version.rb +1 -1
- data/spec/conceptql/behaviors/dottable_spec.rb +39 -51
- data/spec/conceptql/converter_spec.rb +51 -0
- data/spec/conceptql/date_adjuster_spec.rb +15 -15
- data/spec/conceptql/operators/after_spec.rb +16 -0
- data/spec/conceptql/operators/before_spec.rb +16 -0
- data/spec/conceptql/{nodes/casting_node_spec.rb → operators/casting_operator_spec.rb} +16 -20
- data/spec/conceptql/operators/complement_spec.rb +15 -0
- data/spec/conceptql/operators/concept_spec.rb +40 -0
- data/spec/conceptql/{nodes → operators}/condition_type_spec.rb +39 -24
- data/spec/conceptql/operators/contains_spec.rb +19 -0
- data/spec/conceptql/operators/cpt_spec.rb +29 -0
- data/spec/conceptql/operators/date_range_spec.rb +33 -0
- data/spec/conceptql/operators/death_spec.rb +10 -0
- data/spec/conceptql/operators/during_spec.rb +30 -0
- data/spec/conceptql/operators/except_spec.rb +15 -0
- data/spec/conceptql/operators/first_spec.rb +35 -0
- data/spec/conceptql/operators/from_spec.rb +13 -0
- data/spec/conceptql/operators/gender_spec.rb +27 -0
- data/spec/conceptql/operators/hcpcs_spec.rb +29 -0
- data/spec/conceptql/operators/icd10_spec.rb +34 -0
- data/spec/conceptql/operators/icd9_procedure_spec.rb +29 -0
- data/spec/conceptql/operators/icd9_spec.rb +34 -0
- data/spec/conceptql/operators/intersect_spec.rb +28 -0
- data/spec/conceptql/operators/last_spec.rb +36 -0
- data/spec/conceptql/operators/loinc_spec.rb +29 -0
- data/spec/conceptql/operators/medcode_procedure_spec.rb +34 -0
- data/spec/conceptql/operators/medcode_spec.rb +34 -0
- data/spec/conceptql/operators/observation_period_spec.rb +10 -0
- data/spec/conceptql/operators/occurrence_spec.rb +87 -0
- data/spec/conceptql/operators/overlapped_by_spec.rb +32 -0
- data/spec/conceptql/operators/overlaps_spec.rb +21 -0
- data/spec/conceptql/operators/person_filter_spec.rb +15 -0
- data/spec/conceptql/operators/person_spec.rb +10 -0
- data/spec/conceptql/{nodes → operators}/place_of_service_code_spec.rb +6 -8
- data/spec/conceptql/operators/procedure_occurrence_spec.rb +10 -0
- data/spec/conceptql/operators/prodcode_spec.rb +35 -0
- data/spec/conceptql/operators/query_double.rb +20 -0
- data/spec/conceptql/operators/query_double_spec.rb +7 -0
- data/spec/conceptql/operators/race_spec.rb +21 -0
- data/spec/conceptql/operators/rxnorm_spec.rb +29 -0
- data/spec/conceptql/operators/snomed_spec.rb +29 -0
- data/spec/conceptql/operators/source_vocabulary_operator_spec.rb +35 -0
- data/spec/conceptql/operators/standard_vocabulary_operator_spec.rb +35 -0
- data/spec/conceptql/operators/started_by_spec.rb +22 -0
- data/spec/conceptql/{nodes/temporal_node_spec.rb → operators/temporal_operator_spec.rb} +11 -17
- data/spec/conceptql/operators/time_window_spec.rb +77 -0
- data/spec/conceptql/operators/union_spec.rb +21 -0
- data/spec/conceptql/operators/visit_occurrence_spec.rb +10 -0
- data/spec/conceptql/query_spec.rb +10 -9
- data/spec/conceptql/tree_spec.rb +24 -28
- data/spec/doubles/stream_for_casting_double.rb +1 -1
- data/spec/doubles/stream_for_occurrence_double.rb +1 -1
- data/spec/doubles/stream_for_temporal_double.rb +1 -1
- data/spec/spec_helper.rb +74 -58
- metadata +202 -133
- data/lib/conceptql/nodes/after.rb +0 -12
- data/lib/conceptql/nodes/before.rb +0 -11
- data/lib/conceptql/nodes/concept.rb +0 -38
- data/lib/conceptql/nodes/count.rb +0 -23
- data/lib/conceptql/nodes/cpt.rb +0 -20
- data/lib/conceptql/nodes/death.rb +0 -19
- data/lib/conceptql/nodes/define.rb +0 -96
- data/lib/conceptql/nodes/drug_type_concept.rb +0 -18
- data/lib/conceptql/nodes/equal.rb +0 -11
- data/lib/conceptql/nodes/except.rb +0 -11
- data/lib/conceptql/nodes/hcpcs.rb +0 -20
- data/lib/conceptql/nodes/icd10.rb +0 -23
- data/lib/conceptql/nodes/icd9.rb +0 -23
- data/lib/conceptql/nodes/icd9_procedure.rb +0 -20
- data/lib/conceptql/nodes/loinc.rb +0 -20
- data/lib/conceptql/nodes/numeric.rb +0 -40
- data/lib/conceptql/nodes/occurrence.rb +0 -49
- data/lib/conceptql/nodes/pass_thru.rb +0 -11
- data/lib/conceptql/nodes/person_filter.rb +0 -12
- data/lib/conceptql/nodes/procedure_occurrence.rb +0 -21
- data/lib/conceptql/nodes/recall.rb +0 -50
- data/lib/conceptql/nodes/rxnorm.rb +0 -20
- data/lib/conceptql/nodes/snomed.rb +0 -19
- data/lib/conceptql/nodes/visit.rb +0 -11
- data/spec/conceptql/nodes/after_spec.rb +0 -18
- data/spec/conceptql/nodes/before_spec.rb +0 -18
- data/spec/conceptql/nodes/complement_spec.rb +0 -15
- data/spec/conceptql/nodes/concept_spec.rb +0 -34
- data/spec/conceptql/nodes/cpt_spec.rb +0 -31
- data/spec/conceptql/nodes/date_range_spec.rb +0 -35
- data/spec/conceptql/nodes/death_spec.rb +0 -12
- data/spec/conceptql/nodes/during_spec.rb +0 -32
- data/spec/conceptql/nodes/except_spec.rb +0 -18
- data/spec/conceptql/nodes/first_spec.rb +0 -37
- data/spec/conceptql/nodes/from_spec.rb +0 -15
- data/spec/conceptql/nodes/gender_spec.rb +0 -29
- data/spec/conceptql/nodes/hcpcs_spec.rb +0 -31
- data/spec/conceptql/nodes/icd10_spec.rb +0 -36
- data/spec/conceptql/nodes/icd9_procedure_spec.rb +0 -31
- data/spec/conceptql/nodes/icd9_spec.rb +0 -36
- data/spec/conceptql/nodes/intersect_spec.rb +0 -33
- data/spec/conceptql/nodes/last_spec.rb +0 -38
- data/spec/conceptql/nodes/loinc_spec.rb +0 -31
- data/spec/conceptql/nodes/occurrence_spec.rb +0 -89
- data/spec/conceptql/nodes/person_filter_spec.rb +0 -18
- data/spec/conceptql/nodes/person_spec.rb +0 -12
- data/spec/conceptql/nodes/procedure_occurrence_spec.rb +0 -12
- data/spec/conceptql/nodes/query_double.rb +0 -19
- data/spec/conceptql/nodes/race_spec.rb +0 -23
- data/spec/conceptql/nodes/rxnorm_spec.rb +0 -31
- data/spec/conceptql/nodes/snomed_spec.rb +0 -31
- data/spec/conceptql/nodes/source_vocabulary_node_spec.rb +0 -37
- data/spec/conceptql/nodes/standard_vocabulary_node_spec.rb +0 -40
- data/spec/conceptql/nodes/started_by_spec.rb +0 -25
- data/spec/conceptql/nodes/time_window_spec.rb +0 -85
- data/spec/conceptql/nodes/union_spec.rb +0 -25
- data/spec/conceptql/nodes/visit_occurrence_spec.rb +0 -12
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
require 'conceptql/
|
|
2
|
+
require 'conceptql/operators/place_of_service_code'
|
|
3
3
|
|
|
4
|
-
describe ConceptQL::
|
|
5
|
-
|
|
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::
|
|
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::
|
|
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::
|
|
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,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,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/
|
|
2
|
+
require 'conceptql/operators/temporal_operator'
|
|
3
3
|
require_double('stream_for_temporal')
|
|
4
4
|
|
|
5
|
-
describe ConceptQL::
|
|
6
|
-
|
|
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::
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
26
|
+
expect(TemporalDouble.new.inclusive?).to be_falsy
|
|
33
27
|
end
|
|
34
28
|
|
|
35
29
|
it 'can be set to true' do
|
|
36
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
@@ -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 =
|
|
9
|
-
|
|
10
|
-
mock_query =
|
|
8
|
+
mock_tree = double("tree")
|
|
9
|
+
mock_operator = double("operator")
|
|
10
|
+
mock_query = double("query")
|
|
11
|
+
mock_db = double("db")
|
|
11
12
|
|
|
12
|
-
|
|
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
|
-
|
|
18
|
-
mock_tree.
|
|
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
|
data/spec/conceptql/tree_spec.rb
CHANGED
|
@@ -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 =
|
|
8
|
-
@mock_nodifier =
|
|
9
|
-
@
|
|
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
|
-
|
|
13
|
-
@
|
|
14
|
-
@
|
|
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).
|
|
17
|
+
expect(@tree.root(@mock_query_obj)).to eq(:success_indicator)
|
|
22
18
|
end
|
|
23
19
|
|
|
24
|
-
it 'should extend all
|
|
25
|
-
mock_icd9_obj =
|
|
26
|
-
mock_icd9_obj.
|
|
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.
|
|
30
|
-
@mock_query_obj.
|
|
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
|
|
36
|
-
@mock_nodifier.
|
|
37
|
-
@mock_nodifier.
|
|
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.
|
|
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).
|
|
37
|
+
expect(@tree.root(@mock_query_obj)).to eq(:success_indicator)
|
|
42
38
|
end
|
|
43
39
|
|
|
44
|
-
it 'should walk multi-
|
|
45
|
-
@mock_nodifier.
|
|
46
|
-
@mock_nodifier.
|
|
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.
|
|
44
|
+
expect(@mock_query_obj).to receive(:statement).and_return({ any: [{ icd9: '799.22' }] })
|
|
49
45
|
|
|
50
|
-
@tree.root(@mock_query_obj).
|
|
46
|
+
expect(@tree.root(@mock_query_obj)).to eq(:success_indicator)
|
|
51
47
|
end
|
|
52
48
|
end
|
|
53
49
|
end
|