Rhai Cheatsheet¶
Quick reference for Rhai scripting in Kelora. For detailed tutorials, see Scripting Transforms. For practical examples: kelora --help-examples. For function reference: kelora --help-functions.
Variables & Types¶
let x = 42; // Integer (i64)
let price = 19.99; // Float (f64)
let name = "alice"; // String (double quotes only!)
let active = true; // Boolean (true/false)
let tags = [1, "two", 3.0]; // Array (mixed types ok)
let user = #{name: "bob", age: 30}; // Map/object literal
let empty = (); // Unit type (Rhai's "nothing")
type_of(x) // Returns: "i64", "string", "array", "map", "()"
x = "hello"; // Dynamic typing: can change type
Key Points:
letrequired for new variables (no implicit declaration)- Double quotes only for strings (
"text"not'text') - Unit type
()represents "nothing" (not null/undefined) - Arrays and maps are reference types (modifying copies affects original)
Operators¶
// Arithmetic
a + b a - b a * b a / b a % b a ** b // power: 2**3 == 8
// Comparison
a == b a != b a < b a > b a <= b a >= b
// Logical
a && b a || b !a
// Bitwise
a & b a | b a ^ b a << b a >> b
// Assignment
a = b a += b a -= b a *= b a /= b a %= b
a &= b a |= b a ^= b a <<= b a >>= b
// Ranges (for loops only)
1..5 // Exclusive: 1, 2, 3, 4
1..=5 // Inclusive: 1, 2, 3, 4, 5
// Membership
"key" in map // Check if key exists
Control Flow¶
If-Else¶
if x > 10 {
print("big");
} else if x > 5 {
print("medium");
} else {
print("small");
}
// Ternary-style (if is an expression)
let category = if x > 10 { "big" } else { "small" };
Switch¶
let category = switch x {
1 => "one",
2 | 3 => "two or three", // Multiple cases
4..=6 => "four to six", // Range matching
_ => "other" // Default (underscore)
};
Loops¶
// Range loops
for i in 0..10 { print(i); } // 0 to 9
for i in 0..=10 { print(i); } // 0 to 10
// Array iteration
for item in array { print(item); }
// Map iteration
for (key, value) in map {
print(`${key} = ${value}`);
}
// While loop
while condition {
if done { break; }
if skip { continue; }
}
// Infinite loop
loop {
if should_stop { break; }
}
Functions & Closures¶
// Function definition
fn add(a, b) {
a + b // Last expr is return value
}
fn greet(name) {
return "Hello, " + name; // Explicit return
}
// Closures
let double = |x| x * 2;
let add = |a, b| a + b;
// Closures in array methods
[1, 2, 3].map(|x| x * 2) // [2, 4, 6]
[1, 2, 3].filter(|x| x > 1) // [2, 3]
Rhai Special Feature: Function-as-Method¶
Rhai allows calling any function as a method on its first argument:
// These are equivalent:
extract_re(e.line, r"\d+") // Function call style
e.line.extract_re(r"\d+") // Method call style
// Use method style for chaining:
e.domain = e.url
.extract_domain()
.to_lower()
.strip();
// Both styles work for all functions:
to_int(e.port) // Function style
e.port.to_int() // Method style (more readable)
Kelora Event Access¶
The global variable e represents the current event in --filter and --exec stages:
// Direct field access
e.level // Top-level field
e.user.name // Nested field (maps)
e.scores[1] // Array indexing (0-based)
e.scores[-1] // Negative indexing (last element)
e.headers["user-agent"] // Bracket notation for special chars
// Field existence checking
"field" in e // Check top-level field exists
e.has_path("user.role") // Check nested path exists
// Safe field access with defaults
e.get_path("user.role", "guest") // Get nested with fallback
e.get_path("scores[0]", 0) // Works with array paths
// Field removal
e.password = () // Remove field (unit assignment)
e.ssn = () // Remove another field
e = () // Remove entire event (filtered out)
Array & Map Operations¶
JSON arrays become native Rhai arrays with full functionality:
// Array transformations
sorted(e.scores) // Sort numerically/lexicographically
reversed(e.items) // Reverse order
unique(e.tags) // Remove duplicates
dedup(e.values) // Remove consecutive duplicates
sorted_by(e.users, "age") // Sort objects by field
// Array methods
e.tags.len() // Length
e.tags.is_empty() // Check if empty
e.tags.join(", ") // Join to string
e.scores.sum() // Sum numbers
e.scores.min() // Minimum value
e.scores.max() // Maximum value
// Array access patterns
if e.items.len() > 0 {
e.first = e.items[0];
e.last = e.items[-1];
}
// Fan-out: convert array elements to separate events
emit_each(e.items) // Each element becomes an event
emit_each(e.items, #{ctx: "value"}) // Add base fields to each
// Map operations
for (key, val) in e {
print(`${key} = ${val}`);
}
Type Conversions¶
// Strict conversions (return () on error)
to_int(e.port) // String → integer
to_float(e.price) // String → float
to_bool(e.active) // String → boolean
// Safe conversions with defaults
to_int_or(e.port, 8080) // Use default if conversion fails
to_float_or(e.price, 0.0)
to_bool_or(e.active, false)
// String conversions
to_string(42) // Any → string
e.value.to_int() // Method style
// Type checking
type_of(e.field) // Get type as string
type_of(e.field) != "()" // Check if field has value
Common Patterns¶
Safe Nested Access¶
// With default fallback
let role = e.get_path("user.role", "guest");
let port = to_int_or(e.port, 8080);
// With existence check
if e.has_path("user.profile.avatar") {
e.avatar = e.user.profile.avatar;
}
// Safe array access
if e.items.len() > 0 {
e.first_item = e.items[0];
}
Conditional Field Removal¶
// Remove debug fields in production
if e.level != "DEBUG" {
e.stack_trace = ();
e.debug_info = ();
}
// Remove entire event conditionally
if e.status < 400 { e = (); } // Only keep errors
Method Chaining¶
// Extract and normalize domain
e.domain = e.url
.extract_domain()
.to_lower()
.strip();
// Parse and extract from structured text
e.error_line = e.stack_trace
.extract_re(r"line (\d+)", 1)
.to_int_or(0);
Array Processing¶
// Get top N scores
e.top_3 = sorted(e.scores)[-3:];
// Extract names from sorted users
e.winners = sorted_by(e.users, "score")
.reverse()
.map(|u| u.name);
// Filter and count
e.active_items = e.items.filter(|i| i.status == "active");
e.active_count = e.active_items.len();
Multi-Level Fan-Out¶
# First exec: batches → separate events
--exec 'emit_each(e.batches)'
# Second exec: items → separate events with context
--exec 'let ctx = #{batch_id: e.id}; emit_each(e.items, ctx)'
# Filter the final events
--filter 'e.status == "active"'
Global Context¶
conf // Global config map (read-only after --begin)
metrics // Global metrics map (from track_* calls)
meta // Event metadata (filename, line numbers, raw line)
get_env("VAR", "default") // Environment variable access
// meta attributes:
meta.line // Original raw line (always available)
meta.line_num // Line number, 1-based (available with files)
meta.filename // Source filename (multi-file processing)
// Example usage:
--begin 'conf.env = get_env("ENVIRONMENT", "dev")'
--filter 'conf.env == "prod" || e.level == "ERROR"'
// Multi-file tracking
--exec 'if e.level == "ERROR" { track_count(meta.filename) }'
// Debugging with line numbers
--exec 'eprint("Error at " + meta.filename + ":" + meta.line_num)'
Error Handling Modes¶
Default (resilient):
- Parse errors → skip line, continue
- Filter errors → treat as false, drop event
- Exec errors → rollback, keep original event
Strict mode (--strict):
- Any error → abort immediately with exit code 1
Rhai Quirks & Gotchas¶
| Coming from | Watch out for |
|---|---|
| JavaScript | No null/undefined (use ()), no single quotes, let required |
| Python | Braces required, no : after if/for, != not <>, double quotes only |
| Rust | More permissive syntax, semicolons mostly optional, dynamic typing |
Common mistakes:
// ❌ Wrong
x = 1 // Error: x not declared
let name = 'alice'; // Error: single quotes not allowed
if x > 5: print("big") // Error: colon not allowed, braces required
"5" + 3 // Error: no implicit conversion
// ✅ Correct
let x = 1; // Declare with let
let name = "alice"; // Double quotes
if x > 5 { print("big"); } // Braces required
"5".to_int() + 3 // Explicit conversion
Special behaviors:
- Last expression in block is return value (no
returnneeded) - Semicolons recommended but often optional
- Function calls without args:
e.lensame ase.len() - No implicit type conversion (use
to_int(),to_float(), etc.)
Quick Reference¶
// Event manipulation
e.field = value // Set field
e.field = () // Remove field
e = () // Remove event
// Type checking
type_of(e.field) // Get type
"field" in e // Field exists
// Safe access
e.get_path("a.b.c", default) // Nested with fallback
e.has_path("a.b.c") // Check nested exists
// Conversions
to_int_or(val, 0) // Safe int conversion
to_float_or(val, 0.0) // Safe float conversion
to_bool_or(val, false) // Safe bool conversion
// Arrays
e.items.len() // Length
e.items.is_empty() // Check empty
sorted(e.items) // Sort
unique(e.items) // Deduplicate
emit_each(e.items) // Fan out to events
// Strings
e.text.to_lower() // Lowercase
e.text.to_upper() // Uppercase
e.text.strip() // Trim whitespace
e.text.contains("word") // Substring check
e.text.extract_re(r"(\d+)", 1) // Regex extraction
// Environment & Context
get_env("VAR", "default") // Get env var
conf.key // Read config (from --begin)
metrics.key // Read metrics (in --end)
meta.filename // Current source filename
meta.line_num // Current line number (1-based)
meta.line // Original raw line
See Also¶
- Scripting Transforms Tutorial - Detailed walkthrough with examples
kelora --help-functions- Complete function cataloguekelora --help-examples- Practical log analysis patternskelora --help-rhai- Language guide (this cheatsheet's source)- Rhai Documentation - Full Rhai language reference