Working with data
Expressions
Expressions allow defining business logic, computing and transforming data - either directly within a query, or on an expression type, which allows encpaulating business logic into reusiable types.
Expressions in queries
Expressions can be defined inline in a query body, or a given
clause:
Computing values with expressions
Expressions calculate values on-the-fly during query execution, eliminating the need for pre-computed fields
Schema
Play with this snippet by editing it here, or edit it on Taxi Playground
Result
Query failed
Expression types
Define reusable expressions as types:
// Define expression type
type RemainingSeats = TotalSeats - SoldSeats
type UtilizationRate = SoldSeats / TotalSeats
// Use in projections
find { Flight[] } as {
flightNumber : FlightNumber
remaining : RemainingSeats
utilization : UtilizationRate
}[]
Type expressions
Expression types can only reference other types, not field names. They provide reusable business logic.
Using expression types to filter collections
Create types that filter or transform collections:
type IsActive inherits Boolean
type IsPrimary inherits Boolean
// Filter to single item
type ActiveAddress inherits Address =
(Address[]) -> Address[].single((IsActive) -> IsActive == true)
// Filter to collection
type ActiveAddresses inherits Address[] =
(Address[]) -> Address[].filter((IsActive) -> IsActive == true)
// With negation
type InactiveAddresses inherits Address[] =
(Address[]) -> Address[].filter((IsActive) -> IsActive != true)
Collection operations
// single - find one item
type PrimaryContact =
(Contact[]) -> Contact[].single((IsPrimary) -> IsPrimary == true)
// filter - find multiple items
type ActiveContacts =
(Contact[]) -> Contact[].filter((IsActive) -> IsActive == true)
// map - transform items
type ContactNames =
(Contact[]) -> Contact[].map((Contact) -> Contact::FullName)
Combining traversal and expressions
Expression types can be traversed using standard type traversal
// Expression type using traversal
type PrimaryEmail inherits EmailAddress =
PrimaryContact::EmailAddress
// In a query
find { Case } as {
// Combines traversal with expression types
mainContactEmail: PrimaryApplicant::ActiveAddress::EmailAddress
// Multiple levels of expression types
primaryPhone: PrimaryContact::PreferredPhone
}
Data discovery in expressions
When evaluating expressions, TaxiQL:
- First checks the source object for required values
- Then searches available data in scope
- Finally calls services to fetch missing data
model Order {
basePrice : Price
customerId : CustomerId
}
// DiscountRate not on Order, will be discovered
find { Order[] } as {
price : Price
finalPrice : Decimal = Price * (1 - DiscountRate)
}[]
Conditional expressions
type PricingTier = when {
OrderTotal > 1000 -> "Premium"
OrderTotal > 100 -> "Standard"
else -> "Basic"
}
Advanced examples
Nested lambda expressions
// Find customers with active premium accounts
type PremiumActiveCustomers =
(Customer[]) -> Customer[]
.filter((HasAccount) -> HasAccount == true)
.filter((Account) -> Account::Status == "Premium")
.filter((Account) -> Account::IsActive == true)
Expression composition
// Compose multiple expression types
type DiscountedPrice = BasePrice * DiscountMultiplier
type TaxAmount = DiscountedPrice * TaxRate
type FinalPrice = DiscountedPrice + TaxAmount
find { Order[] } as {
orderId : OrderId
final : FinalPrice // Uses composed expressions
}[]
Common use cases
Business calculations
type GrossMargin = (Revenue - CostOfGoods) / Revenue
type NetProfit = Revenue - TotalExpenses
type ProfitMargin = NetProfit / Revenue * 100
Dynamic categorization
type CustomerSegment = when {
LifetimeValue > 10000 && LastOrderDays < 30 -> "VIP Active"
LifetimeValue > 10000 -> "VIP Dormant"
LastOrderDays < 90 -> "Regular Active"
else -> "At Risk"
}