Appearance
Best Practices for Expression Language Usage
This guide provides comprehensive best practices for working with the expression language, organized by topic and supported with practical examples and implementation details.
1. Expression Clarity and Organization
Use Parentheses for Complex Logic
Always use parentheses to make complex logical expressions clear and unambiguous. This is especially important when combining multiple operators.
js
// Hard to read and potentially ambiguous
status == "active" and priority > 3 or category == "urgent"
// Clear and unambiguous
(status == "active" and priority > 3) or category == "urgent"
// Complex nested logic made clear
((status == "active" or status == "pending") and priority > 3) or
(category == "urgent" and @hasRole(user))
Implementation Note: The parser (parser_expr_paren.go
) handles nested expressions efficiently, so there's no performance penalty for using parentheses.
Maintain Consistent Formatting
- Use spaces around operators for better readability
- Break long expressions into multiple lines at logical points
- Align nested expressions for better structure visualization
js
// Poor formatting
status=="active"and(priority>3or category=="urgent")
// Good formatting
status == "active" and (
priority > 3 or
category == "urgent"
)
2. String Operations Best Practices
Choose the Right String Operation
Select the most appropriate string operation based on your needs:
js
// For exact prefix matching
name starts "Test" // More efficient than using match "^Test.*"
// For suffix matching
email ends "@example.com" // More efficient than using match ".*@example.com$"
// For substring presence
description includes "error" // More efficient than using match ".*error.*"
// For pattern matching (use only when needed)
log match "ERROR|WARN: .*"
Implementation Details: See expr_function.go
for the optimized implementation of these operations.
String Matching Optimization
- Use
starts
,ends
, andincludes
overmatch
when possible - Reserve
match
for complex pattern matching needs - Consider case sensitivity requirements
js
// Inefficient
name match "^user.*"
email match ".*@company\\.com$"
// Efficient
name starts "user"
email ends "@company.com"
3. Type Safety and Comparisons
Handle Type Compatibility
Be mindful of type compatibility in comparisons and ensure proper type handling:
js
// Good practices
age > 18 // Numeric comparison
status == "active" // String comparison
isEnabled == true // Boolean comparison
// Avoid implicit type conversions
priority == "5" // Bad: mixing string and number
age > "18" // Bad: comparing number with string
Implementation Note: See helper.go
for type conversion utilities and type checking implementation.
Collection Operations Type Safety
When using the IN operator, maintain consistent types within the collection:
js
// Good - consistent types
status in ("active", "pending", "completed")
priority in (1, 2, 3)
// Avoid - mixed types unless specifically needed
value in ("active", 42, true) // Could lead to unexpected behavior
4. Function Usage
Function Naming and Organization
Follow consistent naming conventions for functions:
js
// Good function names
@isAdmin(user)
@hasPermission(user, "write")
@validateInput(data)
// Avoid
@check(user) // Too vague
@doStuff(data) // Unclear purpose
Function Return Type Handling
Handle function return types appropriately:
js
// Boolean functions - direct usage
@isAdmin(user)
// Numeric functions - use with comparisons
@count(items) > 0
// String functions - use with string operations
@toLowerCase(name) == "admin"
Implementation Details: See expr_function.go
and parser_expr_function.go
for function handling implementation.
5. Error Handling and Validation
Validate Input Data
Ensure input data matches expected types and formats:
js
// Include validation checks
@validateEmail(email) and @validateAge(age)
// Handle potential null values
@hasValue(user.profile) and user.profile.age > 18
Error Cases to Handle
Consider common error scenarios:
- Missing fields
- Null values
- Type mismatches
- Invalid function arguments
- Malformed expressions
js
// Good practice - defensive checking
@exists(user.profile) and (
@validateAge(user.profile.age) and
user.profile.age > 18
)
6. Performance Considerations
Optimize Expression Structure
Structure expressions to minimize evaluation overhead:
js
// Less efficient - evaluates everything
@expensiveCheck(data) and (x == 1 or x == 2)
// More efficient - checks simple conditions first
(x == 1 or x == 2) and @expensiveCheck(data)
Use Appropriate Operators
Choose operators that match your needs while maintaining performance:
js
// Efficient - direct comparison
status == "active"
// Less efficient - regex match for simple comparison
status match "^active$"
7. Testing and Validation
Test Edge Cases
Always test expressions with edge cases:
- Empty strings
- Null values
- Boundary numbers
- Empty collections
- Special characters in strings
// Edge case examples
status in () // Empty collection
name == "" // Empty string
age >= 0 // Boundary value
Validate Complex Expressions
Break down and test complex expressions in parts:
js
// Complex expression
(status in ("active", "pending") and priority > 3) or
(@isAdmin(user) and @hasPermission(user, "override"))
// Test individual parts
1. status in ("active", "pending")
2. priority > 3
3. @isAdmin(user)
4. @hasPermission(user, "override")