make sure we can send limit and offset when querying.
list the diagnostic reports in the dropdown for filtering.
This commit is contained in:
parent
c258ab44d4
commit
6d831f6ee7
|
@ -8,6 +8,7 @@
|
||||||
.idea/**/tasks.xml
|
.idea/**/tasks.xml
|
||||||
.idea/**/dictionaries
|
.idea/**/dictionaries
|
||||||
.idea/**/shelf
|
.idea/**/shelf
|
||||||
|
.idea/dataSources.xml
|
||||||
|
|
||||||
# Sensitive or high-churn files
|
# Sensitive or high-churn files
|
||||||
.idea/**/dataSources/
|
.idea/**/dataSources/
|
||||||
|
|
|
@ -34,19 +34,20 @@ const (
|
||||||
|
|
||||||
const TABLE_ALIAS = "fhir"
|
const TABLE_ALIAS = "fhir"
|
||||||
|
|
||||||
//Allows users to use SearchParameters to query resources
|
// Allows users to use SearchParameters to query resources
|
||||||
// Can generate simple or complex queries, depending on the SearchParameter type:
|
// Can generate simple or complex queries, depending on the SearchParameter type:
|
||||||
//
|
//
|
||||||
// eg. Simple
|
// eg. Simple
|
||||||
//
|
//
|
||||||
//
|
|
||||||
// eg. Complex
|
// eg. Complex
|
||||||
// SELECT fhir.*
|
// SELECT fhir.*
|
||||||
// FROM fhir_observation as fhir, json_each(fhir.code) as codeJson
|
// FROM fhir_observation as fhir, json_each(fhir.code) as codeJson
|
||||||
// WHERE (
|
// WHERE (
|
||||||
|
//
|
||||||
// (codeJson.value ->> '$.code' = "29463-7" AND codeJson.value ->> '$.system' = "http://loinc.org")
|
// (codeJson.value ->> '$.code' = "29463-7" AND codeJson.value ->> '$.system' = "http://loinc.org")
|
||||||
// OR (codeJson.value ->> '$.code' = "3141-9" AND codeJson.value ->> '$.system' = "http://loinc.org")
|
// OR (codeJson.value ->> '$.code' = "3141-9" AND codeJson.value ->> '$.system' = "http://loinc.org")
|
||||||
// OR (codeJson.value ->> '$.code' = "27113001" AND codeJson.value ->> '$.system' = "http://snomed.info/sct")
|
// OR (codeJson.value ->> '$.code' = "27113001" AND codeJson.value ->> '$.system' = "http://snomed.info/sct")
|
||||||
|
//
|
||||||
// )
|
// )
|
||||||
// AND (user_id = "6efcd7c5-3f29-4f0d-926d-a66ff68bbfc2")
|
// AND (user_id = "6efcd7c5-3f29-4f0d-926d-a66ff68bbfc2")
|
||||||
// GROUP BY `fhir`.`id`
|
// GROUP BY `fhir`.`id`
|
||||||
|
@ -142,7 +143,7 @@ func (sr *SqliteRepository) sqlQueryResources(ctx context.Context, query models.
|
||||||
//defaults
|
//defaults
|
||||||
selectClauses := []string{fmt.Sprintf("%s.*", TABLE_ALIAS)}
|
selectClauses := []string{fmt.Sprintf("%s.*", TABLE_ALIAS)}
|
||||||
groupClause := fmt.Sprintf("%s.id", TABLE_ALIAS)
|
groupClause := fmt.Sprintf("%s.id", TABLE_ALIAS)
|
||||||
orderClause := fmt.Sprintf("%s.sort_date ASC", TABLE_ALIAS)
|
orderClause := fmt.Sprintf("%s.sort_date DESC", TABLE_ALIAS)
|
||||||
if query.Aggregations != nil {
|
if query.Aggregations != nil {
|
||||||
|
|
||||||
//Handle Aggregations
|
//Handle Aggregations
|
||||||
|
@ -210,12 +211,21 @@ func (sr *SqliteRepository) sqlQueryResources(ctx context.Context, query models.
|
||||||
fromClauses = lo.Uniq(fromClauses)
|
fromClauses = lo.Uniq(fromClauses)
|
||||||
fromClauses = lo.Compact(fromClauses)
|
fromClauses = lo.Compact(fromClauses)
|
||||||
|
|
||||||
return sr.GormClient.WithContext(ctx).
|
fluentQuery := sr.GormClient.WithContext(ctx).
|
||||||
Select(strings.Join(selectClauses, ", ")).
|
Select(strings.Join(selectClauses, ", ")).
|
||||||
Where(strings.Join(whereClauses, " AND "), whereNamedParameters).
|
Where(strings.Join(whereClauses, " AND "), whereNamedParameters).
|
||||||
Group(groupClause).
|
Group(groupClause).
|
||||||
Order(orderClause).
|
Order(orderClause)
|
||||||
Table(strings.Join(fromClauses, ", ")), nil
|
|
||||||
|
//add limit and offset clauses if present
|
||||||
|
if query.Limit != nil {
|
||||||
|
fluentQuery = fluentQuery.Limit(*query.Limit)
|
||||||
|
}
|
||||||
|
if query.Offset != nil {
|
||||||
|
fluentQuery = fluentQuery.Offset(*query.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fluentQuery.Table(strings.Join(fromClauses, ", ")), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/// INTERNAL functionality. These functions are exported for testing, but are not available in the Interface
|
/// INTERNAL functionality. These functions are exported for testing, but are not available in the Interface
|
||||||
|
@ -227,13 +237,16 @@ type SearchParameter struct {
|
||||||
Modifier string
|
Modifier string
|
||||||
}
|
}
|
||||||
|
|
||||||
//Lists in the SearchParameterValueOperatorTree are AND'd together, and items within each SearchParameterValueOperatorTree list are OR'd together
|
// Lists in the SearchParameterValueOperatorTree are AND'd together, and items within each SearchParameterValueOperatorTree list are OR'd together
|
||||||
//For example, the following would be AND'd together, and then OR'd with the next SearchParameterValueOperatorTree
|
// For example, the following would be AND'd together, and then OR'd with the next SearchParameterValueOperatorTree
|
||||||
|
//
|
||||||
// {
|
// {
|
||||||
// {SearchParameterValue{Value: "foo"}, SearchParameterValue{Value: "bar"}}
|
// {SearchParameterValue{Value: "foo"}, SearchParameterValue{Value: "bar"}}
|
||||||
// {SearchParameterValue{Value: "baz"}},
|
// {SearchParameterValue{Value: "baz"}},
|
||||||
// }
|
// }
|
||||||
//This would result in the following SQL:
|
//
|
||||||
|
// This would result in the following SQL:
|
||||||
|
//
|
||||||
// (value = "foo" OR value = "bar") AND (value = "baz")
|
// (value = "foo" OR value = "bar") AND (value = "baz")
|
||||||
type SearchParameterValueOperatorTree [][]SearchParameterValue
|
type SearchParameterValueOperatorTree [][]SearchParameterValue
|
||||||
|
|
||||||
|
@ -243,7 +256,7 @@ type SearchParameterValue struct {
|
||||||
SecondaryValues map[string]interface{}
|
SecondaryValues map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
//SearchParameters are made up of parameter names and modifiers. For example, "name" and "name:exact" are both valid search parameters
|
// SearchParameters are made up of parameter names and modifiers. For example, "name" and "name:exact" are both valid search parameters
|
||||||
// This function will parse the searchCodeWithModifier and return the SearchParameter
|
// This function will parse the searchCodeWithModifier and return the SearchParameter
|
||||||
func ProcessSearchParameter(searchCodeWithModifier string, searchParamTypeLookup map[string]string) (SearchParameter, error) {
|
func ProcessSearchParameter(searchCodeWithModifier string, searchParamTypeLookup map[string]string) (SearchParameter, error) {
|
||||||
searchParameter := SearchParameter{}
|
searchParameter := SearchParameter{}
|
||||||
|
@ -284,6 +297,7 @@ func ProcessSearchParameter(searchCodeWithModifier string, searchParamTypeLookup
|
||||||
// top level is AND'd together, and each item within the lists are OR'd together
|
// top level is AND'd together, and each item within the lists are OR'd together
|
||||||
//
|
//
|
||||||
// For example, searchParamCodeValueOrValuesWithPrefix may be:
|
// For example, searchParamCodeValueOrValuesWithPrefix may be:
|
||||||
|
//
|
||||||
// "code": "29463-7,3141-9,27113001"
|
// "code": "29463-7,3141-9,27113001"
|
||||||
// "code": ["le29463-7", "gt3141-9", "27113001"]
|
// "code": ["le29463-7", "gt3141-9", "27113001"]
|
||||||
func ProcessSearchParameterValueIntoOperatorTree(searchParameter SearchParameter, searchParamCodeValueOrValuesWithPrefix interface{}) (SearchParameterValueOperatorTree, error) {
|
func ProcessSearchParameterValueIntoOperatorTree(searchParameter SearchParameter, searchParamCodeValueOrValuesWithPrefix interface{}) (SearchParameterValueOperatorTree, error) {
|
||||||
|
@ -416,7 +430,7 @@ func NamedParameterWithSuffix(parameterName string, suffix string) string {
|
||||||
return fmt.Sprintf("%s_%s", parameterName, suffix)
|
return fmt.Sprintf("%s_%s", parameterName, suffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
//SearchCodeToWhereClause converts a searchCode and searchCodeValue to a where clause and a map of named parameters
|
// SearchCodeToWhereClause converts a searchCode and searchCodeValue to a where clause and a map of named parameters
|
||||||
func SearchCodeToWhereClause(searchParam SearchParameter, searchParamValue SearchParameterValue, namedParameterSuffix string) (string, map[string]interface{}, error) {
|
func SearchCodeToWhereClause(searchParam SearchParameter, searchParamValue SearchParameterValue, namedParameterSuffix string) (string, map[string]interface{}, error) {
|
||||||
|
|
||||||
//add named parameters to the lookup map. Basically, this is a map of all the named parameters that will be used in the where clause we're generating
|
//add named parameters to the lookup map. Basically, this is a map of all the named parameters that will be used in the where clause we're generating
|
||||||
|
@ -575,7 +589,7 @@ func AggregationParameterToClause(aggParameter SearchParameter) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//ProcessAggregationParameter processes the aggregation parameters which are fields with optional properties:
|
// ProcessAggregationParameter processes the aggregation parameters which are fields with optional properties:
|
||||||
// Fields that are primitive types (number, uri) must not have any property specified:
|
// Fields that are primitive types (number, uri) must not have any property specified:
|
||||||
// eg. `probability`
|
// eg. `probability`
|
||||||
//
|
//
|
||||||
|
|
|
@ -11,6 +11,8 @@ type QueryResource struct {
|
||||||
Select []string `json:"select"`
|
Select []string `json:"select"`
|
||||||
From string `json:"from"`
|
From string `json:"from"`
|
||||||
Where map[string]interface{} `json:"where"`
|
Where map[string]interface{} `json:"where"`
|
||||||
|
Limit *int `json:"limit,omitempty"`
|
||||||
|
Offset *int `json:"offset,omitempty"`
|
||||||
|
|
||||||
//aggregation fields
|
//aggregation fields
|
||||||
Aggregations *QueryResourceAggregations `json:"aggregations"`
|
Aggregations *QueryResourceAggregations `json:"aggregations"`
|
||||||
|
@ -56,7 +58,13 @@ func (q *QueryResource) Validate() error {
|
||||||
if strings.Contains(q.Aggregations.OrderBy, " ") {
|
if strings.Contains(q.Aggregations.OrderBy, " ") {
|
||||||
return fmt.Errorf("order_by cannot have spaces (or aliases)")
|
return fmt.Errorf("order_by cannot have spaces (or aliases)")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.Limit != nil && *q.Limit < 0 {
|
||||||
|
return fmt.Errorf("'limit' must be greater than or equal to zero")
|
||||||
|
}
|
||||||
|
if q.Offset != nil && *q.Offset < 0 {
|
||||||
|
return fmt.Errorf("'offset' must be greater than or equal to zero")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -3,8 +3,8 @@ export class DashboardWidgetQuery {
|
||||||
select: string[]
|
select: string[]
|
||||||
from: string
|
from: string
|
||||||
where: {[key: string]: string | string[]}
|
where: {[key: string]: string | string[]}
|
||||||
// limit: number
|
limit?: number
|
||||||
// offset: number
|
offset?: number
|
||||||
|
|
||||||
//https://lodash.com/docs/4.17.15#unionBy
|
//https://lodash.com/docs/4.17.15#unionBy
|
||||||
aggregations?: {
|
aggregations?: {
|
||||||
|
|
|
@ -22,8 +22,7 @@
|
||||||
<div ngbDropdownMenu aria-labelledby="dropdownReports">
|
<div ngbDropdownMenu aria-labelledby="dropdownReports">
|
||||||
<button ngbDropdownItem>All</button>
|
<button ngbDropdownItem>All</button>
|
||||||
<button ngbDropdownItem [disabled]="true">-----</button>
|
<button ngbDropdownItem [disabled]="true">-----</button>
|
||||||
<button ngbDropdownItem>Report 1</button>
|
<button *ngFor="let diagnosticReport of diagnosticReports" ngbDropdownItem>{{diagnosticReport?.sort_title}} [{{diagnosticReport?.sort_date | amDateFormat: 'LL'}}]</button>
|
||||||
<button ngbDropdownItem>Report 2</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ngbDropdown class="d-inline-block float-right">
|
<div ngbDropdown class="d-inline-block float-right">
|
||||||
|
|
|
@ -17,12 +17,39 @@ export class ReportLabsComponent implements OnInit {
|
||||||
|
|
||||||
isEmptyReport = false
|
isEmptyReport = false
|
||||||
|
|
||||||
|
diagnosticReports: ResourceFhir[] = []
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private fastenApi: FastenApiService,
|
private fastenApi: FastenApiService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
|
|
||||||
|
this.fastenApi.queryResources({
|
||||||
|
select: ["*"],
|
||||||
|
from: "DiagnosticReport",
|
||||||
|
where: {
|
||||||
|
"category": "http://terminology.hl7.org/CodeSystem/v2-0074|LAB",
|
||||||
|
},
|
||||||
|
limit: 5,
|
||||||
|
}).subscribe(results => {
|
||||||
|
this.diagnosticReports = results.data
|
||||||
|
console.log("ALL DIAGNOSTIC REPORTS", results)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fastenApi.queryResources({
|
||||||
|
select: ["*"],
|
||||||
|
from: "Observation",
|
||||||
|
where: {},
|
||||||
|
aggregations: {
|
||||||
|
order_by: "code:code"
|
||||||
|
}
|
||||||
|
}).subscribe(results => {
|
||||||
|
console.log("OBSERVATIONS GROUPED", results)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
this.fastenApi.getResources("Observation").subscribe(results => {
|
this.fastenApi.getResources("Observation").subscribe(results => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
results = results || []
|
results = results || []
|
||||||
|
|
Loading…
Reference in New Issue