Skip to content

Commit 067033c

Browse files
Merge pull request #96 from vfrank66:issue-94/complete-aggregate-functions
PiperOrigin-RevId: 750243581
2 parents 7a97d3b + 311d10d commit 067033c

17 files changed

Lines changed: 3791 additions & 258 deletions

interpreter/operator_aggregate.go

Lines changed: 1702 additions & 123 deletions
Large diffs are not rendered by default.

interpreter/operator_clinical.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package interpreter
1616

1717
import (
1818
"fmt"
19+
"time"
1920

2021
"github.com/google/cql/model"
2122
"github.com/google/cql/result"
@@ -25,6 +26,29 @@ import (
2526

2627
// CLINICAL OPERATORS - https://cql.hl7.org/09-b-cqlreference.html#clinical-operators-3
2728

29+
30+
// CalculateAgeIn[Years|Months|Weeks|Days|Hours|Minutes|Seconds](birthDate Date|DateTime) Integer
31+
// https://cql.hl7.org/09-b-cqlreference.html#calculateage
32+
func evalCalculateAge(u model.IUnaryExpression, birthObj result.Value) (result.Value, error) {
33+
m := u.(*model.CalculateAge)
34+
p := model.DateTimePrecision(m.Precision)
35+
if err := validatePrecision(p, []model.DateTimePrecision{model.YEAR, model.MONTH, model.WEEK, model.DAY, model.HOUR, model.MINUTE, model.SECOND}); err != nil {
36+
return result.Value{}, err
37+
}
38+
if result.IsNull(birthObj) {
39+
return result.New(nil)
40+
}
41+
42+
birth, err := result.ToDateTime(birthObj)
43+
if err != nil {
44+
return result.Value{}, err
45+
}
46+
47+
// Use current time as asOf time
48+
asOf := result.DateTime{Date: time.Now()}
49+
return calculateAgeAt(birth, asOf, p)
50+
}
51+
2852
// CalculateAgeIn[Years|Months|Weeks|Days]At(birthDate Date, asOf Date) Integer
2953
// https://cql.hl7.org/09-b-cqlreference.html#calculateageat
3054
func evalCalculateAgeAtDate(b model.IBinaryExpression, birthObj, asOfObj result.Value) (result.Value, error) {
@@ -93,6 +117,18 @@ func calculateAgeAt(birth, asOf result.DateTime, p model.DateTimePrecision) (res
93117
if p == model.DAY {
94118
return result.New(int(asOf.Date.Sub(birth.Date).Hours() / 24))
95119
}
120+
121+
if p == model.HOUR {
122+
return result.New(int(asOf.Date.Sub(birth.Date).Hours()))
123+
}
124+
125+
if p == model.MINUTE {
126+
return result.New(int(asOf.Date.Sub(birth.Date).Minutes()))
127+
}
128+
129+
if p == model.SECOND {
130+
return result.New(int(asOf.Date.Sub(birth.Date).Seconds()))
131+
}
96132

97133
// TODO(b/304349114): Per https://cql.hl7.org/09-b-cqlreference.html#ageat and
98134
// the external tests mentioned in b/304349114#comment3, these date-related

interpreter/operator_datetime.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -297,18 +297,23 @@ func evalCanConvertQuantity(b model.IBinaryExpression, lObj, rObj result.Value)
297297
func evalDifferenceBetweenDate(b model.IBinaryExpression, lObj, rObj result.Value) (result.Value, error) {
298298
m := b.(*model.DifferenceBetween)
299299
p := model.DateTimePrecision(m.Precision)
300-
if err := validatePrecision(p, []model.DateTimePrecision{model.YEAR, model.MONTH, model.WEEK, model.DAY}); err != nil {
301-
return result.Value{}, err
302-
}
303300

301+
// Handle null values
304302
if result.IsNull(lObj) || result.IsNull(rObj) {
305303
return result.New(nil)
306304
}
307305

306+
// Validate date precisions
307+
if err := validatePrecision(p, []model.DateTimePrecision{model.YEAR, model.MONTH, model.WEEK, model.DAY}); err != nil {
308+
return result.Value{}, err
309+
}
310+
311+
// Convert both to DateTime and compute difference
308312
l, r, err := applyToValues(lObj, rObj, result.ToDateTime)
309313
if err != nil {
310314
return result.Value{}, err
311315
}
316+
312317
return dateTimeDifference(l, r, p)
313318
}
314319

@@ -318,18 +323,23 @@ func evalDifferenceBetweenDate(b model.IBinaryExpression, lObj, rObj result.Valu
318323
func evalDifferenceBetweenDateTime(b model.IBinaryExpression, lObj, rObj result.Value) (result.Value, error) {
319324
m := b.(*model.DifferenceBetween)
320325
p := model.DateTimePrecision(m.Precision)
321-
if err := validatePrecision(p, []model.DateTimePrecision{model.YEAR, model.MONTH, model.WEEK, model.DAY, model.HOUR, model.MINUTE, model.SECOND, model.MILLISECOND}); err != nil {
322-
return result.Value{}, err
323-
}
324326

327+
// Handle null values
325328
if result.IsNull(lObj) || result.IsNull(rObj) {
326329
return result.New(nil)
327330
}
328331

332+
// Validate datetime precisions
333+
if err := validatePrecision(p, []model.DateTimePrecision{model.YEAR, model.MONTH, model.WEEK, model.DAY, model.HOUR, model.MINUTE, model.SECOND, model.MILLISECOND}); err != nil {
334+
return result.Value{}, err
335+
}
336+
337+
// Convert both to DateTime and compute difference
329338
l, r, err := applyToValues(lObj, rObj, result.ToDateTime)
330339
if err != nil {
331340
return result.Value{}, err
332341
}
342+
333343
return dateTimeDifference(l, r, p)
334344
}
335345

interpreter/operator_dispatcher.go

Lines changed: 167 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -544,25 +544,73 @@ func (i *interpreter) unaryOverloads(m model.IUnaryExpression) ([]convert.Overlo
544544
}, nil
545545
case *model.Max:
546546
return []convert.Overload[evalUnarySignature]{
547+
{
548+
Operands: []types.IType{&types.List{ElementType: types.Integer}},
549+
Result: i.evalMaxInteger,
550+
},
551+
{
552+
Operands: []types.IType{&types.List{ElementType: types.Long}},
553+
Result: i.evalMaxLong,
554+
},
555+
{
556+
Operands: []types.IType{&types.List{ElementType: types.Decimal}},
557+
Result: i.evalMaxDecimal,
558+
},
559+
{
560+
Operands: []types.IType{&types.List{ElementType: types.Quantity}},
561+
Result: i.evalMaxQuantity,
562+
},
547563
{
548564
Operands: []types.IType{&types.List{ElementType: types.Date}},
549-
Result: i.evalMaxDateTime,
565+
Result: i.evalMaxDate,
550566
},
551567
{
552568
Operands: []types.IType{&types.List{ElementType: types.DateTime}},
553569
Result: i.evalMaxDateTime,
554570
},
571+
{
572+
Operands: []types.IType{&types.List{ElementType: types.Time}},
573+
Result: i.evalMaxTime,
574+
},
575+
{
576+
Operands: []types.IType{&types.List{ElementType: types.String}},
577+
Result: i.evalMaxString,
578+
},
555579
}, nil
556580
case *model.Min:
557581
return []convert.Overload[evalUnarySignature]{
582+
{
583+
Operands: []types.IType{&types.List{ElementType: types.Integer}},
584+
Result: i.evalMinInteger,
585+
},
586+
{
587+
Operands: []types.IType{&types.List{ElementType: types.Long}},
588+
Result: i.evalMinLong,
589+
},
590+
{
591+
Operands: []types.IType{&types.List{ElementType: types.Decimal}},
592+
Result: i.evalMinDecimal,
593+
},
594+
{
595+
Operands: []types.IType{&types.List{ElementType: types.Quantity}},
596+
Result: i.evalMinQuantity,
597+
},
558598
{
559599
Operands: []types.IType{&types.List{ElementType: types.Date}},
560-
Result: i.evalMinDateTime,
600+
Result: i.evalMinDate,
561601
},
562602
{
563603
Operands: []types.IType{&types.List{ElementType: types.DateTime}},
564604
Result: i.evalMinDateTime,
565605
},
606+
{
607+
Operands: []types.IType{&types.List{ElementType: types.Time}},
608+
Result: i.evalMinTime,
609+
},
610+
{
611+
Operands: []types.IType{&types.List{ElementType: types.String}},
612+
Result: i.evalMinString,
613+
},
566614
}, nil
567615
case *model.Sum:
568616
return []convert.Overload[evalUnarySignature]{
@@ -605,6 +653,115 @@ func (i *interpreter) unaryOverloads(m model.IUnaryExpression) ([]convert.Overlo
605653
Result: i.evalPopulationStdDevQuantity,
606654
},
607655
}, nil
656+
case *model.PopulationVariance:
657+
return []convert.Overload[evalUnarySignature]{
658+
{
659+
Operands: []types.IType{&types.List{ElementType: types.Decimal}},
660+
Result: i.evalPopulationVarianceDecimal,
661+
},
662+
{
663+
Operands: []types.IType{&types.List{ElementType: types.Quantity}},
664+
Result: i.evalPopulationVarianceQuantity,
665+
},
666+
}, nil
667+
case *model.StdDev:
668+
return []convert.Overload[evalUnarySignature]{
669+
{
670+
Operands: []types.IType{&types.List{ElementType: types.Decimal}},
671+
Result: i.evalStdDevDecimal,
672+
},
673+
{
674+
Operands: []types.IType{&types.List{ElementType: types.Quantity}},
675+
Result: i.evalStdDevQuantity,
676+
},
677+
}, nil
678+
case *model.Variance:
679+
return []convert.Overload[evalUnarySignature]{
680+
{
681+
Operands: []types.IType{&types.List{ElementType: types.Decimal}},
682+
Result: i.evalVarianceDecimal,
683+
},
684+
{
685+
Operands: []types.IType{&types.List{ElementType: types.Quantity}},
686+
Result: i.evalVarianceQuantity,
687+
},
688+
}, nil
689+
case *model.Mode:
690+
return []convert.Overload[evalUnarySignature]{
691+
{
692+
Operands: []types.IType{&types.List{ElementType: types.Decimal}},
693+
Result: i.evalModeDecimal,
694+
},
695+
{
696+
Operands: []types.IType{&types.List{ElementType: types.Quantity}},
697+
Result: i.evalModeQuantity,
698+
},
699+
{
700+
Operands: []types.IType{&types.List{ElementType: types.String}},
701+
Result: i.evalModeString,
702+
},
703+
{
704+
Operands: []types.IType{&types.List{ElementType: types.Integer}},
705+
Result: i.evalModeInteger,
706+
},
707+
{
708+
Operands: []types.IType{&types.List{ElementType: types.Long}},
709+
Result: i.evalModeLong,
710+
},
711+
{
712+
Operands: []types.IType{&types.List{ElementType: types.Date}},
713+
Result: i.evalModeDate,
714+
},
715+
{
716+
Operands: []types.IType{&types.List{ElementType: types.DateTime}},
717+
Result: i.evalModeDateTime,
718+
},
719+
{
720+
Operands: []types.IType{&types.List{ElementType: types.Time}},
721+
Result: i.evalModeTime,
722+
},
723+
}, nil
724+
case *model.GeometricMean:
725+
return []convert.Overload[evalUnarySignature]{
726+
{
727+
Operands: []types.IType{&types.List{ElementType: types.Decimal}},
728+
Result: i.evalGeometricMeanDecimal,
729+
},
730+
{
731+
Operands: []types.IType{&types.List{ElementType: types.Quantity}},
732+
Result: i.evalGeometricMeanQuantity,
733+
},
734+
}, nil
735+
case *model.Product:
736+
return []convert.Overload[evalUnarySignature]{
737+
{
738+
Operands: []types.IType{&types.List{ElementType: types.Integer}},
739+
Result: i.evalProduct,
740+
},
741+
{
742+
Operands: []types.IType{&types.List{ElementType: types.Long}},
743+
Result: i.evalProduct,
744+
},
745+
{
746+
Operands: []types.IType{&types.List{ElementType: types.Decimal}},
747+
Result: i.evalProduct,
748+
},
749+
{
750+
Operands: []types.IType{&types.List{ElementType: types.Quantity}},
751+
Result: i.evalProduct,
752+
},
753+
}, nil
754+
case *model.CalculateAge:
755+
return []convert.Overload[evalUnarySignature]{
756+
{
757+
Operands: []types.IType{types.Date},
758+
Result: evalCalculateAge,
759+
},
760+
{
761+
Operands: []types.IType{types.DateTime},
762+
Result: evalCalculateAge,
763+
},
764+
}, nil
608765
case *model.Flatten:
609766
return []convert.Overload[evalUnarySignature]{
610767
{
@@ -875,6 +1032,14 @@ func (i *interpreter) binaryOverloads(m model.IBinaryExpression) ([]convert.Over
8751032
Operands: []types.IType{types.DateTime, types.DateTime},
8761033
Result: evalDifferenceBetweenDateTime,
8771034
},
1035+
{
1036+
Operands: []types.IType{types.DateTime, types.Date},
1037+
Result: evalDifferenceBetweenDateTime,
1038+
},
1039+
{
1040+
Operands: []types.IType{types.Date, types.DateTime},
1041+
Result: evalDifferenceBetweenDateTime,
1042+
},
8781043
}, nil
8791044
case *model.In:
8801045
// TODO(b/301606416): Support all other In operator overloads.

0 commit comments

Comments
 (0)