@@ -121,7 +121,7 @@ func (i *interpreter) evalQuery(q *model.Query) (result.Value, error) {
121
121
return result.Value {}, err
122
122
}
123
123
} else {
124
- i . sortByColumn (finalVals , q .Sort .ByItems )
124
+ err := i . sortByColumnOrExpression (finalVals , q .Sort .ByItems )
125
125
if err != nil {
126
126
return result.Value {}, err
127
127
}
@@ -490,49 +490,58 @@ func compareNumeralInt[t float64 | int64 | int32](left, right t) int {
490
490
}
491
491
}
492
492
493
- func (i * interpreter ) sortByColumn (objs []result.Value , sbis []model.ISortByItem ) error {
494
- // Validate sort column types.
495
- for _ , sortItems := range sbis {
496
- // TODO(b/316984809): Is this validation in advance necessary? What if other values (beyond
497
- // objs[0]) have a different runtime type for the property (e.g. if they're a choice type)?
498
- // Consider validating types inline during the sort instead.
499
- path := sortItems .(* model.SortByColumn ).Path
500
- propertyType , err := i .modelInfo .PropertyTypeSpecifier (objs [0 ].RuntimeType (), path )
493
+ func (i * interpreter ) dateTimeOrError (v result.Value ) (result.Value , error ) {
494
+ switch sr := v .GolangValue ().(type ) {
495
+ case result.DateTime :
496
+ return v , nil
497
+ case result.Named :
498
+ if sr .RuntimeType .Equal (& types.Named {TypeName : "FHIR.dateTime" }) {
499
+ return i .protoProperty (sr , "value" , types .DateTime )
500
+ }
501
+ }
502
+ return result.Value {}, fmt .Errorf ("sorting only currently supported on DateTime columns" )
503
+ }
504
+
505
+ // getSortValue returns the value to be used for the comparison-based sort. This
506
+ // is typically a field or expression on the structure being sorted.
507
+ func (i * interpreter ) getSortValue (it model.ISortByItem , v result.Value ) (result.Value , error ) {
508
+ var rv result.Value
509
+ var err error
510
+ switch iv := it .(type ) {
511
+ case * model.SortByColumn :
512
+ // Passing the static types here is likely unimportant, but we compute it for completeness.
513
+ t , err := i .modelInfo .PropertyTypeSpecifier (v .RuntimeType (), iv .Path )
501
514
if err != nil {
502
- return err
515
+ return result. Value {}, err
503
516
}
504
- columnVal , err : = i .valueProperty (objs [ 0 ], path , propertyType )
517
+ rv , err = i .valueProperty (v , iv . Path , t )
505
518
if err != nil {
506
- return err
519
+ return result. Value {}, err
507
520
}
508
- // Strictly only allow DateTimes for now.
509
- // TODO(b/316984809): add sorting support for other types.
510
- if ! columnVal .RuntimeType ().Equal (types .DateTime ) {
511
- return fmt .Errorf ("sort column of a query must evaluate to a date time, instead got %v" , columnVal .RuntimeType ())
521
+ case * model.SortByExpression :
522
+ i .refs .EnterStructScope (v )
523
+ defer i .refs .ExitStructScope ()
524
+ rv , err = i .evalExpression (iv .SortExpression )
525
+ if err != nil {
526
+ return result.Value {}, err
512
527
}
528
+ default :
529
+ return result.Value {}, fmt .Errorf ("internal error - unsupported sort by item type: %T" , iv )
513
530
}
514
531
532
+ return i .dateTimeOrError (rv )
533
+ }
534
+
535
+ func (i * interpreter ) sortByColumnOrExpression (objs []result.Value , sbis []model.ISortByItem ) error {
515
536
var sortErr error = nil
516
537
slices .SortFunc (objs [:], func (a , b result.Value ) int {
517
- for _ , sortItems := range sbis {
518
- sortCol := sortItems .(* model.SortByColumn )
519
- // Passing the static types here is likely unimportant, but we compute it for completeness.
520
- aType , err := i .modelInfo .PropertyTypeSpecifier (a .RuntimeType (), sortCol .Path )
521
- if err != nil {
522
- sortErr = err
523
- continue
524
- }
525
- ap , err := i .valueProperty (a , sortCol .Path , aType )
526
- if err != nil {
527
- sortErr = err
528
- continue
529
- }
530
- bType , err := i .modelInfo .PropertyTypeSpecifier (b .RuntimeType (), sortCol .Path )
538
+ for _ , sortItem := range sbis {
539
+ ap , err := i .getSortValue (sortItem , a )
531
540
if err != nil {
532
541
sortErr = err
533
542
continue
534
543
}
535
- bp , err := i .valueProperty ( b , sortCol . Path , bType )
544
+ bp , err := i .getSortValue ( sortItem , b )
536
545
if err != nil {
537
546
sortErr = err
538
547
continue
@@ -544,7 +553,7 @@ func (i *interpreter) sortByColumn(objs []result.Value, sbis []model.ISortByItem
544
553
// TODO(b/308012659): Implement dateTime comparison that doesn't take a precision.
545
554
if av .Equal (bv ) {
546
555
continue
547
- } else if sortCol . SortByItem . Direction == model .DESCENDING {
556
+ } else if sortItem . SortDirection () == model .DESCENDING {
548
557
return bv .Compare (av )
549
558
}
550
559
return av .Compare (bv )
0 commit comments