conceptql 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'casting_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class Death < CastingOperator
|
6
|
+
desc 'Generates all death records, or, if fed a stream, fetches all death records for the people represented in the incoming result set.'
|
7
|
+
types :death
|
8
|
+
allows_one_upstream
|
9
|
+
|
10
|
+
def my_type
|
11
|
+
:death
|
12
|
+
end
|
13
|
+
|
14
|
+
def i_point_at
|
15
|
+
[ :person ]
|
16
|
+
end
|
17
|
+
|
18
|
+
def these_point_at_me
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class DrugTypeConcept < Operator
|
6
|
+
desc 'Given a set of concept IDs in RxNorm, returns that set of drug exposures'
|
7
|
+
argument :concept_ids, type: :codelist, vocab: 'RxNorm'
|
8
|
+
|
9
|
+
def type
|
10
|
+
:drug_exposure
|
11
|
+
end
|
12
|
+
|
13
|
+
def query(db)
|
14
|
+
db.from(:drug_exposure)
|
15
|
+
.where(drug_type_concept_id: arguments)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
@@ -1,8 +1,14 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'temporal_operator'
|
2
2
|
|
3
3
|
module ConceptQL
|
4
|
-
module
|
5
|
-
class During <
|
4
|
+
module Operators
|
5
|
+
class During < TemporalOperator
|
6
|
+
desc <<-EOF
|
7
|
+
Compares all results on a person-by-person basis between the left hand results (LHR) and the right hand resuls (RHR).
|
8
|
+
For any result in the LHR whose start_date and end_date occur within the start_date and end_date of a RHR row, that result is passed through.
|
9
|
+
All other results are discarded, including all results in the RHR.
|
10
|
+
EOF
|
11
|
+
|
6
12
|
def where_clause
|
7
13
|
if inclusive?
|
8
14
|
Sequel.expr(Sequel.expr(Proc.new { r__start_date <= l__start_date}).&(Sequel.expr( Proc.new { l__start_date <= r__end_date })))
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'temporal_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class Equal < TemporalOperator
|
6
|
+
desc 'If a LHR result has the same value_as_number as a RHR result, it is passed through'
|
7
|
+
|
8
|
+
def where_clause
|
9
|
+
{ r__value_as_number: :l__value_as_number }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'binary_operator_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class Except < BinaryOperatorOperator
|
6
|
+
desc 'If a LHR result appears in the RHR result, it is removed from the output result set.'
|
7
|
+
category 'Set Logic'
|
8
|
+
|
9
|
+
def query(db)
|
10
|
+
if ignore_dates?
|
11
|
+
query = db.from(Sequel.as(left.evaluate(db), :l))
|
12
|
+
.left_join(Sequel.as(right.evaluate(db), :r), l__criterion_id: :r__criterion_id, l__criterion_type: :r__criterion_type)
|
13
|
+
.where(r__criterion_id: nil)
|
14
|
+
.select_all(:l)
|
15
|
+
db.from(query)
|
16
|
+
else
|
17
|
+
left.evaluate(db).except(right.evaluate(db))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def ignore_dates?
|
24
|
+
options[:ignore_dates]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'binary_operator_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class Filter < BinaryOperatorOperator
|
6
|
+
desc 'Only pass through results from the LHR that have a corresponding RHR with the same person, criterion_id, and criterion_type'
|
7
|
+
|
8
|
+
def query(db)
|
9
|
+
rhs = right.evaluate(db)
|
10
|
+
rhs = rhs.select_group(:person_id, :criterion_id, :criterion_type)
|
11
|
+
query = db.from(Sequel.as(left.evaluate(db), :l))
|
12
|
+
query = query
|
13
|
+
.left_join(Sequel.as(rhs, :r), l__person_id: :r__person_id, l__criterion_id: :r__criterion_id, l__criterion_type: :r__criterion_type)
|
14
|
+
.exclude(r__criterion_id: nil)
|
15
|
+
.select_all(:l)
|
16
|
+
db.from(query)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -1,20 +1,22 @@
|
|
1
1
|
require_relative 'occurrence'
|
2
2
|
|
3
3
|
module ConceptQL
|
4
|
-
module
|
5
|
-
# Represents a
|
4
|
+
module Operators
|
5
|
+
# Represents a operator that will grab the first occurrence of something
|
6
6
|
#
|
7
|
-
# The
|
7
|
+
# The operator treats all streams as a single, large stream. It partitions
|
8
8
|
# that larget stream by person_id, then sorts within those groupings
|
9
9
|
# by start_date and then select at most one row per person, regardless
|
10
|
-
# of how many different types of streams enter the
|
10
|
+
# of how many different types of streams enter the operator
|
11
11
|
#
|
12
12
|
# If two rows have the same start_date, the order of their ranking
|
13
13
|
# is arbitrary
|
14
14
|
#
|
15
15
|
# If we ask for the first occurrence of something and a person has no
|
16
|
-
# occurrences, this
|
16
|
+
# occurrences, this operator returns nothing for that person
|
17
17
|
class First < Occurrence
|
18
|
+
desc 'Only passes through the row with the earliest start_date per person. If more than one row qualifies, result is arbitrarily chosen.'
|
19
|
+
|
18
20
|
def occurrence
|
19
21
|
1
|
20
22
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class FromSeerVisits < Operator
|
6
|
+
def type
|
7
|
+
:observation
|
8
|
+
end
|
9
|
+
def query(db)
|
10
|
+
visit_ids = stream.evaluate(db)
|
11
|
+
.from_self
|
12
|
+
.where(criterion_type: 'visit_occurrence')
|
13
|
+
query = db[:observation].where(visit_occurrence_id: visit_ids.select(:criterion_id))
|
14
|
+
arguments.inject(query) do |q, key|
|
15
|
+
q.where(observation_source_value: key.to_s.upcase)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
|
@@ -1,11 +1,11 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'operator'
|
2
2
|
|
3
3
|
module ConceptQL
|
4
|
-
module
|
5
|
-
class Gender <
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
module Operators
|
5
|
+
class Gender < Operator
|
6
|
+
desc 'Returns all person records that match the selected gender.'
|
7
|
+
argument :gender, type: :string, options: ['Male', 'Female']
|
8
|
+
types :person
|
9
9
|
|
10
10
|
def query(db)
|
11
11
|
gender_concept_ids = values.map do |value|
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'standard_vocabulary_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class Hcpcs < StandardVocabularyOperator
|
6
|
+
preferred_name 'HCPCS'
|
7
|
+
desc 'Searches the procedure_occurrence table for all procedures with matching HCPCS codes'
|
8
|
+
argument :hcpcs, type: :codelist, vocab: 'HCPCS'
|
9
|
+
predominant_types :procedure_occurrence
|
10
|
+
|
11
|
+
def table
|
12
|
+
:procedure_occurrence
|
13
|
+
end
|
14
|
+
|
15
|
+
def vocabulary_id
|
16
|
+
5
|
17
|
+
end
|
18
|
+
|
19
|
+
def concept_column
|
20
|
+
:procedure_concept_id
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'source_vocabulary_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class Icd10 < SourceVocabularyOperator
|
6
|
+
preferred_name 'ICD-10 CM'
|
7
|
+
desc 'Searches the condition_occurrence table for the given set of ICD-10 codes.'
|
8
|
+
argument :icd10s, type: :codelist, vocab: 'ICD10CM'
|
9
|
+
predominant_types :condition_occurrence
|
10
|
+
|
11
|
+
def table
|
12
|
+
:condition_occurrence
|
13
|
+
end
|
14
|
+
|
15
|
+
def vocabulary_id
|
16
|
+
34
|
17
|
+
end
|
18
|
+
|
19
|
+
def source_column
|
20
|
+
:condition_source_value
|
21
|
+
end
|
22
|
+
|
23
|
+
def concept_column
|
24
|
+
:condition_concept_id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'source_vocabulary_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class Icd9 < SourceVocabularyOperator
|
6
|
+
preferred_name 'ICD-9 CM'
|
7
|
+
desc 'Searches the condition_occurrence table for the given set of ICD-9 codes.'
|
8
|
+
argument :icd9s, type: :codelist, vocab: 'ICD9CM'
|
9
|
+
predominant_types :condition_occurrence
|
10
|
+
|
11
|
+
def table
|
12
|
+
:condition_occurrence
|
13
|
+
end
|
14
|
+
|
15
|
+
def vocabulary_id
|
16
|
+
2
|
17
|
+
end
|
18
|
+
|
19
|
+
def source_column
|
20
|
+
:condition_source_value
|
21
|
+
end
|
22
|
+
|
23
|
+
def concept_column
|
24
|
+
:condition_concept_id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'standard_vocabulary_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class Icd9Procedure < StandardVocabularyOperator
|
6
|
+
preferred_name 'ICD-9 Proc'
|
7
|
+
desc 'Searches the procedure_occurrence table for the given set of ICD-9 codes.'
|
8
|
+
argument :icd9s, type: :codelist, vocab: 'ICD9Proc'
|
9
|
+
predominant_types :procedure_occurrence
|
10
|
+
|
11
|
+
def table
|
12
|
+
:procedure_occurrence
|
13
|
+
end
|
14
|
+
|
15
|
+
def vocabulary_id
|
16
|
+
3
|
17
|
+
end
|
18
|
+
|
19
|
+
def concept_column
|
20
|
+
:procedure_concept_id
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -1,15 +1,19 @@
|
|
1
1
|
require_relative 'pass_thru'
|
2
2
|
|
3
3
|
module ConceptQL
|
4
|
-
module
|
4
|
+
module Operators
|
5
5
|
class Intersect < PassThru
|
6
|
+
desc 'Passes thru any result row that appears in all incoming result sets.'
|
7
|
+
allows_many_upstreams
|
8
|
+
category 'Set Logic'
|
9
|
+
|
6
10
|
def types
|
7
|
-
|
11
|
+
upstreams.map(&:types).flatten.uniq
|
8
12
|
end
|
9
13
|
|
10
14
|
def query(db)
|
11
15
|
exprs = {}
|
12
|
-
|
16
|
+
upstreams.each do |expression|
|
13
17
|
evaled = expression.evaluate(db)
|
14
18
|
expression.types.each do |type|
|
15
19
|
(exprs[type] ||= []) << evaled
|
@@ -1,20 +1,22 @@
|
|
1
1
|
require_relative 'occurrence'
|
2
2
|
|
3
3
|
module ConceptQL
|
4
|
-
module
|
5
|
-
# Represents a
|
4
|
+
module Operators
|
5
|
+
# Represents a operator that will grab the last occurrence of something
|
6
6
|
#
|
7
|
-
# The
|
7
|
+
# The operator treats all streams as a single, large stream. It partitions
|
8
8
|
# that larget stream by person_id, then sorts within those groupings
|
9
9
|
# by start_date and then select at most one row per person, regardless
|
10
|
-
# of how many different types of streams enter the
|
10
|
+
# of how many different types of streams enter the operator
|
11
11
|
#
|
12
12
|
# If two rows have the same start_date, the order of their ranking
|
13
13
|
# is arbitrary
|
14
14
|
#
|
15
15
|
# If we ask for the last occurrence of something and a person has no
|
16
|
-
# occurrences, this
|
16
|
+
# occurrences, this operator returns nothing for that person
|
17
17
|
class Last < Occurrence
|
18
|
+
desc 'Only passes through the row with the most recent start_date per person. If more than one row qualifies, result is arbitrarily chosen.'
|
19
|
+
|
18
20
|
def occurrence
|
19
21
|
-1
|
20
22
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'standard_vocabulary_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class Loinc < StandardVocabularyOperator
|
6
|
+
preferred_name 'LOINC'
|
7
|
+
desc 'Searches the observation table for all observations with matching LOINC codes'
|
8
|
+
argument :loincs, type: :codelist, vocab: 'LOINC'
|
9
|
+
predominant_types :observation
|
10
|
+
|
11
|
+
def table
|
12
|
+
:observation
|
13
|
+
end
|
14
|
+
|
15
|
+
def vocabulary_id
|
16
|
+
6
|
17
|
+
end
|
18
|
+
|
19
|
+
def concept_column
|
20
|
+
:observation_concept_id
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'source_vocabulary_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class Medcode < SourceVocabularyOperator
|
6
|
+
desc 'Searches the condition_occurrence table for all conditions with matching Medcodes'
|
7
|
+
argument :medcodes, type: :codelist, vocab_id: '203'
|
8
|
+
predominant_types :condition_occurrence
|
9
|
+
|
10
|
+
def table
|
11
|
+
:condition_occurrence
|
12
|
+
end
|
13
|
+
|
14
|
+
def vocabulary_id
|
15
|
+
203
|
16
|
+
end
|
17
|
+
|
18
|
+
def source_column
|
19
|
+
:condition_source_value
|
20
|
+
end
|
21
|
+
|
22
|
+
def concept_column
|
23
|
+
:condition_concept_id
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'source_vocabulary_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class MedcodeProcedure < SourceVocabularyOperator
|
6
|
+
desc 'Searches the procedure_occurrence table for all procedures with matching Medcodes'
|
7
|
+
argument :medcodes, type: :codelist, vocab: '204'
|
8
|
+
predominant_types :procedure_occurrence
|
9
|
+
|
10
|
+
def table
|
11
|
+
:procedure_occurrence
|
12
|
+
end
|
13
|
+
|
14
|
+
def vocabulary_id
|
15
|
+
204
|
16
|
+
end
|
17
|
+
|
18
|
+
def source_column
|
19
|
+
:procedure_source_value
|
20
|
+
end
|
21
|
+
|
22
|
+
def concept_column
|
23
|
+
:procedure_concept_id
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'source_vocabulary_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class Ndc < SourceVocabularyOperator
|
6
|
+
preferred_name 'NDC'
|
7
|
+
desc 'Searches the drug_exposure table for all procedures with matching NDC codes'
|
8
|
+
argument :ndcs, type: :codelist, vocab: 'NDC'
|
9
|
+
predominant_types :drug_exposure
|
10
|
+
|
11
|
+
def table
|
12
|
+
:drug_exposure
|
13
|
+
end
|
14
|
+
|
15
|
+
def vocabulary_id
|
16
|
+
9
|
17
|
+
end
|
18
|
+
|
19
|
+
def source_column
|
20
|
+
:drug_source_value
|
21
|
+
end
|
22
|
+
|
23
|
+
def concept_column
|
24
|
+
:drug_concept_id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative 'pass_thru'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
# Represents a operator that will either:
|
6
|
+
# - create a value_as_number value for every person in the database
|
7
|
+
# - change the value_as_number value for every every result passed in
|
8
|
+
# - either to a number
|
9
|
+
# - or a value from a column in the origin row
|
10
|
+
#
|
11
|
+
# Accepts two params:
|
12
|
+
# - Either a number value or a symbol representing a column name
|
13
|
+
# - An optional stream
|
14
|
+
class Numeric < PassThru
|
15
|
+
desc <<-EOF
|
16
|
+
Represents a operator that will either:
|
17
|
+
- create a value_as_number value for every person in the database
|
18
|
+
- change the value_as_number value for every every result passed in
|
19
|
+
- either to a number
|
20
|
+
- or a value from a column in the origin row
|
21
|
+
|
22
|
+
Accepts two params:
|
23
|
+
- Either a number value or a symbol representing a column name
|
24
|
+
- An optional stream
|
25
|
+
EOF
|
26
|
+
argument :value, type: :float
|
27
|
+
allows_one_upstream
|
28
|
+
|
29
|
+
def query(db)
|
30
|
+
stream.nil? ? as_criterion(db) : with_kids(db)
|
31
|
+
end
|
32
|
+
|
33
|
+
def types
|
34
|
+
stream.nil? ? [:person] : super
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def with_kids(db)
|
39
|
+
db.from(stream.evaluate(db))
|
40
|
+
.select(*(COLUMNS - [:value_as_number]))
|
41
|
+
.select_append(Sequel.lit('?', arguments.first).cast(Float).as(:value_as_number))
|
42
|
+
.from_self
|
43
|
+
end
|
44
|
+
|
45
|
+
def as_criterion(db)
|
46
|
+
db.from(select_it(db.from(:person), :person))
|
47
|
+
.select(*(COLUMNS - [:value_as_number]))
|
48
|
+
.select_append(Sequel.lit('?', arguments.first).cast(Float).as(:value_as_number))
|
49
|
+
.from_self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'source_vocabulary_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class ObservationByEnttype < SourceVocabularyOperator
|
6
|
+
desc 'Searches the observation table for all observations with matching Enttype'
|
7
|
+
argument :enttypes, type: :codelist, vocab_id: [206, 207]
|
8
|
+
predominant_types :observation
|
9
|
+
|
10
|
+
def table
|
11
|
+
:observation
|
12
|
+
end
|
13
|
+
|
14
|
+
def vocabulary_id
|
15
|
+
[206, 207]
|
16
|
+
end
|
17
|
+
|
18
|
+
def source_column
|
19
|
+
:observation_source_value
|
20
|
+
end
|
21
|
+
|
22
|
+
def concept_column
|
23
|
+
:observation_concept_id
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'casting_operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
class ObservationPeriod < CastingOperator
|
6
|
+
desc 'Generates all observation_period records, or, if fed a stream, fetches all observation_period records for the people represented in the incoming result set.'
|
7
|
+
types :observation_period
|
8
|
+
allows_one_upstream
|
9
|
+
|
10
|
+
def my_type
|
11
|
+
:observation_period
|
12
|
+
end
|
13
|
+
|
14
|
+
def i_point_at
|
15
|
+
[]
|
16
|
+
end
|
17
|
+
|
18
|
+
def these_point_at_me
|
19
|
+
# I could list ALL the types we use, but the default behavior of casting,
|
20
|
+
# when there is no explicit casting defined, is to convert everything to
|
21
|
+
# person IDs
|
22
|
+
#
|
23
|
+
# So by defining no known castable relationships in this operator, all
|
24
|
+
# types will be converted to person
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative 'operator'
|
2
|
+
|
3
|
+
module ConceptQL
|
4
|
+
module Operators
|
5
|
+
# Represents a operator that will grab the Nth occurrence of something
|
6
|
+
#
|
7
|
+
# Specify occurrences as integers, excluding O
|
8
|
+
# 1 => first
|
9
|
+
# 2 => second
|
10
|
+
# ...
|
11
|
+
# -1 => last
|
12
|
+
# -2 => second-to-last
|
13
|
+
#
|
14
|
+
# The operator treats all streams as a single, large stream. It partitions
|
15
|
+
# that larget stream by person_id, then sorts within those groupings
|
16
|
+
# by start_date and then select at most one row per person, regardless
|
17
|
+
# of how many different types of streams enter the operator
|
18
|
+
#
|
19
|
+
# If two rows have the same start_date, the order of their ranking
|
20
|
+
# is arbitrary
|
21
|
+
#
|
22
|
+
# If we ask for the second occurrence of something and a person has only one
|
23
|
+
# occurrence, this operator returns nothing for that person
|
24
|
+
class Occurrence < Operator
|
25
|
+
preferred_name 'Nth Occurrence'
|
26
|
+
desc <<-EOF
|
27
|
+
Groups all results by person, then orders by start_date, then finds the nth occurrence.
|
28
|
+
nth occurrence can be positive or negative.
|
29
|
+
1 => first
|
30
|
+
2 => second
|
31
|
+
...
|
32
|
+
-1 => last
|
33
|
+
-2 => second-to-last
|
34
|
+
|
35
|
+
If two rows have the same start_date, the order of their ranking
|
36
|
+
is arbitrary
|
37
|
+
|
38
|
+
If we ask for the second occurrence of something and a person has only one
|
39
|
+
occurrence, this operator returns nothing for that person
|
40
|
+
EOF
|
41
|
+
argument :occurrence, type: :integer
|
42
|
+
allows_one_upstream
|
43
|
+
category %w(Temporal Occurrence)
|
44
|
+
|
45
|
+
def query(db)
|
46
|
+
db[:occurrences]
|
47
|
+
.with(:occurrences,
|
48
|
+
stream.evaluate(db)
|
49
|
+
.from_self
|
50
|
+
.select_append { |o| o.row_number(:over, partition: :person_id, order: ordered_columns){}.as(:rn) })
|
51
|
+
.where(rn: occurrence.abs)
|
52
|
+
end
|
53
|
+
|
54
|
+
def occurrence
|
55
|
+
@occurrence ||= arguments.first
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def asc_or_desc
|
60
|
+
occurrence < 0 ? :desc : :asc
|
61
|
+
end
|
62
|
+
|
63
|
+
def ordered_columns
|
64
|
+
ordered_columns = [Sequel.send(asc_or_desc, :start_date)]
|
65
|
+
ordered_columns += [:criterion_type, :criterion_id]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|