Expressions

Learn how to build complex JEXL expressions using functions, transforms, and operators

JEXL Expressions

JEXL expressions are the heart of the language, combining literals, variables, operators, functions, and transforms to create powerful data processing pipelines. This guide covers how to build and structure complex expressions effectively.

Expression Fundamentals

Simple Expressions

// Literal values
42
"hello world"
true
null

// Variable access
name
user.email
scores[0]

Complex Expressions

// Arithmetic with variables
(score1 + score2 + score3) / 3

// String manipulation
firstName + " " + lastName | uppercase

// Conditional logic
age >= 18 ? "adult" : "minor"

Function Calls

Functions perform operations and return values. JEXL Extended provides 80+ built-in functions.

Basic Function Calls

// Single argument
length("hello")              // 5
abs(-10)                     // 10
uppercase("hello")           // "HELLO"

// Multiple arguments
max([1, 5, 3, 9, 2])        // 9
substring("hello world", 0, 5)  // "hello"
contains("hello world", "world")  // true

Nested Function Calls

// Functions within functions
length(split("a,b,c", ","))     // 3
max(map([1, 2, 3], "value * 2"))  // 6
round(average([1.1, 2.7, 3.9]), 2)  // 2.57

Functions with Complex Arguments

// Using expressions as arguments
filter(users, "value.age > " + minAge)
map(items, "value.price * " + taxRate)
sort(products, "value.priority == 'high' ? 1 : 2")

Transform Operations

Transforms use the pipe operator (|) to create data processing pipelines.

Single Transforms

"  hello world  " | trim        // "hello world"
[1, 2, 3, 4, 5] | length       // 5
{a: 1, b: 2, c: 3} | keys      // ["a", "b", "c"]

Transform Chains

// String processing pipeline
text | trim | lowercase | split(" ") | join("-")

// Array processing pipeline
numbers | filter("value > 0") | map("value * 2") | sort | reverse

// Mixed data processing
users | filter("value.active") | map("value.email") | distinct | sort

Transforms with Arguments

// Transform functions with parameters
"hello world" | substring(0, 5)           // "hello"
[1, 2, 3, 4] | filter("value > 2")       // [3, 4]
"apple,banana,cherry" | split(",")        // ["apple", "banana", "cherry"]

Conditional Expressions

Ternary Operator

// Simple conditions
status = isActive ? "online" : "offline"
message = count == 1 ? "1 item" : count + " items"

// Nested conditions
grade = score >= 90 ? "A" :
        score >= 80 ? "B" :
        score >= 70 ? "C" :
        score >= 60 ? "D" : "F"

Logical Operations for Conditions

// Multiple conditions
canVote = age >= 18 && citizenship == "US" && registered == true
hasAccess = isAdmin || (isMember && subscription.active)

// Short-circuit evaluation
userName = user && user.profile && user.profile.name || "Anonymous"

Array Operations

Array Creation and Manipulation

// Creating arrays
scores = [95, 87, 92, 78, 88]
names = ["Alice", "Bob", "Charlie"]
mixed = [user.name, user.age, user.active]

// Array transformations
highScores = scores | filter("value > 85")          // [95, 87, 92, 88]
upperNames = names | map("value | uppercase")       // ["ALICE", "BOB", "CHARLIE"]
sortedScores = scores | sort | reverse              // [95, 92, 88, 87, 78]

Array Aggregations

// Statistical operations
totalScore = scores | sum                           // 440
averageScore = scores | average                     // 88
highestScore = scores | max                         // 95
lowestScore = scores | min                          // 78

// Array analysis
uniqueValues = data | distinct
itemCount = items | length
hasHighScores = scores | any("value > 90")         // true
allPassing = scores | all("value >= 60")           // true

Advanced Array Processing

// Group and process
usersByDepartment = users | groupBy("value.department")
departmentCounts = usersByDepartment | map("length(value)")

// Find operations
firstAdult = users | find("value.age >= 18")
adminIndex = users | findIndex("value.role == 'admin'")

// Array reduction
concatenated = strings | reduce("acc + value", "")
product = numbers | reduce("acc * value", 1)

Object Operations

Object Creation and Access

// Creating objects
user = {
  name: firstName + " " + lastName,
  age: currentYear - birthYear,
  isAdult: age >= 18,
  email: name | lowercase | replace(" ", ".") + "@company.com"
}

// Dynamic property access
property = "email"
userEmail = user[property]

Object Transformation

// Extract object information
userKeys = user | keys                              // ["name", "age", "isAdult", "email"]
userValues = user | values                          // ["John Doe", 30, true, "john.doe@company.com"]
userEntries = user | entries                        // [["name", "John Doe"], ...]

// Object merging
defaults = {theme: "light", timeout: 5000}
userPrefs = {theme: "dark"}
settings = merge(defaults, userPrefs)               // {theme: "dark", timeout: 5000}

String Processing

String Manipulation Chains

// Clean and format text
cleanTitle = rawTitle | trim | lowercase | replace(/[^a-z0-9\s]/g, "") | split(" ") | join("-")

// Text analysis
wordCount = text | split(" ") | length
hasKeyword = text | lowercase | contains(searchTerm | lowercase)

// String formatting
formatted = template | replace("{name}", user.name) | replace("{date}", now() | dateTimeFormat("YYYY-MM-DD"))

String Validation

// Email validation pattern
isValidEmail = email | contains("@") && email | contains(".") && length(email) > 5

// Password strength
isStrongPassword = password | length >= 8 && 
                  password | contains(/[A-Z]/) && 
                  password | contains(/[a-z]/) && 
                  password | contains(/[0-9]/)

Mathematical Expressions

Calculations

// Complex calculations
totalPrice = items | map("value.price * value.quantity") | sum
taxAmount = totalPrice * taxRate
finalPrice = totalPrice + taxAmount

// Statistics
variance = numbers | map("(value - " + (numbers | average) + ") ^ 2") | average
standardDeviation = sqrt(variance)

// Financial calculations
monthlyPayment = principal * (rate * (1 + rate)^months) / ((1 + rate)^months - 1)

Mathematical Functions

// Trigonometry and advanced math
hypotenuse = sqrt(a^2 + b^2)
area = 3.14159 * radius^2
compound = principal * (1 + rate/periods)^(periods * years)

// Rounding and formatting
formatted = value | round(2) | formatNumber("$#,##0.00")
percentage = (value / total * 100) | round(1) + "%"

Date and Time Operations

Date Calculations

// Current time operations
currentTime = now()
currentMillis = millis()
formatted = currentTime | dateTimeFormat("YYYY-MM-DD HH:mm:ss")

// Date arithmetic
futureDate = currentTime | dateTimeAdd("days", 30)
pastDate = currentTime | dateTimeAdd("months", -6)

// Time comparisons
isRecent = (now() | dateTimeToMillis) - (createdDate | dateTimeToMillis) < 86400000  // 24 hours
age = (now() | dateTimeToMillis) - (birthDate | dateTimeToMillis) | millisToDateTime | dateTimeFormat("YYYY")

Error Handling and Safety

Safe Property Access

// Null-safe operations
userName = user && user.profile && user.profile.name || "Unknown"
email = user?.profile?.contact?.email || "No email"

// Checking existence
hasEmail = "email" in user && user.email != null && user.email != ""
validUser = user != null && "name" in user && "id" in user

Type Safety

// Type checking before operations
safeLength = typeof value == "string" || Array.isArray(value) ? length(value) : 0
safeUppercase = typeof text == "string" ? text | uppercase : text

// Default values for different types
safeName = typeof name == "string" && name | trim | length > 0 ? name | trim : "Unknown"
safeNumber = typeof num == "number" && !isNaN(num) ? num : 0

Expression Composition

Building Complex Logic

// User eligibility check
isEligible = user.age >= minAge && 
            user.status == "active" && 
            user.credits >= requiredCredits &&
            user.lastLogin | dateTimeToMillis >= cutoffDate | dateTimeToMillis

// Data processing pipeline
result = rawData 
  | filter("value != null && value.id != null")
  | map("merge(value, {processedAt: now()})")
  | sort("value.priority")
  | filter("value.category in allowedCategories")
  | map("transform(value)")

Reusable Sub-expressions

// Define common calculations
taxRate = config.taxRate || 0.08
shippingCost = weight > 50 ? 25 : weight > 20 ? 15 : 5
discount = membership == "premium" ? 0.15 : membership == "standard" ? 0.10 : 0

// Use in main calculation
finalPrice = (basePrice * (1 - discount) * (1 + taxRate)) + shippingCost

Performance Considerations

Efficient Expression Structure

// Good - filter early, transform late
result = largeArray 
  | filter("value.active && value.score > threshold")
  | map("complexTransformation(value)")

// Less efficient - transform everything first
result = largeArray 
  | map("complexTransformation(value)")
  | filter("value.active && value.score > threshold")

Minimize Function Calls

// Good - calculate once
currentTime = now()
recent = items | filter("value.timestamp > " + (currentTime - 86400000))

// Less efficient - calculate repeatedly
recent = items | filter("value.timestamp > " + (now() - 86400000))

Common Expression Patterns

Data Validation

// Form validation
isValidForm = name | trim | length > 0 &&
              email | contains("@") &&
              age >= 18 &&
              termsAccepted == true

// Data completeness
isComplete = requiredFields | all("value in data && data[value] != null")

Data Transformation

// API response transformation
apiResponse = rawData | map("{
  id: value.id,
  name: value.full_name | trim,
  email: value.email_address | lowercase,
  isActive: value.status == 'active',
  lastSeen: value.last_login | dateTimeFormat('YYYY-MM-DD')
}")

Aggregation and Reporting

// Sales report
report = {
  totalSales: orders | sum("value.amount"),
  averageOrder: orders | average("value.amount"),
  topCustomer: orders | groupBy("value.customerId") | map("sum(value, 'amount')") | max,
  ordersByStatus: orders | groupBy("value.status") | map("length(value)")
}

Search and Filtering

// Advanced search
searchResults = products 
  | filter("
    (searchTerm == null || value.name | lowercase | contains(searchTerm | lowercase)) &&
    (minPrice == null || value.price >= minPrice) &&
    (maxPrice == null || value.price <= maxPrice) &&
    (category == null || value.category == category)
  ")
  | sort("value.relevanceScore")
  | map("merge(value, {highlighted: highlightMatches(value.name, searchTerm)})")

Best Practices

1. Use Meaningful Names

// Good
isEligibleCustomer = customer.age >= 18 && customer.creditScore > 600
customerFullName = customer.firstName + " " + customer.lastName

// Less clear
result = c.a >= 18 && c.cs > 600
name = c.fn + " " + c.ln

2. Break Complex Expressions

// Good - readable steps
eligibilityAge = customer.age >= minimumAge
eligibilityCredit = customer.creditScore >= minimumCredit
eligibilityStatus = customer.status == "active"
isEligible = eligibilityAge && eligibilityCredit && eligibilityStatus

// Harder to read
isEligible = customer.age >= minimumAge && customer.creditScore >= minimumCredit && customer.status == "active"

3. Use Comments for Complex Logic

// Calculate compound interest: P(1 + r/n)^(nt)
futureValue = principal * (1 + annualRate / compoundingsPerYear) ^ (compoundingsPerYear * years)

// Filter active users who logged in within the last 30 days
activeUsers = users | filter("value.status == 'active' && (now() - value.lastLogin) < 2592000000")

4. Validate Inputs

// Good - safe processing
result = input != null && typeof input == "string" ? 
         input | trim | lowercase | split(",") | map("value | trim") :
         []

// Risky - assumes input is valid
result = input | trim | lowercase | split(",") | map("value | trim")

Next: Learn about Context and Variables in JEXL expressions.

Cookie Notice

We use cookies to enhance your browsing experience.