0.416 (04 Aug 2025)

4 August, 2025

This release adds conditional aggregate functions to computed fields, giving you more control over how related data is aggregated.

Conditional Aggregate Functions

Building on the computed fields released in 0.400, you can now use conditional aggregate functions to filter data before aggregating. This is useful when you need to exclude certain records from calculations, such as deleted items or inactive entries.

model Invoice {
    fields {
        items Item[]
 
        // Sum all items
        total Decimal @computed(SUM(invoice.items.price))
 
        // Sum only non-deleted items
        activeTotal Decimal @computed(SUMIF(invoice.items.price, !invoice.items.isDeleted))
 
        // Count only active items with price > 0
        activeCount Number @computed(COUNTIF(invoice.items, invoice.items.isActive && invoice.items.price > 0))
    }
}
 
model Item {
    fields {
        invoice Invoice
        price Decimal
        isActive Boolean @default(true)
        isDeleted Boolean @default(false)
    }
}

Available Functions

FunctionDescription
SUMIF(values, condition)Sum values where condition is true
COUNTIF(items, condition)Count items where condition is true
AVGIF(values, condition)Average of values where condition is true
MINIF(values, condition)Minimum value where condition is true
MAXIF(values, condition)Maximum value where condition is true
MEDIANIF(values, condition)Median of values where condition is true

Example: Class Grades

Here's a more complete example showing how conditional aggregates can be used to calculate statistics for active students only:

model Class {
    fields {
        name Text
        enrollments Enrollment[]
 
        // Only count and average grades for active students
        numberOfActiveStudents Number @computed(COUNTIF(class.enrollments, class.enrollments.student.isActive))
        averageGrade Decimal @computed(AVGIF(class.enrollments.grade, class.enrollments.student.isActive))
        highestGrade Decimal @computed(MAXIF(class.enrollments.grade, class.enrollments.student.isActive))
        lowestGrade Decimal @computed(MINIF(class.enrollments.grade, class.enrollments.student.isActive))
    }
}
 
model Student {
    fields {
        name Text
        isActive Boolean @default(true)
        enrollments Enrollment[]
    }
}
 
model Enrollment {
    fields {
        student Student
        class Class
        grade Decimal?
    }
}

Fixes and Improvements

For a full list of fixes and improvements, check out our GitHub releases page (opens in a new tab).

For any issues or feedback, please contact us at help@keel.so.

Thank you for using Keel!