conceptql 0.2.0 → 0.4.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 +5 -5
- data/CHANGELOG.md +226 -117
- data/LICENSE +21 -0
- data/README.md +35 -83
- data/bin/conceptql +2 -1
- data/bin/conceptql_utils +6 -0
- data/config/multiple_vocabularies.csv +4 -0
- data/config/provenance.yml +489 -0
- data/config/race.yml +50 -0
- data/config/vocabularies.csv +87 -0
- data/lib/conceptql/behaviors/code_lister.rb +23 -0
- data/lib/conceptql/behaviors/drugish.rb +17 -0
- data/lib/conceptql/behaviors/labish.rb +19 -0
- data/lib/conceptql/behaviors/metadatable.rb +226 -73
- data/lib/conceptql/behaviors/provenanceable.rb +26 -0
- data/lib/conceptql/behaviors/single_event.rb +11 -0
- data/lib/conceptql/behaviors/timeless.rb +11 -0
- data/lib/conceptql/behaviors/utilizable.rb +206 -0
- data/lib/conceptql/behaviors/windowable.rb +27 -0
- data/lib/conceptql/cli.rb +117 -108
- data/lib/conceptql/columnizer.rb +75 -0
- data/lib/conceptql/data_model/base.rb +462 -0
- data/lib/conceptql/data_model/gdm.rb +155 -0
- data/lib/conceptql/data_model/gdm_wide.rb +42 -0
- data/lib/conceptql/data_model/omopv4_plus.rb +12 -0
- data/lib/conceptql/data_model.rb +30 -0
- data/lib/conceptql/database.rb +214 -0
- data/lib/conceptql/date_adjuster.rb +66 -18
- data/lib/conceptql/diagram.rb +243 -0
- data/lib/conceptql/lexicon/lexicon_gdm.rb +47 -0
- data/lib/conceptql/lexicon/lexicon_no_db.rb +49 -0
- data/lib/conceptql/lexicon/lexicon_ohdsi.rb +63 -0
- data/lib/conceptql/lexicon/lexicon_strategy.rb +116 -0
- data/lib/conceptql/lexicon.rb +111 -0
- data/lib/conceptql/nodifier.rb +65 -24
- data/lib/conceptql/operators/after.rb +34 -14
- data/lib/conceptql/operators/any_overlap.rb +8 -6
- data/lib/conceptql/operators/before.rb +34 -10
- data/lib/conceptql/operators/binary_operator_operator.rb +66 -23
- data/lib/conceptql/operators/casting_operator.rb +51 -36
- data/lib/conceptql/operators/co_reported.rb +111 -0
- data/lib/conceptql/operators/concurrent_within.rb +69 -0
- data/lib/conceptql/operators/contains.rb +12 -8
- data/lib/conceptql/operators/count.rb +17 -9
- data/lib/conceptql/operators/date_range.rb +48 -14
- data/lib/conceptql/operators/death.rb +14 -4
- data/lib/conceptql/operators/dedup.rb +31 -0
- data/lib/conceptql/operators/during.rb +7 -12
- data/lib/conceptql/operators/episode.rb +146 -0
- data/lib/conceptql/operators/equal.rb +9 -3
- data/lib/conceptql/operators/ethnicity.rb +49 -0
- data/lib/conceptql/operators/except.rb +19 -12
- data/lib/conceptql/operators/filter.rb +23 -8
- data/lib/conceptql/operators/first.rb +16 -3
- data/lib/conceptql/operators/from.rb +52 -4
- data/lib/conceptql/operators/gender.rb +34 -11
- data/lib/conceptql/operators/hospice.rb +19 -0
- data/lib/conceptql/operators/hospitalization.rb +19 -0
- data/lib/conceptql/operators/information_periods.rb +36 -0
- data/lib/conceptql/operators/invalid.rb +35 -0
- data/lib/conceptql/operators/last.rb +16 -3
- data/lib/conceptql/operators/match.rb +91 -0
- data/lib/conceptql/operators/multiple_vocabularies.rb +172 -0
- data/lib/conceptql/operators/numeric.rb +65 -23
- data/lib/conceptql/operators/numeric_filter.rb +68 -0
- data/lib/conceptql/operators/occurrence.rb +153 -32
- data/lib/conceptql/operators/one_in_two_out.rb +237 -38
- data/lib/conceptql/operators/operator.rb +624 -178
- data/lib/conceptql/operators/pass_thru.rb +17 -3
- data/lib/conceptql/operators/person.rb +18 -6
- data/lib/conceptql/operators/person_filter.rb +9 -3
- data/lib/conceptql/operators/place_of_service_filter.rb +43 -0
- data/lib/conceptql/operators/provenance.rb +55 -0
- data/lib/conceptql/operators/provider_filter.rb +62 -0
- data/lib/conceptql/operators/race.rb +55 -7
- data/lib/conceptql/operators/read.rb +168 -0
- data/lib/conceptql/operators/recall.rb +49 -14
- data/lib/conceptql/operators/snf.rb +19 -0
- data/lib/conceptql/operators/sum.rb +29 -12
- data/lib/conceptql/operators/temporal_operator.rb +108 -16
- data/lib/conceptql/operators/time_window.rb +20 -32
- data/lib/conceptql/operators/trim_date.rb +71 -0
- data/lib/conceptql/operators/trim_date_end.rb +28 -40
- data/lib/conceptql/operators/trim_date_start.rb +28 -41
- data/lib/conceptql/operators/union.rb +67 -6
- data/lib/conceptql/operators/vocabulary.rb +151 -0
- data/lib/conceptql/paths.rb +41 -0
- data/lib/conceptql/query.rb +191 -26
- data/lib/conceptql/query_modifiers/gdm/admission_date_query_modifier.rb +46 -0
- data/lib/conceptql/query_modifiers/gdm/drug_query_modifier.rb +54 -0
- data/lib/conceptql/query_modifiers/gdm/lab_query_modifier.rb +57 -0
- data/lib/conceptql/query_modifiers/gdm/pos_query_modifier.rb +46 -0
- data/lib/conceptql/query_modifiers/gdm/provenance_query_modifier.rb +45 -0
- data/lib/conceptql/query_modifiers/gdm/provider_query_modifier.rb +53 -0
- data/lib/conceptql/query_modifiers/gdm_wide/admission_date_query_modifier.rb +42 -0
- data/lib/conceptql/query_modifiers/gdm_wide/drug_query_modifier.rb +52 -0
- data/lib/conceptql/query_modifiers/gdm_wide/lab_query_modifier.rb +55 -0
- data/lib/conceptql/query_modifiers/gdm_wide/pos_query_modifier.rb +37 -0
- data/lib/conceptql/query_modifiers/gdm_wide/provenance_query_modifier.rb +38 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/admission_date_query_modifier.rb +45 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/drug_query_modifier.rb +80 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/pos_query_modifier.rb +47 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/provenance_query_modifier.rb +45 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/provider_query_modifier.rb +26 -0
- data/lib/conceptql/query_modifiers/query_modifier.rb +20 -0
- data/lib/conceptql/rdbms/duckdb.rb +25 -0
- data/lib/conceptql/rdbms/generic.rb +120 -0
- data/lib/conceptql/rdbms/postgres.rb +51 -0
- data/lib/conceptql/rdbms/spark.rb +22 -0
- data/lib/conceptql/rdbms.rb +22 -0
- data/lib/conceptql/scope.rb +481 -31
- data/lib/conceptql/sql_formatters/formatter.rb +25 -0
- data/lib/conceptql/sql_formatters.rb +22 -0
- data/lib/conceptql/utils.rb +113 -0
- data/lib/conceptql/utils_cli.rb +17 -0
- data/lib/conceptql/version.rb +3 -1
- data/lib/conceptql/visitors/metadata_extractor.rb +54 -0
- data/lib/conceptql/vocabularies/behaviors/costish.rb +24 -0
- data/lib/conceptql/vocabularies/behaviors/gdmish.rb +19 -0
- data/lib/conceptql/vocabularies/behaviors/omopish.rb +32 -0
- data/lib/conceptql/vocabularies/dynamic_vocabularies.rb +63 -0
- data/lib/conceptql/vocabularies/entry.rb +138 -0
- data/lib/conceptql/vocabularies/vocabulary_class_factory.rb +85 -0
- data/lib/conceptql/window/base.rb +26 -0
- data/lib/conceptql/window/date_range.rb +25 -0
- data/lib/conceptql/window/table.rb +116 -0
- data/lib/conceptql/window.rb +72 -0
- data/lib/conceptql.rb +65 -2
- data/lib/sequel/extensions/pg_ctas_explain.rb +22 -0
- data/lib/sequel/extensions/pg_vacuum_table.rb +23 -0
- data/schemas/gdm.yml +882 -0
- data/schemas/gdm_wide.yml +757 -0
- data/schemas/ohdsi_vocabs.yml +143 -0
- data/schemas/omopv4_plus.yml +729 -0
- data/schemas/omopv4_plus_more_ignorables.tsv +13 -0
- metadata +267 -207
- data/.gitignore +0 -23
- data/Gemfile +0 -4
- data/Guardfile +0 -56
- data/LICENSE.txt +0 -22
- data/Rakefile +0 -1
- data/conceptql.gemspec +0 -32
- data/doc/ConceptQL Specification (alpha).pdf +0 -0
- data/doc/diagram_0.png +0 -0
- data/doc/implementation_notes.md +0 -39
- data/doc/spec.md +0 -1344
- data/lib/conceptql/behaviors/debuggable.rb +0 -70
- data/lib/conceptql/behaviors/dottable.rb +0 -103
- data/lib/conceptql/behaviors/preppable.rb +0 -20
- data/lib/conceptql/converter.rb +0 -65
- data/lib/conceptql/debugger.rb +0 -48
- data/lib/conceptql/fake_grapher.rb +0 -20
- data/lib/conceptql/graph.rb +0 -52
- data/lib/conceptql/graph_nodifier.rb +0 -203
- data/lib/conceptql/logger.rb +0 -10
- data/lib/conceptql/operators/complement.rb +0 -38
- data/lib/conceptql/operators/concept.rb +0 -70
- data/lib/conceptql/operators/condition_type.rb +0 -71
- data/lib/conceptql/operators/cpt.rb +0 -25
- data/lib/conceptql/operators/drug_type_concept.rb +0 -21
- data/lib/conceptql/operators/from_seer_visits.rb +0 -23
- data/lib/conceptql/operators/hcpcs.rb +0 -25
- data/lib/conceptql/operators/icd10.rb +0 -28
- data/lib/conceptql/operators/icd9.rb +0 -28
- data/lib/conceptql/operators/icd9_procedure.rb +0 -25
- data/lib/conceptql/operators/intersect.rb +0 -34
- data/lib/conceptql/operators/loinc.rb +0 -25
- data/lib/conceptql/operators/medcode.rb +0 -28
- data/lib/conceptql/operators/medcode_procedure.rb +0 -27
- data/lib/conceptql/operators/ndc.rb +0 -29
- data/lib/conceptql/operators/observation_by_enttype.rb +0 -29
- data/lib/conceptql/operators/observation_period.rb +0 -30
- data/lib/conceptql/operators/overlapped_by.rb +0 -23
- data/lib/conceptql/operators/overlaps.rb +0 -19
- data/lib/conceptql/operators/place_of_service_code.rb +0 -24
- data/lib/conceptql/operators/procedure_occurrence.rb +0 -25
- data/lib/conceptql/operators/prodcode.rb +0 -29
- data/lib/conceptql/operators/rxnorm.rb +0 -24
- data/lib/conceptql/operators/snomed.rb +0 -24
- data/lib/conceptql/operators/snomed_condition.rb +0 -26
- data/lib/conceptql/operators/source_vocabulary_operator.rb +0 -58
- data/lib/conceptql/operators/standard_vocabulary_operator.rb +0 -45
- data/lib/conceptql/operators/started_by.rb +0 -22
- data/lib/conceptql/operators/to_seer_visits.rb +0 -24
- data/lib/conceptql/operators/visit.rb +0 -15
- data/lib/conceptql/operators/visit_occurrence.rb +0 -30
- data/lib/conceptql/tree.rb +0 -53
- data/lib/conceptql/utils/temp_table.rb +0 -72
- data/spec/conceptql/behaviors/dottable_spec.rb +0 -99
- data/spec/conceptql/converter_spec.rb +0 -51
- data/spec/conceptql/date_adjuster_spec.rb +0 -68
- data/spec/conceptql/operators/after_spec.rb +0 -16
- data/spec/conceptql/operators/before_spec.rb +0 -16
- data/spec/conceptql/operators/casting_operator_spec.rb +0 -68
- data/spec/conceptql/operators/complement_spec.rb +0 -15
- data/spec/conceptql/operators/concept_spec.rb +0 -40
- data/spec/conceptql/operators/condition_type_spec.rb +0 -128
- data/spec/conceptql/operators/contains_spec.rb +0 -19
- data/spec/conceptql/operators/cpt_spec.rb +0 -29
- data/spec/conceptql/operators/date_range_spec.rb +0 -33
- data/spec/conceptql/operators/death_spec.rb +0 -10
- data/spec/conceptql/operators/during_spec.rb +0 -30
- data/spec/conceptql/operators/except_spec.rb +0 -15
- data/spec/conceptql/operators/first_spec.rb +0 -35
- data/spec/conceptql/operators/from_spec.rb +0 -13
- data/spec/conceptql/operators/gender_spec.rb +0 -27
- data/spec/conceptql/operators/hcpcs_spec.rb +0 -29
- data/spec/conceptql/operators/icd10_spec.rb +0 -34
- data/spec/conceptql/operators/icd9_procedure_spec.rb +0 -29
- data/spec/conceptql/operators/icd9_spec.rb +0 -34
- data/spec/conceptql/operators/intersect_spec.rb +0 -28
- data/spec/conceptql/operators/last_spec.rb +0 -36
- data/spec/conceptql/operators/loinc_spec.rb +0 -29
- data/spec/conceptql/operators/medcode_procedure_spec.rb +0 -34
- data/spec/conceptql/operators/medcode_spec.rb +0 -34
- data/spec/conceptql/operators/observation_period_spec.rb +0 -10
- data/spec/conceptql/operators/occurrence_spec.rb +0 -87
- data/spec/conceptql/operators/overlapped_by_spec.rb +0 -32
- data/spec/conceptql/operators/overlaps_spec.rb +0 -21
- data/spec/conceptql/operators/person_filter_spec.rb +0 -15
- data/spec/conceptql/operators/person_spec.rb +0 -10
- data/spec/conceptql/operators/place_of_service_code_spec.rb +0 -24
- data/spec/conceptql/operators/procedure_occurrence_spec.rb +0 -10
- data/spec/conceptql/operators/prodcode_spec.rb +0 -35
- data/spec/conceptql/operators/query_double.rb +0 -20
- data/spec/conceptql/operators/query_double_spec.rb +0 -7
- data/spec/conceptql/operators/race_spec.rb +0 -21
- data/spec/conceptql/operators/rxnorm_spec.rb +0 -29
- data/spec/conceptql/operators/snomed_spec.rb +0 -29
- data/spec/conceptql/operators/source_vocabulary_operator_spec.rb +0 -35
- data/spec/conceptql/operators/standard_vocabulary_operator_spec.rb +0 -35
- data/spec/conceptql/operators/started_by_spec.rb +0 -22
- data/spec/conceptql/operators/temporal_operator_spec.rb +0 -51
- data/spec/conceptql/operators/time_window_spec.rb +0 -77
- data/spec/conceptql/operators/union_spec.rb +0 -21
- data/spec/conceptql/operators/visit_occurrence_spec.rb +0 -10
- data/spec/conceptql/query_spec.rb +0 -22
- data/spec/conceptql/tree_spec.rb +0 -50
- data/spec/doubles/stream_for_casting_double.rb +0 -9
- data/spec/doubles/stream_for_occurrence_double.rb +0 -25
- data/spec/doubles/stream_for_temporal_double.rb +0 -6
- data/spec/spec_helper.rb +0 -102
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../query_modifier"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module QueryModifiers
|
|
7
|
+
module Gdm
|
|
8
|
+
class ProviderQueryModifier < QueryModifier
|
|
9
|
+
def self.provided_columns
|
|
10
|
+
%i[provider_id context_specialty_concept_id practitioner_specialty_concept_id role_type_concept_id]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.has_required_columns?(cols)
|
|
14
|
+
needed = %i[practitioner_id context_id].sort
|
|
15
|
+
found = needed | cols
|
|
16
|
+
!found.empty?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def modified_query
|
|
20
|
+
if dm.table_cols(source_table).include?(:context_id)
|
|
21
|
+
query.from_self(alias: :c)
|
|
22
|
+
.left_join(Sequel[:contexts_practitioners].as(:cp), Sequel[:c][:context_id] => Sequel[:cp][:context_id])
|
|
23
|
+
.left_join(Sequel[:practitioners].as(:p), Sequel[:p][:id] => Sequel[:cp][:practitioner_id])
|
|
24
|
+
.select_all(:c)
|
|
25
|
+
.select_append(
|
|
26
|
+
Sequel[:cp][:practitioner_id].as(:provider_id),
|
|
27
|
+
Sequel[:cp][:role_type_concept_id].as(:role_type_concept_id),
|
|
28
|
+
Sequel[:cp][:specialty_type_concept_id].as(:context_specialty_concept_id),
|
|
29
|
+
Sequel[:p][:specialty_concept_id].as(:practitioner_specialty_concept_id)
|
|
30
|
+
)
|
|
31
|
+
else
|
|
32
|
+
ds = query.select_all
|
|
33
|
+
ds = if ds.columns.include?(:provider_id)
|
|
34
|
+
ds
|
|
35
|
+
elsif dm.table_cols(source_table).include?(:practitioner_id)
|
|
36
|
+
ds.select_append(Sequel[:practitioner_id].as(:provider_id))
|
|
37
|
+
else
|
|
38
|
+
ds.select_append(Sequel.cast(nil, :Bigint).as(:provider_id))
|
|
39
|
+
end
|
|
40
|
+
%i[role_type_concept_id context_specialty_concept_id practitioner_specialty_concept_id].each do |col|
|
|
41
|
+
ds = if ds.columns.include?(col)
|
|
42
|
+
ds
|
|
43
|
+
else
|
|
44
|
+
ds.select_append(Sequel.cast(nil, :Bigint).as(col))
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
ds
|
|
48
|
+
end.from_self
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../query_modifier"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module QueryModifiers
|
|
7
|
+
module GdmWide
|
|
8
|
+
class AdmissionDateQueryModifier < QueryModifier
|
|
9
|
+
def self.provided_columns
|
|
10
|
+
%i[
|
|
11
|
+
admission_date
|
|
12
|
+
discharge_date
|
|
13
|
+
]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.has_required_columns?(cols)
|
|
17
|
+
needed = %i[admit_admission_date admit_discharge_date].sort
|
|
18
|
+
found = needed & cols
|
|
19
|
+
needed == found
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def modified_query
|
|
23
|
+
return query unless dm.table_cols(source_table).include?(:admit_admission_date)
|
|
24
|
+
|
|
25
|
+
query.from_self
|
|
26
|
+
.select_append(Sequel.function(:coalesce, :admit_admission_date, :start_date).as(:admission_date))
|
|
27
|
+
.select_append(Sequel.function(:coalesce, :admit_discharge_date, :end_date,
|
|
28
|
+
:start_date).as(:discharge_date))
|
|
29
|
+
.from_self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def domain
|
|
35
|
+
op.domain
|
|
36
|
+
rescue StandardError
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../query_modifier"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module QueryModifiers
|
|
7
|
+
module GdmWide
|
|
8
|
+
class DrugQueryModifier < QueryModifier
|
|
9
|
+
def self.provided_columns
|
|
10
|
+
%i[
|
|
11
|
+
drug_name
|
|
12
|
+
drug_amount
|
|
13
|
+
drug_amount_units
|
|
14
|
+
drug_quantity
|
|
15
|
+
drug_strength_source_value
|
|
16
|
+
drug_days_supply
|
|
17
|
+
]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.has_required_columns?(cols)
|
|
21
|
+
needed = [ :dose_unit_concept_id ]
|
|
22
|
+
found = needed & cols
|
|
23
|
+
needed == found
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def modified_query
|
|
27
|
+
return query unless dm.table_cols(source_table).include?(:dose_unit_concept_id)
|
|
28
|
+
|
|
29
|
+
query.from_self(alias: :cc)
|
|
30
|
+
.left_join(dm.name_concepts_table(query.db).as(:dose_con), Sequel[:cc][:dose_unit_concept_id] => Sequel[:dose_con][:id])
|
|
31
|
+
.left_join(dm.name_concepts_table(query.db).as(:ing_con), Sequel[:cc][:clinical_code_concept_id] => Sequel[:ing_con][:id])
|
|
32
|
+
.select_all(:cc)
|
|
33
|
+
.select_append(Sequel[:cc][:dose_value].as(:drug_amount))
|
|
34
|
+
.select_append(Sequel[:dose_con][:concept_text].as(:drug_amount_units))
|
|
35
|
+
.select_append(Sequel[:ing_con][:concept_text].as(:drug_name))
|
|
36
|
+
.select_append(Sequel[:cc][:days_supply].as(:drug_days_supply))
|
|
37
|
+
.select_append(Sequel[:cc][:strength_source_value].as(:drug_strength_source_value))
|
|
38
|
+
.select_append(Sequel[:cc][:quantity].as(:drug_quantity))
|
|
39
|
+
.from_self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def domain
|
|
45
|
+
op.domain
|
|
46
|
+
rescue StandardError
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../query_modifier"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module QueryModifiers
|
|
7
|
+
module GdmWide
|
|
8
|
+
class LabQueryModifier < QueryModifier
|
|
9
|
+
def self.provided_columns
|
|
10
|
+
%i[
|
|
11
|
+
value_as_number
|
|
12
|
+
value_as_string
|
|
13
|
+
value_as_concept_id
|
|
14
|
+
value_as_concept
|
|
15
|
+
unit_concept_id
|
|
16
|
+
unit_concept
|
|
17
|
+
range_low
|
|
18
|
+
range_high
|
|
19
|
+
]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.has_required_columns?(cols)
|
|
23
|
+
needed = [ :unit_concept_id ]
|
|
24
|
+
found = needed & cols
|
|
25
|
+
needed == found
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def modified_query
|
|
29
|
+
return query unless dm.table_cols(source_table).include?(:unit_concept_id)
|
|
30
|
+
|
|
31
|
+
query.from_self(alias: :cc)
|
|
32
|
+
.left_join(dm.name_concepts_table(query.db).as(:unit_con), Sequel[:cc][:unit_concept_id] => Sequel[:unit_con][:id])
|
|
33
|
+
.left_join(dm.name_concepts_table(query.db).as(:concept_con), Sequel[:cc][:result_as_concept_id] => Sequel[:concept_con][:id])
|
|
34
|
+
.select_all(:cc)
|
|
35
|
+
.select_append(Sequel[:cc][:result_as_number].as(:value_as_number))
|
|
36
|
+
.select_append(Sequel[:cc][:result_as_string].as(:value_as_string))
|
|
37
|
+
.select_append(Sequel[:cc][:result_as_concept_id].as(:value_as_concept_id))
|
|
38
|
+
.select_append(Sequel[:concept_con][:concept_code].as(:value_as_concept))
|
|
39
|
+
.select_append(Sequel[:unit_con][:concept_code].as(:unit_concept))
|
|
40
|
+
.select_append(Sequel[:cc][:normal_range_low].as(:range_low))
|
|
41
|
+
.select_append(Sequel[:cc][:normal_range_high].as(:range_high))
|
|
42
|
+
.from_self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def domain
|
|
48
|
+
op.domain
|
|
49
|
+
rescue StandardError
|
|
50
|
+
nil
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../query_modifier"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module QueryModifiers
|
|
7
|
+
module GdmWide
|
|
8
|
+
class PoSQueryModifier < QueryModifier
|
|
9
|
+
attr_reader :source_column, :column
|
|
10
|
+
|
|
11
|
+
def self.provided_columns
|
|
12
|
+
[ :visit_source_concept_id ]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.has_required_columns?(cols)
|
|
16
|
+
needed = [ :context_id ]
|
|
17
|
+
found = needed & cols
|
|
18
|
+
needed == found
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(*args)
|
|
22
|
+
super
|
|
23
|
+
@column = :visit_source_concept_id
|
|
24
|
+
@source_column = :pos_concept_id
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def modified_query
|
|
28
|
+
return query unless dm.table_cols(source_table).include?(:context_id)
|
|
29
|
+
|
|
30
|
+
query.from_self
|
|
31
|
+
.select_append(Sequel[source_column].as(column))
|
|
32
|
+
.from_self
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../query_modifier"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module QueryModifiers
|
|
7
|
+
module GdmWide
|
|
8
|
+
class ProvenanceQueryModifier < QueryModifier
|
|
9
|
+
RELATED_COLUMNS = %w[
|
|
10
|
+
provenance_concept_id
|
|
11
|
+
source_type_concept_id
|
|
12
|
+
].sort.map(&:to_sym)
|
|
13
|
+
|
|
14
|
+
attr_reader :db
|
|
15
|
+
|
|
16
|
+
def self.provided_columns
|
|
17
|
+
%i[
|
|
18
|
+
file_provenance_type
|
|
19
|
+
code_provenance_type
|
|
20
|
+
]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.has_required_columns?(cols)
|
|
24
|
+
!(RELATED_COLUMNS & cols).empty?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def modified_query
|
|
28
|
+
return query unless self.class.has_required_columns?(dm.table_cols(source_table))
|
|
29
|
+
|
|
30
|
+
query.from_self
|
|
31
|
+
.select_append(Sequel[:source_type_concept_id].as(:file_provenance_type),
|
|
32
|
+
Sequel[:provenance_concept_id].as(:code_provenance_type))
|
|
33
|
+
.from_self
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../query_modifier"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module QueryModifiers
|
|
7
|
+
module Omopv4Plus
|
|
8
|
+
class AdmissionDateQueryModifier < QueryModifier
|
|
9
|
+
def self.provided_columns
|
|
10
|
+
%i[
|
|
11
|
+
admission_date
|
|
12
|
+
discharge_date
|
|
13
|
+
]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.has_required_columns?(cols)
|
|
17
|
+
cols.map(&:to_s).any? { |col| col =~ /start_date/i }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def modified_query
|
|
21
|
+
cols = dm.table_cols(source_table).map(&:to_s)
|
|
22
|
+
return query unless cols.any? { |col| col =~ /start_date/i }
|
|
23
|
+
|
|
24
|
+
start_col = cols.find { |col| col =~ /start_date/i }.to_sym
|
|
25
|
+
end_col = cols.find { |col| col =~ /end_date/i }.to_sym
|
|
26
|
+
|
|
27
|
+
query.from_self(alias: :t)
|
|
28
|
+
.select_all(:t)
|
|
29
|
+
.select_append(Sequel[:t][start_col].as(:admission_date))
|
|
30
|
+
.select_append(Sequel.function(:coalesce, Sequel[:t][end_col],
|
|
31
|
+
Sequel[:t][start_col]).as(:discharge_date))
|
|
32
|
+
.from_self
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def domain
|
|
38
|
+
op.domain
|
|
39
|
+
rescue StandardError
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../query_modifier"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module QueryModifiers
|
|
7
|
+
module Omopv4Plus
|
|
8
|
+
class DrugQueryModifier < QueryModifier
|
|
9
|
+
attr_reader :db
|
|
10
|
+
|
|
11
|
+
def self.provided_columns
|
|
12
|
+
%i[
|
|
13
|
+
drug_name
|
|
14
|
+
drug_amount
|
|
15
|
+
drug_amount_units
|
|
16
|
+
drug_quantity
|
|
17
|
+
drug_days_supply
|
|
18
|
+
]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.has_required_columns?(cols)
|
|
22
|
+
needed = [ :drug_concept_id ]
|
|
23
|
+
found = needed & cols
|
|
24
|
+
needed == found
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize(*args)
|
|
28
|
+
super
|
|
29
|
+
@db = query.db
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def modified_query
|
|
33
|
+
return query unless dm.table_cols(source_table).include?(:drug_concept_id)
|
|
34
|
+
|
|
35
|
+
query.db
|
|
36
|
+
drug_concept_ids = query.select(:drug_concept_id).distinct
|
|
37
|
+
query.from_self(alias: :de)
|
|
38
|
+
.left_join(micro_table(drug_concept_ids).as(:mt), drug_concept_id: :drug_concept_id)
|
|
39
|
+
.select_all(:de)
|
|
40
|
+
.select_append(Sequel[:de][:quantity].as(:drug_quantity))
|
|
41
|
+
.select_append(Sequel[:de][:days_supply].as(:drug_days_supply))
|
|
42
|
+
.select_append(Sequel[:mt][:amount_value].as(:drug_amount))
|
|
43
|
+
.select_append(Sequel[:mt][:amount_unit].as(:drug_amount_units))
|
|
44
|
+
.select_append(Sequel[:mt][:drug_name].as(:drug_name))
|
|
45
|
+
.from_self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def micro_table(drug_concept_ids)
|
|
51
|
+
# TODO: Does drug_strength only have RXNORM concept_ids?
|
|
52
|
+
# TODO: What is vocabulary for units? Can we shrink concept table to just that vocab before joining?
|
|
53
|
+
collapsed_strengths = db[:drug_strength]
|
|
54
|
+
.where(drug_concept_id: drug_concept_ids)
|
|
55
|
+
.select_group(:drug_concept_id)
|
|
56
|
+
.select_append(
|
|
57
|
+
Sequel.function(:min, :amount_value).as(:amount_value),
|
|
58
|
+
Sequel.function(:min, :amount_unit).as(:amount_unit),
|
|
59
|
+
Sequel.function(:count, 1).as(:dcount)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
value_case = Sequel.case({ 1 => :amount_value }, nil, :dcount).as(:amount_value)
|
|
63
|
+
unit_case = Sequel.case({ 1 => :amount_unit }, nil, :dcount).as(:amount_unit)
|
|
64
|
+
|
|
65
|
+
collapsed_strengths = collapsed_strengths.from_self.select(:drug_concept_id, value_case, unit_case)
|
|
66
|
+
|
|
67
|
+
db.from(Sequel[:concept].as(:dc))
|
|
68
|
+
.left_join(collapsed_strengths, { drug_concept_id: :concept_id }, { table_alias: :ds })
|
|
69
|
+
.where(Sequel[:dc][:concept_id] => drug_concept_ids)
|
|
70
|
+
.select(
|
|
71
|
+
Sequel[:dc][:concept_id].as(:drug_concept_id),
|
|
72
|
+
Sequel[:dc][:concept_name].as(:drug_name),
|
|
73
|
+
Sequel[:ds][:amount_value].as(:amount_value),
|
|
74
|
+
Sequel[:ds][:amount_unit].as(:amount_unit)
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../query_modifier"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module QueryModifiers
|
|
7
|
+
module Omopv4Plus
|
|
8
|
+
class PoSQueryModifier < QueryModifier
|
|
9
|
+
attr_reader :join_id, :table, :source_column, :column
|
|
10
|
+
|
|
11
|
+
def self.provided_columns
|
|
12
|
+
[ :visit_source_concept_id ]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.has_required_columns?(cols)
|
|
16
|
+
needed = [ :visit_occurrence_id ]
|
|
17
|
+
found = needed & cols
|
|
18
|
+
needed == found
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(*args)
|
|
22
|
+
super
|
|
23
|
+
@column = :visit_source_concept_id
|
|
24
|
+
@join_id = :visit_occurrence_id
|
|
25
|
+
@table = :visit_occurrence
|
|
26
|
+
@source_column = :visit_source_concept_id
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def modified_query
|
|
30
|
+
return query unless dm.table_cols(source_table).include?(join_id)
|
|
31
|
+
return query if dm.table_cols(source_table).include?(column)
|
|
32
|
+
|
|
33
|
+
left_alias = "tab1".to_sym
|
|
34
|
+
right_alias = "tab2".to_sym
|
|
35
|
+
|
|
36
|
+
extra_table = query.db.from(table).select(Sequel.as(source_column, column), join_id)
|
|
37
|
+
ds = query.from_self(alias: left_alias)
|
|
38
|
+
ds.left_join(extra_table.as(right_alias),
|
|
39
|
+
Sequel.qualify(left_alias, join_id) => Sequel.qualify(right_alias, join_id))
|
|
40
|
+
.select_all(left_alias)
|
|
41
|
+
.select_append(Sequel.qualify(right_alias, column).as(column))
|
|
42
|
+
.from_self
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../query_modifier"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module QueryModifiers
|
|
7
|
+
module Omopv4Plus
|
|
8
|
+
class ProvenanceQueryModifier < QueryModifier
|
|
9
|
+
RELATED_COLUMNS = %w[
|
|
10
|
+
condition_type_concept_id
|
|
11
|
+
procedure_type_concept_id
|
|
12
|
+
death_type_concept_id
|
|
13
|
+
observation_type_concept_id
|
|
14
|
+
drug_type_concept_id
|
|
15
|
+
].sort.map(&:to_sym)
|
|
16
|
+
|
|
17
|
+
attr_reader :db
|
|
18
|
+
|
|
19
|
+
def self.provided_columns
|
|
20
|
+
%i[
|
|
21
|
+
file_provenance_type
|
|
22
|
+
code_provenance_type
|
|
23
|
+
]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.has_required_columns?(cols)
|
|
27
|
+
!(RELATED_COLUMNS & cols).empty?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def initialize(*args)
|
|
31
|
+
super
|
|
32
|
+
@db = query.db
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def modified_query
|
|
36
|
+
return query unless self.class.has_required_columns?(dm.table_cols(source_table))
|
|
37
|
+
|
|
38
|
+
query.select_append(Sequel[dm.file_provenance_type_column(query, source_table)].as(:file_provenance_type),
|
|
39
|
+
Sequel[dm.code_provenance_type_column(query,
|
|
40
|
+
source_table)].as(:code_provenance_type)).from_self
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../query_modifier"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module QueryModifiers
|
|
7
|
+
module Omopv4Plus
|
|
8
|
+
class ProviderQueryModifier < QueryModifier
|
|
9
|
+
def self.provided_columns
|
|
10
|
+
[ :provider_id ]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.has_required_columns?(cols)
|
|
14
|
+
cols.find { |col| col.to_s =~ /provider_id/ }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def modified_query
|
|
18
|
+
col = self.class.has_required_columns?(dm.table_cols(source_table))
|
|
19
|
+
return query if col.nil? || col.to_sym == :provider_id
|
|
20
|
+
|
|
21
|
+
query.select_append(Sequel[col].as(:provider_id)).from_self
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module QueryModifiers
|
|
5
|
+
class QueryModifier
|
|
6
|
+
attr_reader :query, :op, :source_table, :dm
|
|
7
|
+
|
|
8
|
+
def initialize(query, op, table, dm)
|
|
9
|
+
@query = query
|
|
10
|
+
@op = op
|
|
11
|
+
@source_table = table
|
|
12
|
+
@dm = dm
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def modified_query
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "generic"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module Rdbms
|
|
7
|
+
class DuckDB < Generic
|
|
8
|
+
# Returns from - to (see Generic#datediff). DuckDB's
|
|
9
|
+
# datediff(part, startdate, enddate) returns enddate - startdate, so the
|
|
10
|
+
# arguments go in reversed.
|
|
11
|
+
def datediff(from, to)
|
|
12
|
+
Sequel.function(:datediff, "day", to, from)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def preferred_formatter
|
|
16
|
+
SqlFormatters::PG_FORMAT
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# DuckDB accepts WITH ... AS MATERIALIZED since v0.8.0.
|
|
20
|
+
def supports_materialized?
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Rdbms
|
|
5
|
+
class Generic
|
|
6
|
+
def process(column, value = nil)
|
|
7
|
+
type = Scope::COLUMN_TYPES.fetch(column)
|
|
8
|
+
new_column = case type
|
|
9
|
+
when String, :String
|
|
10
|
+
Sequel.cast_string(value)
|
|
11
|
+
when Date, :Date
|
|
12
|
+
cast_date(value)
|
|
13
|
+
when Float, :Bigint, :Float
|
|
14
|
+
Sequel.cast_numeric(value, type)
|
|
15
|
+
else
|
|
16
|
+
raise "Unexpected type: '#{type.inspect}' for column: '#{column}'"
|
|
17
|
+
end
|
|
18
|
+
new_column.as(column)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def semi_join(ds, table, *exprs)
|
|
22
|
+
ds = Sequel[ds] if ds.is_a?(Symbol)
|
|
23
|
+
table = Sequel[table] if table.is_a?(Symbol)
|
|
24
|
+
expr = exprs.inject(&:&)
|
|
25
|
+
ds.from_self(alias: :l).where(ds.db[table.as(:r)]
|
|
26
|
+
.select(1)
|
|
27
|
+
.where(expr)
|
|
28
|
+
.exists).from_self
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# True when lhs and rhs are equal or both are NULL. IS NOT DISTINCT
|
|
32
|
+
# FROM is standard SQL, and Postgres and DuckDB support it. Spark
|
|
33
|
+
# overrides this with its <=> operator.
|
|
34
|
+
def null_safe_equal(lhs, rhs)
|
|
35
|
+
Sequel.lit("(? IS NOT DISTINCT FROM ?)", lhs, rhs)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def cast_date(date)
|
|
39
|
+
Sequel.cast(date, Date)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The number of days from from_column to to_column: to_column - from_column.
|
|
43
|
+
# Note that datediff takes its arguments in the opposite order.
|
|
44
|
+
def days_between(from_column, to_column)
|
|
45
|
+
cast_date(to_column) - cast_date(from_column)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def uuid
|
|
49
|
+
uuid_items
|
|
50
|
+
.zip([ Sequel.cast_string("/") ] * (uuid_items.length - 1))
|
|
51
|
+
.flatten
|
|
52
|
+
.compact
|
|
53
|
+
.inject(:+)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def uuid_items
|
|
57
|
+
%w[person_id criterion_id criterion_table start_date end_date].map do |column|
|
|
58
|
+
Sequel.cast_string(column.to_sym)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def preferred_formatter
|
|
63
|
+
SqlFormatters::SQL_FORMATTER
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns from - to in days, NOT to - from, so datediff(later, earlier)
|
|
67
|
+
# is positive. Every adapter keeps this order. This default emits
|
|
68
|
+
# Spark's datediff(endDate, startDate), which returns endDate - startDate,
|
|
69
|
+
# and Spark uses it as is. days_between(from, to) returns to - from.
|
|
70
|
+
def datediff(from, to)
|
|
71
|
+
Sequel.function(:datediff, from, to)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def analyze_temp_tables?
|
|
75
|
+
false
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def explain_temp_tables?
|
|
79
|
+
false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def supports_materialized?
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def create_options(_scope, _ds)
|
|
87
|
+
opts = {}
|
|
88
|
+
opts[:analyze] = opts[:explain] = explain_temp_tables?
|
|
89
|
+
opts
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def drop_options
|
|
93
|
+
{}
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def post_create(db, table_name); end
|
|
97
|
+
|
|
98
|
+
# One primary clinical code per collection: the row with the lowest
|
|
99
|
+
# concept id. ROW_NUMBER() works on every supported RDBMS. Postgres
|
|
100
|
+
# overrides this with DISTINCT ON.
|
|
101
|
+
def primary_concepts(db, all_primary_ids)
|
|
102
|
+
db[Sequel[:clinical_codes].as(:pcc)]
|
|
103
|
+
.where(provenance_concept_id: all_primary_ids)
|
|
104
|
+
.select(
|
|
105
|
+
Sequel[:pcc][:collection_id].as(:collection_id),
|
|
106
|
+
Sequel[:pcc][:clinical_code_concept_id].as(:concept_id),
|
|
107
|
+
Sequel[:pcc][:clinical_code_source_value].as(:concept_code),
|
|
108
|
+
Sequel[:pcc][:clinical_code_vocabulary_id].as(:vocabulary_id),
|
|
109
|
+
Sequel[true].as(:is_primary)
|
|
110
|
+
)
|
|
111
|
+
.select_append(Sequel[:ROW_NUMBER].function.over(
|
|
112
|
+
partition: :collection_id,
|
|
113
|
+
order: [ Sequel[:pcc][:collection_id], Sequel[:pcc][:clinical_code_concept_id] ]
|
|
114
|
+
).as(:nummy))
|
|
115
|
+
.from_self
|
|
116
|
+
.where(nummy: 1)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|